// 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. #ifndef VIXL_AARCH64_SIMULATOR_AARCH64_H_ #define VIXL_AARCH64_SIMULATOR_AARCH64_H_ #include #include #include #include "../globals-vixl.h" #include "../utils-vixl.h" #include "cpu-features.h" #include "abi-aarch64.h" #include "cpu-features-auditor-aarch64.h" #include "disasm-aarch64.h" #include "instructions-aarch64.h" #include "simulator-constants-aarch64.h" #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 // These are only used for the ABI feature, and depend on checks performed for // it. #ifdef VIXL_HAS_ABI_SUPPORT #include #if __cplusplus >= 201402L // Required for `std::index_sequence` #include #endif #endif // The hosts that Simulator running on may not have these flags defined. #ifndef PROT_BTI #define PROT_BTI 0x10 #endif #ifndef PROT_MTE #define PROT_MTE 0x20 #endif namespace vixl { namespace aarch64 { class SimStack { public: SimStack() {} explicit SimStack(size_t size) : usable_size_(size) {} // Guard against accesses above the stack base. This could occur, for example, // if the first simulated function tries to read stack arguments that haven't // been properly initialised in the Simulator's stack. void SetBaseGuardSize(size_t size) { base_guard_size_ = size; } // Guard against stack overflows. The size should be large enough to detect // the largest stride made (by `MacroAssembler::Claim()` or equivalent) whilst // initialising stack objects. void SetLimitGuardSize(size_t size) { limit_guard_size_ = size; } // The minimum usable size of the stack. // Equal to "stack base" - "stack limit", in AAPCS64 terminology. void SetUsableSize(size_t size) { usable_size_ = size; } // Set the minimum alignment for the stack parameters. void AlignToBytesLog2(int align_log2) { align_log2_ = align_log2; } class Allocated { public: // Using AAPCS64 terminology, highest addresses at the top: // // data_.get() + alloc_size -> // | // | Base guard // GetBase() -> | | // | | // | | AAPCS64-legal // | Usable stack | values of 'sp'. // | | // | | // GetLimit() -> | // | Limit guard // data_.get() -> | // // The Simulator detects (and forbids) accesses to either guard region. char* GetBase() const { return base_; } char* GetLimit() const { return limit_; } template bool IsAccessInGuardRegion(const T* base, size_t size) const { VIXL_ASSERT(size > 0); // Inclusive bounds. const char* start = reinterpret_cast(base); const char* end = start + size - 1; const char* data_start = data_.get(); const char* data_end = data_start + alloc_size_ - 1; bool in_base_guard = (start <= data_end) && (end >= base_); bool in_limit_guard = (start <= limit_) && (end >= data_start); return in_base_guard || in_limit_guard; } private: std::unique_ptr data_; char* limit_; char* base_; size_t alloc_size_; friend class SimStack; }; // Allocate the stack, locking the parameters. Allocated Allocate() { size_t align_to = 1 << align_log2_; size_t l = AlignUp(limit_guard_size_, align_to); size_t u = AlignUp(usable_size_, align_to); size_t b = AlignUp(base_guard_size_, align_to); size_t size = l + u + b; Allocated a; size_t alloc_size = (align_to - 1) + size; a.data_ = std::make_unique(alloc_size); void* data = a.data_.get(); auto data_aligned = reinterpret_cast(std::align(align_to, size, data, alloc_size)); a.limit_ = data_aligned + l - 1; a.base_ = data_aligned + l + u; a.alloc_size_ = alloc_size; return a; } private: size_t base_guard_size_ = 256; size_t limit_guard_size_ = 4 * 1024; size_t usable_size_ = 8 * 1024; size_t align_log2_ = 4; static const size_t kDefaultBaseGuardSize = 256; static const size_t kDefaultLimitGuardSize = 4 * 1024; static const size_t kDefaultUsableSize = 8 * 1024; }; // Armv8.5 MTE helpers. inline int GetAllocationTagFromAddress(uint64_t address) { return static_cast(ExtractUnsignedBitfield64(59, 56, address)); } template 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); } class MetaDataDepot { public: class MetaDataMTE { public: explicit MetaDataMTE(int tag) : tag_(tag) {} int GetTag() const { return tag_; } void SetTag(int tag) { VIXL_ASSERT(IsUint4(tag)); tag_ = tag; } static bool IsActive() { return is_active; } static void SetActive(bool value) { is_active = value; } private: static bool is_active; int16_t tag_; friend class MetaDataDepot; }; // Generate a key for metadata recording from a untagged address. template uint64_t GenerateMTEkey(T address) const { // Cast the address using a C-style cast. A reinterpret_cast would be // appropriate, but it can't cast one integral type to another. return (uint64_t)(AddressUntag(address)) >> kMTETagGranuleInBytesLog2; } template R GetAttribute(T map, uint64_t key) { auto pair = map->find(key); R value = (pair == map->end()) ? nullptr : &pair->second; return value; } template int GetMTETag(T address, Instruction const* pc = nullptr) { uint64_t key = GenerateMTEkey(address); MetaDataMTE* m = GetAttribute(&metadata_mte_, key); if (!m) { std::stringstream sstream; sstream << std::hex << "MTE ERROR : instruction at 0x" << reinterpret_cast(pc) << " touched a unallocated memory location 0x" << (uint64_t)(address) << ".\n"; VIXL_ABORT_WITH_MSG(sstream.str().c_str()); } return m->GetTag(); } template void SetMTETag(T address, int tag, Instruction const* pc = nullptr) { VIXL_ASSERT(IsAligned((uintptr_t)address, kMTETagGranuleInBytes)); uint64_t key = GenerateMTEkey(address); MetaDataMTE* m = GetAttribute(&metadata_mte_, key); if (!m) { metadata_mte_.insert({key, MetaDataMTE(tag)}); } else { // Overwrite if (m->GetTag() == tag) { std::stringstream sstream; sstream << std::hex << "MTE WARNING : instruction at 0x" << reinterpret_cast(pc) << ", the same tag is assigned to the address 0x" << (uint64_t)(address) << ".\n"; VIXL_WARNING(sstream.str().c_str()); } m->SetTag(tag); } } template size_t CleanMTETag(T address) { VIXL_ASSERT( IsAligned(reinterpret_cast(address), kMTETagGranuleInBytes)); uint64_t key = GenerateMTEkey(address); return metadata_mte_.erase(key); } size_t GetTotalCountMTE() { return metadata_mte_.size(); } private: // Tag recording of each allocated memory in the tag-granule. std::unordered_map metadata_mte_; }; // Representation of memory, with typed getters and setters for access. class Memory { public: explicit Memory(SimStack::Allocated stack) : stack_(std::move(stack)) { metadata_depot_ = nullptr; } const SimStack::Allocated& GetStack() { return stack_; } template bool IsMTETagsMatched(A address, Instruction const* pc = nullptr) const { if (MetaDataDepot::MetaDataMTE::IsActive()) { // 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 addr = (uint64_t)address; int pointer_tag = GetAllocationTagFromAddress(addr); int memory_tag = metadata_depot_->GetMTETag(AddressUntag(addr), pc); return pointer_tag == memory_tag; } return true; } template T Read(A address, Instruction const* pc = nullptr) const { T value; VIXL_STATIC_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) || (sizeof(value) == 4) || (sizeof(value) == 8) || (sizeof(value) == 16)); auto base = reinterpret_cast(AddressUntag(address)); if (stack_.IsAccessInGuardRegion(base, sizeof(value))) { VIXL_ABORT_WITH_MSG("Attempt to read from stack guard region"); } if (!IsMTETagsMatched(address, pc)) { VIXL_ABORT_WITH_MSG("Tag mismatch."); } memcpy(&value, base, sizeof(value)); return value; } template void Write(A address, T value, Instruction const* pc = nullptr) const { VIXL_STATIC_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) || (sizeof(value) == 4) || (sizeof(value) == 8) || (sizeof(value) == 16)); auto base = reinterpret_cast(AddressUntag(address)); if (stack_.IsAccessInGuardRegion(base, sizeof(value))) { VIXL_ABORT_WITH_MSG("Attempt to write to stack guard region"); } if (!IsMTETagsMatched(address, pc)) { VIXL_ABORT_WITH_MSG("Tag mismatch."); } memcpy(base, &value, sizeof(value)); } template uint64_t ReadUint(int size_in_bytes, A address) const { switch (size_in_bytes) { case 1: return Read(address); case 2: return Read(address); case 4: return Read(address); case 8: return Read(address); } VIXL_UNREACHABLE(); return 0; } template int64_t ReadInt(int size_in_bytes, A address) const { switch (size_in_bytes) { case 1: return Read(address); case 2: return Read(address); case 4: return Read(address); case 8: return Read(address); } VIXL_UNREACHABLE(); return 0; } template void Write(int size_in_bytes, A address, uint64_t value) const { switch (size_in_bytes) { case 1: return Write(address, static_cast(value)); case 2: return Write(address, static_cast(value)); case 4: return Write(address, static_cast(value)); case 8: return Write(address, value); } VIXL_UNREACHABLE(); } void AppendMetaData(MetaDataDepot* metadata_depot) { VIXL_ASSERT(metadata_depot != nullptr); VIXL_ASSERT(metadata_depot_ == nullptr); metadata_depot_ = metadata_depot; } private: SimStack::Allocated stack_; MetaDataDepot* metadata_depot_; }; // Represent a register (r0-r31, v0-v31, z0-z31, p0-p15). template class SimRegisterBase { public: static const unsigned kMaxSizeInBytes = kMaxSizeInBits / kBitsPerByte; VIXL_STATIC_ASSERT((kMaxSizeInBytes * kBitsPerByte) == kMaxSizeInBits); SimRegisterBase() : size_in_bytes_(kMaxSizeInBytes) { Clear(); } unsigned GetSizeInBits() const { return size_in_bytes_ * kBitsPerByte; } unsigned GetSizeInBytes() const { return size_in_bytes_; } void SetSizeInBytes(unsigned size_in_bytes) { VIXL_ASSERT(size_in_bytes <= kMaxSizeInBytes); size_in_bytes_ = size_in_bytes; } void SetSizeInBits(unsigned size_in_bits) { VIXL_ASSERT(size_in_bits <= kMaxSizeInBits); VIXL_ASSERT((size_in_bits % kBitsPerByte) == 0); SetSizeInBytes(size_in_bits / kBitsPerByte); } // Write the specified value. The value is zero-extended if necessary. template void Write(T new_value) { // All AArch64 registers are zero-extending. if (sizeof(new_value) < GetSizeInBytes()) Clear(); WriteLane(new_value, 0); NotifyRegisterWrite(); } template VIXL_DEPRECATED("Write", void Set(T new_value)) { Write(new_value); } void Clear() { memset(value_, 0, kMaxSizeInBytes); 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 void Insert(int lane, T new_value) { WriteLane(new_value, lane); NotifyRegisterWrite(); } // Get the value as the specified type. The value is truncated if necessary. template T Get() const { return GetLane(0); } // Get the lane value as the specified type. The value is truncated if // necessary. template T GetLane(int lane) const { T result; ReadLane(&result, lane); return result; } template VIXL_DEPRECATED("GetLane", T Get(int lane) const) { return GetLane(lane); } // Get the value of a specific bit, indexed from the least-significant bit of // lane 0. bool GetBit(int bit) const { int bit_in_byte = bit % (sizeof(value_[0]) * kBitsPerByte); int byte = bit / (sizeof(value_[0]) * kBitsPerByte); return ((value_[byte] >> bit_in_byte) & 1) != 0; } // Return a pointer to the raw, underlying byte array. const uint8_t* GetBytes() const { return value_; } // 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_; } void NotifyRegisterLogged() { written_since_last_log_ = false; } protected: uint8_t value_[kMaxSizeInBytes]; unsigned size_in_bytes_; // Helpers to aid with register tracing. bool written_since_last_log_; void NotifyRegisterWrite() { written_since_last_log_ = true; } private: template void ReadLane(T* dst, int lane) const { VIXL_ASSERT(lane >= 0); VIXL_ASSERT((sizeof(*dst) + (lane * sizeof(*dst))) <= GetSizeInBytes()); memcpy(dst, &value_[lane * sizeof(*dst)], sizeof(*dst)); } template void WriteLane(T src, int lane) { VIXL_ASSERT(lane >= 0); VIXL_ASSERT((sizeof(src) + (lane * sizeof(src))) <= GetSizeInBytes()); memcpy(&value_[lane * sizeof(src)], &src, sizeof(src)); } // The default ReadLane and WriteLane methods assume what we are copying is // "trivially copyable" by using memcpy. We have to provide alternative // implementations for SimFloat16 which cannot be copied this way. void ReadLane(vixl::internal::SimFloat16* dst, int lane) const { uint16_t rawbits; ReadLane(&rawbits, lane); *dst = RawbitsToFloat16(rawbits); } void WriteLane(vixl::internal::SimFloat16 src, int lane) { WriteLane(Float16ToRawbits(src), lane); } }; typedef SimRegisterBase SimRegister; // r0-r31 typedef SimRegisterBase SimPRegister; // p0-p15 // FFR has the same format as a predicate register. typedef SimPRegister SimFFRRegister; // v0-v31 and z0-z31 class SimVRegister : public SimRegisterBase { public: SimVRegister() : SimRegisterBase(), accessed_as_z_(false) {} void NotifyAccessAsZ() { accessed_as_z_ = true; } void NotifyRegisterLogged() { SimRegisterBase::NotifyRegisterLogged(); accessed_as_z_ = false; } bool AccessedAsZSinceLastLog() const { return accessed_as_z_; } private: bool accessed_as_z_; }; // Representation of a SVE predicate register. class LogicPRegister { public: inline LogicPRegister( SimPRegister& other) // NOLINT(runtime/references)(runtime/explicit) : register_(other) {} // Set a conveniently-sized block to 16 bits as the minimum predicate length // is 16 bits and allow to be increased to multiples of 16 bits. typedef uint16_t ChunkType; // Assign a bit into the end positon of the specified lane. // The bit is zero-extended if necessary. void SetActive(VectorFormat vform, int lane_index, bool value) { int psize = LaneSizeInBytesFromFormat(vform); int bit_index = lane_index * psize; int byte_index = bit_index / kBitsPerByte; int bit_offset = bit_index % kBitsPerByte; uint8_t byte = register_.GetLane(byte_index); register_.Insert(byte_index, ZeroExtend(byte, bit_offset, psize, value)); } bool IsActive(VectorFormat vform, int lane_index) const { int psize = LaneSizeInBytesFromFormat(vform); int bit_index = lane_index * psize; int byte_index = bit_index / kBitsPerByte; int bit_offset = bit_index % kBitsPerByte; uint8_t byte = register_.GetLane(byte_index); return ExtractBit(byte, bit_offset); } // The accessors for bulk processing. int GetChunkCount() const { VIXL_ASSERT((register_.GetSizeInBytes() % sizeof(ChunkType)) == 0); return register_.GetSizeInBytes() / sizeof(ChunkType); } ChunkType GetChunk(int lane) const { return GetActiveMask(lane); } void SetChunk(int lane, ChunkType new_value) { SetActiveMask(lane, new_value); } void SetAllBits() { int chunk_size = sizeof(ChunkType) * kBitsPerByte; ChunkType bits = GetUintMask(chunk_size); for (int lane = 0; lane < (static_cast(register_.GetSizeInBits() / chunk_size)); lane++) { SetChunk(lane, bits); } } template T GetActiveMask(int lane) const { return register_.GetLane(lane); } template void SetActiveMask(int lane, T new_value) { register_.Insert(lane, new_value); } void Clear() { register_.Clear(); } bool Aliases(const LogicPRegister& other) const { return ®ister_ == &other.register_; } private: // The bit assignment is zero-extended to fill the size of predicate element. uint8_t ZeroExtend(uint8_t byte, int index, int psize, bool value) { VIXL_ASSERT(index >= 0); VIXL_ASSERT(index + psize <= kBitsPerByte); int bits = value ? 1 : 0; switch (psize) { case 1: AssignBit(byte, index, bits); break; case 2: AssignBits(byte, index, 0x03, bits); break; case 4: AssignBits(byte, index, 0x0f, bits); break; case 8: AssignBits(byte, index, 0xff, bits); break; default: VIXL_UNREACHABLE(); return 0; } return byte; } SimPRegister& register_; }; // 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(runtime/references)(runtime/explicit) : register_(other) { for (size_t i = 0; i < ArrayLength(saturated_); i++) { saturated_[i] = kNotSaturated; } for (size_t i = 0; i < ArrayLength(round_); i++) { round_[i] = 0; } } int64_t Int(VectorFormat vform, int index) const { if (IsSVEFormat(vform)) register_.NotifyAccessAsZ(); int64_t element; switch (LaneSizeInBitsFromFormat(vform)) { case 8: element = register_.GetLane(index); break; case 16: element = register_.GetLane(index); break; case 32: element = register_.GetLane(index); break; case 64: element = register_.GetLane(index); break; default: VIXL_UNREACHABLE(); return 0; } return element; } uint64_t Uint(VectorFormat vform, int index) const { if (IsSVEFormat(vform)) register_.NotifyAccessAsZ(); uint64_t element; switch (LaneSizeInBitsFromFormat(vform)) { case 8: element = register_.GetLane(index); break; case 16: element = register_.GetLane(index); break; case 32: element = register_.GetLane(index); break; case 64: element = register_.GetLane(index); break; default: VIXL_UNREACHABLE(); return 0; } return element; } int UintArray(VectorFormat vform, uint64_t* dst) const { for (int i = 0; i < LaneCountFromFormat(vform); i++) { dst[i] = Uint(vform, i); } return LaneCountFromFormat(vform); } uint64_t UintLeftJustified(VectorFormat vform, int index) const { return Uint(vform, index) << (64 - LaneSizeInBitsFromFormat(vform)); } int64_t IntLeftJustified(VectorFormat vform, int index) const { uint64_t value = UintLeftJustified(vform, index); int64_t result; memcpy(&result, &value, sizeof(result)); return result; } void SetInt(VectorFormat vform, int index, int64_t value) const { if (IsSVEFormat(vform)) register_.NotifyAccessAsZ(); switch (LaneSizeInBitsFromFormat(vform)) { case 8: register_.Insert(index, static_cast(value)); break; case 16: register_.Insert(index, static_cast(value)); break; case 32: register_.Insert(index, static_cast(value)); break; case 64: register_.Insert(index, static_cast(value)); break; default: VIXL_UNREACHABLE(); return; } } void SetIntArray(VectorFormat vform, const int64_t* src) const { ClearForWrite(vform); for (int i = 0; i < LaneCountFromFormat(vform); i++) { SetInt(vform, i, src[i]); } } void SetUint(VectorFormat vform, int index, uint64_t value) const { if (IsSVEFormat(vform)) register_.NotifyAccessAsZ(); switch (LaneSizeInBitsFromFormat(vform)) { case 8: register_.Insert(index, static_cast(value)); break; case 16: register_.Insert(index, static_cast(value)); break; case 32: register_.Insert(index, static_cast(value)); break; case 64: register_.Insert(index, static_cast(value)); break; default: VIXL_UNREACHABLE(); return; } } void SetUintArray(VectorFormat vform, const uint64_t* src) const { ClearForWrite(vform); for (int i = 0; i < LaneCountFromFormat(vform); i++) { SetUint(vform, i, src[i]); } } template T Float(int index) const { return register_.GetLane(index); } template void SetFloat(int index, T value) const { register_.Insert(index, value); } template void SetFloat(VectorFormat vform, int index, T value) const { if (IsSVEFormat(vform)) register_.NotifyAccessAsZ(); register_.Insert(index, value); } void Clear() { register_.Clear(); } // When setting a result in a register larger than the result itself, the top // bits of the register must be cleared. void ClearForWrite(VectorFormat vform) const { // SVE destinations write whole registers, so we have nothing to clear. if (IsSVEFormat(vform)) return; unsigned size = RegisterSizeInBytesFromFormat(vform); for (unsigned i = size; i < register_.GetSizeInBytes(); 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 }; // Getters for saturation state. Saturation GetSignedSaturation(int index) { return static_cast(saturated_[index] & kSignedSatMask); } Saturation GetUnsignedSaturation(int index) { return static_cast(saturated_[index] & kUnsignedSatMask); } // Setters for saturation state. void ClearSat(int index) { saturated_[index] = kNotSaturated; } void SetSignedSat(int index, bool positive) { SetSatFlag(index, positive ? kSignedSatPositive : kSignedSatNegative); } void SetUnsignedSat(int index, bool positive) { SetSatFlag(index, positive ? kUnsignedSatPositive : kUnsignedSatNegative); } void SetSatFlag(int index, Saturation sat) { saturated_[index] = static_cast(saturated_[index] | sat); VIXL_ASSERT((sat & kUnsignedSatMask) != kUnsignedSatUndefined); VIXL_ASSERT((sat & kSignedSatMask) != kSignedSatUndefined); } // 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)); } else if (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)); } else if (sat == kUnsignedSatNegative) { SetUint(vform, i, 0); } } return *this; } // Getter for rounding state. bool GetRounding(int index) { return round_[index]; } // Setter for rounding state. void SetRounding(int index, bool round) { round_[index] = round; } // Round lanes of a vector based on rounding state. LogicVRegister& Round(VectorFormat vform) { for (int i = 0; i < LaneCountFromFormat(vform); i++) { SetUint(vform, i, Uint(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 = ExtractSignedBitfield64(63, 1, val); // >>= 1 if (GetSignedSaturation(i) == kNotSaturated) { SetInt(vform, i, val); } else { // If the operation causes signed saturation, the sign bit must be // inverted. uint64_t uval = static_cast(val); SetUint(vform, i, uval ^ ((MaxUintFromFormat(vform) >> 1) + 1)); } } return *this; } int LaneCountFromFormat(VectorFormat vform) const { if (IsSVEFormat(vform)) { return register_.GetSizeInBits() / LaneSizeInBitsFromFormat(vform); } else { return vixl::aarch64::LaneCountFromFormat(vform); } } 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_[kZRegMaxSizeInBytes]; // Allocate one rounding state entry per lane. bool round_[kZRegMaxSizeInBytes]; }; // Represent an SVE addressing mode and abstract per-lane address generation to // make iteration easy. // // Contiguous accesses are described with a simple base address, the memory // occupied by each lane (`SetMsizeInBytesLog2()`) and the number of elements in // each struct (`SetRegCount()`). // // Scatter-gather accesses also require a SimVRegister and information about how // to extract lanes from it. class LogicSVEAddressVector { public: // scalar-plus-scalar // scalar-plus-immediate explicit LogicSVEAddressVector(uint64_t base) : base_(base), msize_in_bytes_log2_(kUnknownMsizeInBytesLog2), reg_count_(1), vector_(NULL), vector_form_(kFormatUndefined), vector_mod_(NO_SVE_OFFSET_MODIFIER), vector_shift_(0) {} // scalar-plus-vector // vector-plus-immediate // `base` should be the constant used for each element. That is, the value // of `xn`, or `#`. // `vector` should be the SimVRegister with offsets for each element. The // vector format must be specified; SVE scatter/gather accesses typically // support both 32-bit and 64-bit addressing. // // `mod` and `shift` correspond to the modifiers applied to each element in // scalar-plus-vector forms, such as those used for unpacking and // sign-extension. They are not used for vector-plus-immediate. LogicSVEAddressVector(uint64_t base, const SimVRegister* vector, VectorFormat vform, SVEOffsetModifier mod = NO_SVE_OFFSET_MODIFIER, int shift = 0) : base_(base), msize_in_bytes_log2_(kUnknownMsizeInBytesLog2), reg_count_(1), vector_(vector), vector_form_(vform), vector_mod_(mod), vector_shift_(shift) {} // Set `msize` -- the memory occupied by each lane -- for address // calculations. void SetMsizeInBytesLog2(int msize_in_bytes_log2) { VIXL_ASSERT(msize_in_bytes_log2 >= static_cast(kBRegSizeInBytesLog2)); VIXL_ASSERT(msize_in_bytes_log2 <= static_cast(kDRegSizeInBytesLog2)); msize_in_bytes_log2_ = msize_in_bytes_log2; } bool HasMsize() const { return msize_in_bytes_log2_ != kUnknownMsizeInBytesLog2; } int GetMsizeInBytesLog2() const { VIXL_ASSERT(HasMsize()); return msize_in_bytes_log2_; } int GetMsizeInBitsLog2() const { return GetMsizeInBytesLog2() + kBitsPerByteLog2; } int GetMsizeInBytes() const { return 1 << GetMsizeInBytesLog2(); } int GetMsizeInBits() const { return 1 << GetMsizeInBitsLog2(); } void SetRegCount(int reg_count) { VIXL_ASSERT(reg_count >= 1); // E.g. ld1/st1 VIXL_ASSERT(reg_count <= 4); // E.g. ld4/st4 reg_count_ = reg_count; } int GetRegCount() const { return reg_count_; } // Full per-element address calculation for structured accesses. // // Note that the register number argument (`reg`) is zero-based. uint64_t GetElementAddress(int lane, int reg) const { VIXL_ASSERT(reg < GetRegCount()); // Individual structures are always contiguous in memory, so this // implementation works for both contiguous and scatter-gather addressing. return GetStructAddress(lane) + (reg * GetMsizeInBytes()); } // Full per-struct address calculation for structured accesses. uint64_t GetStructAddress(int lane) const; bool IsContiguous() const { return vector_ == NULL; } bool IsScatterGather() const { return !IsContiguous(); } private: uint64_t base_; int msize_in_bytes_log2_; int reg_count_; const SimVRegister* vector_; VectorFormat vector_form_; SVEOffsetModifier vector_mod_; int vector_shift_; static const int kUnknownMsizeInBytesLog2 = -1; }; // 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) {} uint32_t GetRawValue() const { return value_; } VIXL_DEPRECATED("GetRawValue", uint32_t RawValue() const) { return GetRawValue(); } void SetRawValue(uint32_t new_value) { value_ = (value_ & write_ignore_mask_) | (new_value & ~write_ignore_mask_); } uint32_t ExtractBits(int msb, int lsb) const { return ExtractUnsignedBitfield32(msb, lsb, value_); } VIXL_DEPRECATED("ExtractBits", uint32_t Bits(int msb, int lsb) const) { return ExtractBits(msb, lsb); } int32_t ExtractSignedBits(int msb, int lsb) const { return ExtractSignedBitfield32(msb, lsb, value_); } VIXL_DEPRECATED("ExtractSignedBits", int32_t SignedBits(int msb, int lsb) const) { return ExtractSignedBits(msb, lsb); } void SetBits(int msb, int lsb, uint32_t bits); // Default system register values. static SimSystemRegister DefaultValueFor(SystemRegister id); #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ uint32_t Get##Name() const { return this->Func(HighBit, LowBit); } \ VIXL_DEPRECATED("Get" #Name, uint32_t Name() const) { return Get##Name(); } \ void Set##Name(uint32_t bits) { SetBits(HighBit, LowBit, bits); } #define DEFINE_WRITE_IGNORE_MASK(Name, Mask) \ static const uint32_t Name##WriteIgnoreMask = ~static_cast(Mask); SYSTEM_REGISTER_FIELDS_LIST(DEFINE_GETTER, DEFINE_WRITE_IGNORE_MASK) #undef DEFINE_ZERO_BITS #undef DEFINE_GETTER 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) {} uint32_t value_; uint32_t write_ignore_mask_; }; class SimExclusiveLocalMonitor { public: SimExclusiveLocalMonitor() : kSkipClearProbability(8), seed_(0x87654321) { Clear(); } // Clear the exclusive monitor (like clrex). void Clear() { address_ = 0; size_ = 0; } // 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_); } private: uint64_t address_; size_t size_; const int kSkipClearProbability; uint32_t seed_; }; // 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) {} bool IsExclusive(uint64_t address, size_t size) { USE(address, size); bool pass = (seed_ % kPassProbability) != 0; // Advance seed_ using a simple linear congruential generator. seed_ = (seed_ * 48271) % 2147483647; return pass; } private: const int kPassProbability; uint32_t seed_; }; class Simulator : public DecoderVisitor { public: explicit Simulator(Decoder* decoder, FILE* stream = stdout, SimStack::Allocated stack = SimStack().Allocate()); ~Simulator(); void ResetState(); // Run the simulator. virtual void Run(); void RunFrom(const Instruction* first); #if defined(VIXL_HAS_ABI_SUPPORT) && __cplusplus >= 201103L && \ (defined(__clang__) || GCC_VERSION_OR_NEWER(4, 9, 1)) // Templated `RunFrom` version taking care of passing arguments and returning // the result value. // This allows code like: // int32_t res = simulator.RunFrom(GenerateCode(), // 0x123); // It requires VIXL's ABI features, and C++11 or greater. // Also, the initialisation of tuples is incorrect in GCC before 4.9.1: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51253 template R RunFrom(const Instruction* code, P... arguments) { return RunFromStructHelper::Wrapper(this, code, arguments...); } template struct RunFromStructHelper { static R Wrapper(Simulator* simulator, const Instruction* code, P... arguments) { ABI abi; std::tuple unused_tuple{ // TODO: We currently do not support arguments passed on the stack. We // could do so by using `WriteGenericOperand()` here, but may need to // add features to handle situations where the stack is or is not set // up. (simulator->WriteCPURegister(abi.GetNextParameterGenericOperand

() .GetCPURegister(), arguments), arguments)...}; simulator->RunFrom(code); return simulator->ReadGenericOperand(abi.GetReturnGenericOperand()); } }; // Partial specialization when the return type is `void`. template struct RunFromStructHelper { static void Wrapper(Simulator* simulator, const Instruction* code, P... arguments) { ABI abi; std::tuple unused_tuple{ // TODO: We currently do not support arguments passed on the stack. We // could do so by using `WriteGenericOperand()` here, but may need to // add features to handle situations where the stack is or is not set // up. (simulator->WriteCPURegister(abi.GetNextParameterGenericOperand

() .GetCPURegister(), arguments), arguments)...}; simulator->RunFrom(code); } }; #endif // Execution ends when the PC hits this address. static const Instruction* kEndOfSimAddress; // Simulation helpers. const Instruction* ReadPc() const { return pc_; } VIXL_DEPRECATED("ReadPc", const Instruction* pc() const) { return ReadPc(); } enum BranchLogMode { LogBranches, NoBranchLog }; void WritePc(const Instruction* new_pc, BranchLogMode log_mode = LogBranches) { if (log_mode == LogBranches) LogTakenBranch(new_pc); pc_ = AddressUntag(new_pc); pc_modified_ = true; } VIXL_DEPRECATED("WritePc", void set_pc(const Instruction* new_pc)) { return WritePc(new_pc); } void IncrementPc() { if (!pc_modified_) { pc_ = pc_->GetNextInstruction(); } } VIXL_DEPRECATED("IncrementPc", void increment_pc()) { IncrementPc(); } BType ReadBType() const { return btype_; } void WriteNextBType(BType btype) { next_btype_ = btype; } void UpdateBType() { btype_ = next_btype_; next_btype_ = DefaultBType; } // Helper function to determine BType for branches. BType GetBTypeFromInstruction(const Instruction* instr) const; bool PcIsInGuardedPage() const { return guard_pages_; } void SetGuardedPages(bool guard_pages) { guard_pages_ = guard_pages; } const Instruction* GetLastExecutedInstruction() const { return last_instr_; } void ExecuteInstruction() { // The program counter should always be aligned. VIXL_ASSERT(IsWordAligned(pc_)); pc_modified_ = false; // On guarded pages, if BType is not zero, take an exception on any // instruction other than BTI, PACI[AB]SP, HLT or BRK. if (PcIsInGuardedPage() && (ReadBType() != DefaultBType)) { if (pc_->IsPAuth()) { Instr i = pc_->Mask(SystemPAuthMask); if ((i != PACIASP) && (i != PACIBSP)) { VIXL_ABORT_WITH_MSG( "Executing non-BTI instruction with wrong BType."); } } else if (!pc_->IsBti() && !pc_->IsException()) { VIXL_ABORT_WITH_MSG("Executing non-BTI instruction with wrong BType."); } } bool last_instr_was_movprfx = (form_hash_ == "movprfx_z_z"_h) || (form_hash_ == "movprfx_z_p_z"_h); // decoder_->Decode(...) triggers at least the following visitors: // 1. The CPUFeaturesAuditor (`cpu_features_auditor_`). // 2. The PrintDisassembler (`print_disasm_`), if enabled. // 3. The Simulator (`this`). // User can add additional visitors at any point, but the Simulator requires // that the ordering above is preserved. decoder_->Decode(pc_); if (last_instr_was_movprfx) { VIXL_ASSERT(last_instr_ != NULL); VIXL_CHECK(pc_->CanTakeSVEMovprfx(form_hash_, last_instr_)); } last_instr_ = ReadPc(); IncrementPc(); LogAllWrittenRegisters(); UpdateBType(); VIXL_CHECK(cpu_features_auditor_.InstructionIsAvailable()); } virtual void Visit(Metadata* metadata, const Instruction* instr) VIXL_OVERRIDE; #define DECLARE(A) virtual void Visit##A(const Instruction* instr); VISITOR_LIST_THAT_RETURN(DECLARE) #undef DECLARE #define DECLARE(A) \ VIXL_NO_RETURN virtual void Visit##A(const Instruction* instr); VISITOR_LIST_THAT_DONT_RETURN(DECLARE) #undef DECLARE void Simulate_PdT_PgZ_ZnT_ZmT(const Instruction* instr); void Simulate_PdT_Xn_Xm(const Instruction* instr); void Simulate_ZdB_Zn1B_Zn2B_imm(const Instruction* instr); void Simulate_ZdB_ZnB_ZmB(const Instruction* instr); void Simulate_ZdD_ZnD_ZmD_imm(const Instruction* instr); void Simulate_ZdH_PgM_ZnS(const Instruction* instr); void Simulate_ZdH_ZnH_ZmH_imm(const Instruction* instr); void Simulate_ZdS_PgM_ZnD(const Instruction* instr); void Simulate_ZdS_PgM_ZnS(const Instruction* instr); void Simulate_ZdS_ZnS_ZmS_imm(const Instruction* instr); void Simulate_ZdT_PgM_ZnT(const Instruction* instr); void Simulate_ZdT_PgZ_ZnT_ZmT(const Instruction* instr); void Simulate_ZdT_ZnT_ZmT(const Instruction* instr); void Simulate_ZdT_ZnT_ZmTb(const Instruction* instr); void Simulate_ZdT_ZnT_const(const Instruction* instr); void Simulate_ZdaD_ZnS_ZmS_imm(const Instruction* instr); void Simulate_ZdaH_ZnH_ZmH_imm_const(const Instruction* instr); void Simulate_ZdaS_ZnH_ZmH(const Instruction* instr); void Simulate_ZdaS_ZnH_ZmH_imm(const Instruction* instr); void Simulate_ZdaS_ZnS_ZmS_imm_const(const Instruction* instr); void Simulate_ZdaT_PgM_ZnTb(const Instruction* instr); void Simulate_ZdaT_ZnT_ZmT(const Instruction* instr); void Simulate_ZdaT_ZnT_const(const Instruction* instr); void Simulate_ZdaT_ZnTb_ZmTb(const Instruction* instr); void Simulate_ZdnT_PgM_ZdnT_ZmT(const Instruction* instr); void Simulate_ZdnT_PgM_ZdnT_const(const Instruction* instr); void Simulate_ZdnT_ZdnT_ZmT_const(const Instruction* instr); void Simulate_ZtD_PgZ_ZnD_Xm(const Instruction* instr); void Simulate_ZtD_Pg_ZnD_Xm(const Instruction* instr); void Simulate_ZtS_PgZ_ZnS_Xm(const Instruction* instr); void Simulate_ZtS_Pg_ZnS_Xm(const Instruction* instr); void SimulateSVEHalvingAddSub(const Instruction* instr); void SimulateSVESaturatingArithmetic(const Instruction* instr); void SimulateSVEIntArithPair(const Instruction* instr); void SimulateSVENarrow(const Instruction* instr); void SimulateSVEInterleavedArithLong(const Instruction* instr); void SimulateSVEShiftLeftImm(const Instruction* instr); void SimulateSVEAddSubCarry(const Instruction* instr); void SimulateSVEAddSubHigh(const Instruction* instr); void SimulateSVEIntMulLongVec(const Instruction* instr); void SimulateSVESaturatingIntMulLongIdx(const Instruction* instr); void SimulateSVEExclusiveOrRotate(const Instruction* instr); void SimulateSVEBitwiseTernary(const Instruction* instr); void SimulateSVEComplexDotProduct(const Instruction* instr); void SimulateSVEMulIndex(const Instruction* instr); void SimulateSVEMlaMlsIndex(const Instruction* instr); void SimulateSVEComplexIntMulAdd(const Instruction* instr); void SimulateSVESaturatingMulAddHigh(const Instruction* instr); void SimulateSVESaturatingMulHighIndex(const Instruction* instr); void SimulateSVEFPConvertLong(const Instruction* instr); void SimulateMatrixMul(const Instruction* instr); void SimulateSVEFPMatrixMul(const Instruction* instr); void SimulateNEONMulByElementLong(const Instruction* instr); void SimulateNEONFPMulByElement(const Instruction* instr); void SimulateNEONFPMulByElementLong(const Instruction* instr); void SimulateNEONComplexMulByElement(const Instruction* instr); void SimulateNEONDotProdByElement(const Instruction* instr); void SimulateMTEAddSubTag(const Instruction* instr); void SimulateMTETagMaskInsert(const Instruction* instr); void SimulateMTESubPointer(const Instruction* instr); void SimulateMTELoadTag(const Instruction* instr); void SimulateMTEStoreTag(const Instruction* instr); void SimulateMTEStoreTagPair(const Instruction* instr); void Simulate_XdSP_XnSP_Xm(const Instruction* instr); void SimulateCpy(const Instruction* instr); void SimulateCpyFP(const Instruction* instr); void SimulateCpyP(const Instruction* instr); void SimulateCpyM(const Instruction* instr); void SimulateCpyE(const Instruction* instr); void SimulateSetP(const Instruction* instr); void SimulateSetM(const Instruction* instr); void SimulateSetE(const Instruction* instr); void SimulateSetGP(const Instruction* instr); void SimulateSetGM(const Instruction* instr); void SimulateSignedMinMax(const Instruction* instr); void SimulateUnsignedMinMax(const Instruction* instr); // Integer register accessors. // Basic accessor: Read the register as the specified type. template T ReadRegister(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const { VIXL_ASSERT( code < kNumberOfRegisters || ((r31mode == Reg31IsZeroRegister) && (code == kSPRegInternalCode))); if ((code == 31) && (r31mode == Reg31IsZeroRegister)) { T result; memset(&result, 0, sizeof(result)); return result; } if ((r31mode == Reg31IsZeroRegister) && (code == kSPRegInternalCode)) { code = 31; } return registers_[code].Get(); } template VIXL_DEPRECATED("ReadRegister", T reg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const) { return ReadRegister(code, r31mode); } // Common specialized accessors for the ReadRegister() template. int32_t ReadWRegister(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const { return ReadRegister(code, r31mode); } VIXL_DEPRECATED("ReadWRegister", int32_t wreg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const) { return ReadWRegister(code, r31mode); } int64_t ReadXRegister(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const { return ReadRegister(code, r31mode); } VIXL_DEPRECATED("ReadXRegister", int64_t xreg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const) { return ReadXRegister(code, r31mode); } SimPRegister& ReadPRegister(unsigned code) { VIXL_ASSERT(code < kNumberOfPRegisters); return pregisters_[code]; } SimFFRRegister& ReadFFR() { return ffr_register_; } // As above, with parameterized size and return type. The value is // either zero-extended or truncated to fit, as required. template T ReadRegister(unsigned size, unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const { uint64_t raw; switch (size) { case kWRegSize: raw = ReadRegister(code, r31mode); break; case kXRegSize: raw = ReadRegister(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; } template VIXL_DEPRECATED("ReadRegister", T reg(unsigned size, unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const) { return ReadRegister(size, code, r31mode); } // Use int64_t by default if T is not specified. int64_t ReadRegister(unsigned size, unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const { return ReadRegister(size, code, r31mode); } VIXL_DEPRECATED("ReadRegister", int64_t reg(unsigned size, unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const) { return ReadRegister(size, code, r31mode); } enum RegLogMode { LogRegWrites, NoRegLog }; // Write 'value' into an integer register. The value is zero-extended. This // behaviour matches AArch64 register writes. // // SP may be specified in one of two ways: // - (code == kSPRegInternalCode) && (r31mode == Reg31IsZeroRegister) // - (code == 31) && (r31mode == Reg31IsStackPointer) template void WriteRegister(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(tmp_64bit); WriteRegister(code, tmp_32bit, log_mode, r31mode); return; } VIXL_ASSERT((sizeof(T) == kWRegSizeInBytes) || (sizeof(T) == kXRegSizeInBytes)); VIXL_ASSERT( (code < kNumberOfRegisters) || ((r31mode == Reg31IsZeroRegister) && (code == kSPRegInternalCode))); if (code == 31) { if (r31mode == Reg31IsZeroRegister) { // Discard writes to the zero register. return; } else { code = kSPRegInternalCode; } } // registers_[31] is the stack pointer. VIXL_STATIC_ASSERT((kSPRegInternalCode % kNumberOfRegisters) == 31); registers_[code % kNumberOfRegisters].Write(value); if (log_mode == LogRegWrites) { LogRegister(code, GetPrintRegisterFormatForSize(sizeof(T))); } } template VIXL_DEPRECATED("WriteRegister", void set_reg(unsigned code, T value, RegLogMode log_mode = LogRegWrites, Reg31Mode r31mode = Reg31IsZeroRegister)) { WriteRegister(code, value, log_mode, r31mode); } // Common specialized accessors for the set_reg() template. void WriteWRegister(unsigned code, int32_t value, RegLogMode log_mode = LogRegWrites, Reg31Mode r31mode = Reg31IsZeroRegister) { WriteRegister(code, value, log_mode, r31mode); } VIXL_DEPRECATED("WriteWRegister", void set_wreg(unsigned code, int32_t value, RegLogMode log_mode = LogRegWrites, Reg31Mode r31mode = Reg31IsZeroRegister)) { WriteWRegister(code, value, log_mode, r31mode); } void WriteXRegister(unsigned code, int64_t value, RegLogMode log_mode = LogRegWrites, Reg31Mode r31mode = Reg31IsZeroRegister) { WriteRegister(code, value, log_mode, r31mode); } VIXL_DEPRECATED("WriteXRegister", void set_xreg(unsigned code, int64_t value, RegLogMode log_mode = LogRegWrites, Reg31Mode r31mode = Reg31IsZeroRegister)) { WriteXRegister(code, value, log_mode, r31mode); } // As above, with parameterized size and type. The value is either // zero-extended or truncated to fit, as required. template void WriteRegister(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: WriteRegister(code, static_cast(raw), log_mode, r31mode); break; case kXRegSize: WriteRegister(code, raw, log_mode, r31mode); break; default: VIXL_UNREACHABLE(); return; } } template VIXL_DEPRECATED("WriteRegister", void set_reg(unsigned size, unsigned code, T value, RegLogMode log_mode = LogRegWrites, Reg31Mode r31mode = Reg31IsZeroRegister)) { WriteRegister(size, code, value, log_mode, r31mode); } // Common specialized accessors for the set_reg() template. // Commonly-used special cases. template void WriteLr(T value) { WriteRegister(kLinkRegCode, value); } template VIXL_DEPRECATED("WriteLr", void set_lr(T value)) { WriteLr(value); } template void WriteSp(T value) { WriteRegister(31, value, LogRegWrites, Reg31IsStackPointer); } template VIXL_DEPRECATED("WriteSp", void set_sp(T value)) { WriteSp(value); } // Vector register accessors. // These are equivalent to the integer register accessors, but for vector // registers. // A structure for representing a 128-bit Q register. struct qreg_t { uint8_t val[kQRegSizeInBytes]; }; // A structure for representing a SVE Z register. struct zreg_t { uint8_t val[kZRegMaxSizeInBytes]; }; // Basic accessor: read the register as the specified type. template T ReadVRegister(unsigned code) const { VIXL_STATIC_ASSERT( (sizeof(T) == kBRegSizeInBytes) || (sizeof(T) == kHRegSizeInBytes) || (sizeof(T) == kSRegSizeInBytes) || (sizeof(T) == kDRegSizeInBytes) || (sizeof(T) == kQRegSizeInBytes)); VIXL_ASSERT(code < kNumberOfVRegisters); return vregisters_[code].Get(); } template VIXL_DEPRECATED("ReadVRegister", T vreg(unsigned code) const) { return ReadVRegister(code); } // Common specialized accessors for the vreg() template. int8_t ReadBRegister(unsigned code) const { return ReadVRegister(code); } VIXL_DEPRECATED("ReadBRegister", int8_t breg(unsigned code) const) { return ReadBRegister(code); } vixl::internal::SimFloat16 ReadHRegister(unsigned code) const { return RawbitsToFloat16(ReadHRegisterBits(code)); } VIXL_DEPRECATED("ReadHRegister", int16_t hreg(unsigned code) const) { return Float16ToRawbits(ReadHRegister(code)); } uint16_t ReadHRegisterBits(unsigned code) const { return ReadVRegister(code); } float ReadSRegister(unsigned code) const { return ReadVRegister(code); } VIXL_DEPRECATED("ReadSRegister", float sreg(unsigned code) const) { return ReadSRegister(code); } uint32_t ReadSRegisterBits(unsigned code) const { return ReadVRegister(code); } VIXL_DEPRECATED("ReadSRegisterBits", uint32_t sreg_bits(unsigned code) const) { return ReadSRegisterBits(code); } double ReadDRegister(unsigned code) const { return ReadVRegister(code); } VIXL_DEPRECATED("ReadDRegister", double dreg(unsigned code) const) { return ReadDRegister(code); } uint64_t ReadDRegisterBits(unsigned code) const { return ReadVRegister(code); } VIXL_DEPRECATED("ReadDRegisterBits", uint64_t dreg_bits(unsigned code) const) { return ReadDRegisterBits(code); } qreg_t ReadQRegister(unsigned code) const { return ReadVRegister(code); } VIXL_DEPRECATED("ReadQRegister", qreg_t qreg(unsigned code) const) { return ReadQRegister(code); } // As above, with parameterized size and return type. The value is // either zero-extended or truncated to fit, as required. template T ReadVRegister(unsigned size, unsigned code) const { uint64_t raw = 0; T result; switch (size) { case kSRegSize: raw = ReadVRegister(code); break; case kDRegSize: raw = ReadVRegister(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; } template VIXL_DEPRECATED("ReadVRegister", T vreg(unsigned size, unsigned code) const) { return ReadVRegister(size, code); } SimVRegister& ReadVRegister(unsigned code) { return vregisters_[code]; } VIXL_DEPRECATED("ReadVRegister", SimVRegister& vreg(unsigned code)) { return ReadVRegister(code); } // Basic accessor: Write the specified value. template void WriteVRegister(unsigned code, T value, RegLogMode log_mode = LogRegWrites) { VIXL_STATIC_ASSERT((sizeof(value) == kBRegSizeInBytes) || (sizeof(value) == kHRegSizeInBytes) || (sizeof(value) == kSRegSizeInBytes) || (sizeof(value) == kDRegSizeInBytes) || (sizeof(value) == kQRegSizeInBytes) || (sizeof(value) == kZRegMaxSizeInBytes)); VIXL_ASSERT(code < kNumberOfVRegisters); vregisters_[code].Write(value); if (log_mode == LogRegWrites) { LogVRegister(code, GetPrintRegisterFormat(value)); } } template VIXL_DEPRECATED("WriteVRegister", void set_vreg(unsigned code, T value, RegLogMode log_mode = LogRegWrites)) { WriteVRegister(code, value, log_mode); } // Common specialized accessors for the WriteVRegister() template. void WriteBRegister(unsigned code, int8_t value, RegLogMode log_mode = LogRegWrites) { WriteVRegister(code, value, log_mode); } VIXL_DEPRECATED("WriteBRegister", void set_breg(unsigned code, int8_t value, RegLogMode log_mode = LogRegWrites)) { return WriteBRegister(code, value, log_mode); } void WriteHRegister(unsigned code, vixl::internal::SimFloat16 value, RegLogMode log_mode = LogRegWrites) { WriteVRegister(code, Float16ToRawbits(value), log_mode); } void WriteHRegister(unsigned code, int16_t value, RegLogMode log_mode = LogRegWrites) { WriteVRegister(code, value, log_mode); } VIXL_DEPRECATED("WriteHRegister", void set_hreg(unsigned code, int16_t value, RegLogMode log_mode = LogRegWrites)) { return WriteHRegister(code, value, log_mode); } void WriteSRegister(unsigned code, float value, RegLogMode log_mode = LogRegWrites) { WriteVRegister(code, value, log_mode); } VIXL_DEPRECATED("WriteSRegister", void set_sreg(unsigned code, float value, RegLogMode log_mode = LogRegWrites)) { WriteSRegister(code, value, log_mode); } void WriteSRegisterBits(unsigned code, uint32_t value, RegLogMode log_mode = LogRegWrites) { WriteVRegister(code, value, log_mode); } VIXL_DEPRECATED("WriteSRegisterBits", void set_sreg_bits(unsigned code, uint32_t value, RegLogMode log_mode = LogRegWrites)) { WriteSRegisterBits(code, value, log_mode); } void WriteDRegister(unsigned code, double value, RegLogMode log_mode = LogRegWrites) { WriteVRegister(code, value, log_mode); } VIXL_DEPRECATED("WriteDRegister", void set_dreg(unsigned code, double value, RegLogMode log_mode = LogRegWrites)) { WriteDRegister(code, value, log_mode); } void WriteDRegisterBits(unsigned code, uint64_t value, RegLogMode log_mode = LogRegWrites) { WriteVRegister(code, value, log_mode); } VIXL_DEPRECATED("WriteDRegisterBits", void set_dreg_bits(unsigned code, uint64_t value, RegLogMode log_mode = LogRegWrites)) { WriteDRegisterBits(code, value, log_mode); } void WriteQRegister(unsigned code, qreg_t value, RegLogMode log_mode = LogRegWrites) { WriteVRegister(code, value, log_mode); } VIXL_DEPRECATED("WriteQRegister", void set_qreg(unsigned code, qreg_t value, RegLogMode log_mode = LogRegWrites)) { WriteQRegister(code, value, log_mode); } void WriteZRegister(unsigned code, zreg_t value, RegLogMode log_mode = LogRegWrites) { WriteVRegister(code, value, log_mode); } template T ReadRegister(Register reg) const { return ReadRegister(reg.GetCode(), Reg31IsZeroRegister); } template void WriteRegister(Register reg, T value, RegLogMode log_mode = LogRegWrites) { WriteRegister(reg.GetCode(), value, log_mode, Reg31IsZeroRegister); } template T ReadVRegister(VRegister vreg) const { return ReadVRegister(vreg.GetCode()); } template void WriteVRegister(VRegister vreg, T value, RegLogMode log_mode = LogRegWrites) { WriteVRegister(vreg.GetCode(), value, log_mode); } template T ReadCPURegister(CPURegister reg) const { if (reg.IsVRegister()) { return ReadVRegister(VRegister(reg)); } else { return ReadRegister(Register(reg)); } } template void WriteCPURegister(CPURegister reg, T value, RegLogMode log_mode = LogRegWrites) { if (reg.IsVRegister()) { WriteVRegister(VRegister(reg), value, log_mode); } else { WriteRegister(Register(reg), value, log_mode); } } template T MemRead(A address) const { Instruction const* pc = ReadPc(); return memory_.Read(address, pc); } template void MemWrite(A address, T value) const { Instruction const* pc = ReadPc(); return memory_.Write(address, value, pc); } template uint64_t MemReadUint(int size_in_bytes, A address) const { return memory_.ReadUint(size_in_bytes, address); } template int64_t MemReadInt(int size_in_bytes, A address) const { return memory_.ReadInt(size_in_bytes, address); } template void MemWrite(int size_in_bytes, A address, uint64_t value) const { return memory_.Write(size_in_bytes, address, value); } void LoadLane(LogicVRegister dst, VectorFormat vform, int index, uint64_t addr) const { unsigned msize_in_bytes = LaneSizeInBytesFromFormat(vform); LoadUintToLane(dst, vform, msize_in_bytes, index, addr); } void LoadUintToLane(LogicVRegister dst, VectorFormat vform, unsigned msize_in_bytes, int index, uint64_t addr) const { dst.SetUint(vform, index, MemReadUint(msize_in_bytes, addr)); } void LoadIntToLane(LogicVRegister dst, VectorFormat vform, unsigned msize_in_bytes, int index, uint64_t addr) const { dst.SetInt(vform, index, MemReadInt(msize_in_bytes, addr)); } void StoreLane(const LogicVRegister& src, VectorFormat vform, int index, uint64_t addr) const { unsigned msize_in_bytes = LaneSizeInBytesFromFormat(vform); MemWrite(msize_in_bytes, addr, src.Uint(vform, index)); } uint64_t ComputeMemOperandAddress(const MemOperand& mem_op) const; template T ReadGenericOperand(GenericOperand operand) const { if (operand.IsCPURegister()) { return ReadCPURegister(operand.GetCPURegister()); } else { VIXL_ASSERT(operand.IsMemOperand()); return MemRead(ComputeMemOperandAddress(operand.GetMemOperand())); } } template void WriteGenericOperand(GenericOperand operand, T value, RegLogMode log_mode = LogRegWrites) { if (operand.IsCPURegister()) { // Outside SIMD, registers are 64-bit or a subset of a 64-bit register. If // the width of the value to write is smaller than 64 bits, the unused // bits may contain unrelated values that the code following this write // needs to handle gracefully. // Here we fill the unused bits with a predefined pattern to catch issues // early. VIXL_ASSERT(operand.GetCPURegister().GetSizeInBits() <= 64); uint64_t raw = 0xdeadda1adeadda1a; memcpy(&raw, &value, sizeof(value)); WriteCPURegister(operand.GetCPURegister(), raw, log_mode); } else { VIXL_ASSERT(operand.IsMemOperand()); MemWrite(ComputeMemOperandAddress(operand.GetMemOperand()), value); } } bool ReadN() const { return nzcv_.GetN() != 0; } VIXL_DEPRECATED("ReadN", bool N() const) { return ReadN(); } bool ReadZ() const { return nzcv_.GetZ() != 0; } VIXL_DEPRECATED("ReadZ", bool Z() const) { return ReadZ(); } bool ReadC() const { return nzcv_.GetC() != 0; } VIXL_DEPRECATED("ReadC", bool C() const) { return ReadC(); } bool ReadV() const { return nzcv_.GetV() != 0; } VIXL_DEPRECATED("ReadV", bool V() const) { return ReadV(); } SimSystemRegister& ReadNzcv() { return nzcv_; } VIXL_DEPRECATED("ReadNzcv", SimSystemRegister& nzcv()) { return ReadNzcv(); } // TODO: Find a way to make the fpcr_ members return the proper types, so // these accessors are not necessary. FPRounding ReadRMode() const { return static_cast(fpcr_.GetRMode()); } VIXL_DEPRECATED("ReadRMode", FPRounding RMode()) { return ReadRMode(); } UseDefaultNaN ReadDN() const { return fpcr_.GetDN() != 0 ? kUseDefaultNaN : kIgnoreDefaultNaN; } VIXL_DEPRECATED("ReadDN", bool DN()) { return ReadDN() == kUseDefaultNaN ? true : false; } SimSystemRegister& ReadFpcr() { return fpcr_; } VIXL_DEPRECATED("ReadFpcr", SimSystemRegister& fpcr()) { return ReadFpcr(); } // Specify relevant register formats for Print(V)Register and related helpers. enum PrintRegisterFormat { // The lane size. kPrintRegLaneSizeB = 0 << 0, kPrintRegLaneSizeH = 1 << 0, kPrintRegLaneSizeS = 2 << 0, kPrintRegLaneSizeW = kPrintRegLaneSizeS, kPrintRegLaneSizeD = 3 << 0, kPrintRegLaneSizeX = kPrintRegLaneSizeD, kPrintRegLaneSizeQ = 4 << 0, kPrintRegLaneSizeUnknown = 5 << 0, kPrintRegLaneSizeOffset = 0, kPrintRegLaneSizeMask = 7 << 0, // The overall register size. kPrintRegAsScalar = 0, kPrintRegAsDVector = 1 << 3, kPrintRegAsQVector = 2 << 3, kPrintRegAsSVEVector = 3 << 3, kPrintRegAsVectorMask = 3 << 3, // Indicate floating-point format lanes. (This flag is only supported for // S-, H-, and D-sized lanes.) kPrintRegAsFP = 1 << 5, // With this flag, print helpers won't check that the upper bits are zero. // This also forces the register name to be printed with the `reg` // format. // // The flag is supported with any PrintRegisterFormat other than those with // kPrintRegAsSVEVector. kPrintRegPartial = 1 << 6, // Supported combinations. // These exist so that they can be referred to by name, but also because C++ // does not allow enum types to hold values that aren't explicitly // enumerated, and we want to be able to combine the above flags. // Scalar formats. #define VIXL_DECL_PRINT_REG_SCALAR(size) \ kPrint##size##Reg = kPrintRegLaneSize##size | kPrintRegAsScalar, \ kPrint##size##RegPartial = kPrintRegLaneSize##size | kPrintRegPartial #define VIXL_DECL_PRINT_REG_SCALAR_FP(size) \ VIXL_DECL_PRINT_REG_SCALAR(size) \ , kPrint##size##RegFP = kPrint##size##Reg | kPrintRegAsFP, \ kPrint##size##RegPartialFP = kPrint##size##RegPartial | kPrintRegAsFP VIXL_DECL_PRINT_REG_SCALAR(W), VIXL_DECL_PRINT_REG_SCALAR(X), VIXL_DECL_PRINT_REG_SCALAR_FP(H), VIXL_DECL_PRINT_REG_SCALAR_FP(S), VIXL_DECL_PRINT_REG_SCALAR_FP(D), VIXL_DECL_PRINT_REG_SCALAR(Q), #undef VIXL_DECL_PRINT_REG_SCALAR #undef VIXL_DECL_PRINT_REG_SCALAR_FP #define VIXL_DECL_PRINT_REG_NEON(count, type, size) \ kPrintReg##count##type = kPrintRegLaneSize##type | kPrintRegAs##size, \ kPrintReg##count##type##Partial = kPrintReg##count##type | kPrintRegPartial #define VIXL_DECL_PRINT_REG_NEON_FP(count, type, size) \ VIXL_DECL_PRINT_REG_NEON(count, type, size) \ , kPrintReg##count##type##FP = kPrintReg##count##type | kPrintRegAsFP, \ kPrintReg##count##type##PartialFP = \ kPrintReg##count##type##Partial | kPrintRegAsFP VIXL_DECL_PRINT_REG_NEON(1, B, Scalar), VIXL_DECL_PRINT_REG_NEON(8, B, DVector), VIXL_DECL_PRINT_REG_NEON(16, B, QVector), VIXL_DECL_PRINT_REG_NEON_FP(1, H, Scalar), VIXL_DECL_PRINT_REG_NEON_FP(4, H, DVector), VIXL_DECL_PRINT_REG_NEON_FP(8, H, QVector), VIXL_DECL_PRINT_REG_NEON_FP(1, S, Scalar), VIXL_DECL_PRINT_REG_NEON_FP(2, S, DVector), VIXL_DECL_PRINT_REG_NEON_FP(4, S, QVector), VIXL_DECL_PRINT_REG_NEON_FP(1, D, Scalar), VIXL_DECL_PRINT_REG_NEON_FP(2, D, QVector), VIXL_DECL_PRINT_REG_NEON(1, Q, Scalar), #undef VIXL_DECL_PRINT_REG_NEON #undef VIXL_DECL_PRINT_REG_NEON_FP #define VIXL_DECL_PRINT_REG_SVE(type) \ kPrintRegVn##type = kPrintRegLaneSize##type | kPrintRegAsSVEVector, \ kPrintRegVn##type##Partial = kPrintRegVn##type | kPrintRegPartial #define VIXL_DECL_PRINT_REG_SVE_FP(type) \ VIXL_DECL_PRINT_REG_SVE(type) \ , kPrintRegVn##type##FP = kPrintRegVn##type | kPrintRegAsFP, \ kPrintRegVn##type##PartialFP = kPrintRegVn##type##Partial | kPrintRegAsFP VIXL_DECL_PRINT_REG_SVE(B), VIXL_DECL_PRINT_REG_SVE_FP(H), VIXL_DECL_PRINT_REG_SVE_FP(S), VIXL_DECL_PRINT_REG_SVE_FP(D), VIXL_DECL_PRINT_REG_SVE(Q) #undef VIXL_DECL_PRINT_REG_SVE #undef VIXL_DECL_PRINT_REG_SVE_FP }; // Return `format` with the kPrintRegPartial flag set. PrintRegisterFormat GetPrintRegPartial(PrintRegisterFormat format) { // Every PrintRegisterFormat has a kPrintRegPartial counterpart, so the // result of this cast will always be well-defined. return static_cast(format | kPrintRegPartial); } // For SVE formats, return the format of a Q register part of it. PrintRegisterFormat GetPrintRegAsQChunkOfSVE(PrintRegisterFormat format) { VIXL_ASSERT((format & kPrintRegAsVectorMask) == kPrintRegAsSVEVector); // Keep the FP and lane size fields. int q_format = format & (kPrintRegLaneSizeMask | kPrintRegAsFP); // The resulting format must always be partial, because we're not formatting // the whole Z register. q_format |= (kPrintRegAsQVector | kPrintRegPartial); // This cast is always safe because NEON QVector formats support every // combination of FP and lane size that SVE formats do. return static_cast(q_format); } unsigned GetPrintRegLaneSizeInBytesLog2(PrintRegisterFormat format) { VIXL_ASSERT((format & kPrintRegLaneSizeMask) != kPrintRegLaneSizeUnknown); return (format & kPrintRegLaneSizeMask) >> kPrintRegLaneSizeOffset; } unsigned GetPrintRegLaneSizeInBytes(PrintRegisterFormat format) { return 1 << GetPrintRegLaneSizeInBytesLog2(format); } unsigned GetPrintRegSizeInBytesLog2(PrintRegisterFormat format) { switch (format & kPrintRegAsVectorMask) { case kPrintRegAsScalar: return GetPrintRegLaneSizeInBytesLog2(format); case kPrintRegAsDVector: return kDRegSizeInBytesLog2; case kPrintRegAsQVector: return kQRegSizeInBytesLog2; default: case kPrintRegAsSVEVector: // We print SVE vectors in Q-sized chunks. These need special handling, // and it's probably an error to call this function in that case. VIXL_UNREACHABLE(); return kQRegSizeInBytesLog2; } } unsigned GetPrintRegSizeInBytes(PrintRegisterFormat format) { return 1 << GetPrintRegSizeInBytesLog2(format); } unsigned GetPrintRegSizeInBitsLog2(PrintRegisterFormat format) { return GetPrintRegSizeInBytesLog2(format) + kBitsPerByteLog2; } unsigned GetPrintRegSizeInBits(PrintRegisterFormat format) { return 1 << GetPrintRegSizeInBitsLog2(format); } const char* GetPartialRegSuffix(PrintRegisterFormat format) { switch (GetPrintRegSizeInBitsLog2(format)) { case kBRegSizeLog2: return "<7:0>"; case kHRegSizeLog2: return "<15:0>"; case kSRegSizeLog2: return "<31:0>"; case kDRegSizeLog2: return "<63:0>"; case kQRegSizeLog2: return "<127:0>"; } VIXL_UNREACHABLE(); return ""; } unsigned GetPrintRegLaneCount(PrintRegisterFormat format) { unsigned reg_size_log2 = GetPrintRegSizeInBytesLog2(format); unsigned lane_size_log2 = GetPrintRegLaneSizeInBytesLog2(format); VIXL_ASSERT(reg_size_log2 >= lane_size_log2); return 1 << (reg_size_log2 - lane_size_log2); } uint16_t GetPrintRegLaneMask(PrintRegisterFormat format) { int print_as = format & kPrintRegAsVectorMask; if (print_as == kPrintRegAsScalar) return 1; // Vector formats, including SVE formats printed in Q-sized chunks. static const uint16_t masks[] = {0xffff, 0x5555, 0x1111, 0x0101, 0x0001}; unsigned size_in_bytes_log2 = GetPrintRegLaneSizeInBytesLog2(format); VIXL_ASSERT(size_in_bytes_log2 < ArrayLength(masks)); uint16_t mask = masks[size_in_bytes_log2]; // Exclude lanes that aren't visible in D vectors. if (print_as == kPrintRegAsDVector) mask &= 0x00ff; return mask; } PrintRegisterFormat GetPrintRegisterFormatForSize(unsigned reg_size, unsigned lane_size); PrintRegisterFormat GetPrintRegisterFormatForSize(unsigned size) { return GetPrintRegisterFormatForSize(size, size); } PrintRegisterFormat GetPrintRegisterFormatForSizeFP(unsigned size) { switch (size) { default: VIXL_UNREACHABLE(); return kPrintDReg; case kDRegSizeInBytes: return kPrintDReg; case kSRegSizeInBytes: return kPrintSReg; case kHRegSizeInBytes: return kPrintHReg; } } PrintRegisterFormat GetPrintRegisterFormatTryFP(PrintRegisterFormat format) { if ((GetPrintRegLaneSizeInBytes(format) == kHRegSizeInBytes) || (GetPrintRegLaneSizeInBytes(format) == kSRegSizeInBytes) || (GetPrintRegLaneSizeInBytes(format) == kDRegSizeInBytes)) { return static_cast(format | kPrintRegAsFP); } return format; } PrintRegisterFormat GetPrintRegisterFormatForSizeTryFP(unsigned size) { return GetPrintRegisterFormatTryFP(GetPrintRegisterFormatForSize(size)); } template PrintRegisterFormat GetPrintRegisterFormat(T value) { return GetPrintRegisterFormatForSize(sizeof(value)); } PrintRegisterFormat GetPrintRegisterFormat(double value) { VIXL_STATIC_ASSERT(sizeof(value) == kDRegSizeInBytes); return GetPrintRegisterFormatForSizeFP(sizeof(value)); } PrintRegisterFormat GetPrintRegisterFormat(float value) { VIXL_STATIC_ASSERT(sizeof(value) == kSRegSizeInBytes); return GetPrintRegisterFormatForSizeFP(sizeof(value)); } PrintRegisterFormat GetPrintRegisterFormat(Float16 value) { VIXL_STATIC_ASSERT(sizeof(Float16ToRawbits(value)) == kHRegSizeInBytes); return GetPrintRegisterFormatForSizeFP(sizeof(Float16ToRawbits(value))); } PrintRegisterFormat GetPrintRegisterFormat(VectorFormat vform); PrintRegisterFormat GetPrintRegisterFormatFP(VectorFormat vform); // Print all registers of the specified types. void PrintRegisters(); void PrintVRegisters(); void PrintZRegisters(); void PrintSystemRegisters(); // As above, but only print the registers that have been updated. void PrintWrittenRegisters(); void PrintWrittenVRegisters(); void PrintWrittenPRegisters(); // As above, but respect LOG_REG and LOG_VREG. void LogWrittenRegisters() { if (ShouldTraceRegs()) PrintWrittenRegisters(); } void LogWrittenVRegisters() { if (ShouldTraceVRegs()) PrintWrittenVRegisters(); } void LogWrittenPRegisters() { if (ShouldTraceVRegs()) PrintWrittenPRegisters(); } void LogAllWrittenRegisters() { LogWrittenRegisters(); LogWrittenVRegisters(); LogWrittenPRegisters(); } // The amount of space to leave for a register name. This is used to keep the // values vertically aligned. The longest register name has the form // "z31<2047:1920>". The total overall value indentation must also take into // account the fixed formatting: "# {name}: 0x{value}". static const int kPrintRegisterNameFieldWidth = 14; // Print whole, individual register values. // - The format can be used to restrict how much of the register is printed, // but such formats indicate that the unprinted high-order bits are zero and // these helpers will assert that. // - If the format includes the kPrintRegAsFP flag then human-friendly FP // value annotations will be printed. // - The suffix can be used to add annotations (such as memory access // details), or to suppress the newline. void PrintRegister(int code, PrintRegisterFormat format = kPrintXReg, const char* suffix = "\n"); void PrintVRegister(int code, PrintRegisterFormat format = kPrintReg1Q, const char* suffix = "\n"); // PrintZRegister and PrintPRegister print over several lines, so they cannot // allow the suffix to be overridden. void PrintZRegister(int code, PrintRegisterFormat format = kPrintRegVnQ); void PrintPRegister(int code, PrintRegisterFormat format = kPrintRegVnQ); void PrintFFR(PrintRegisterFormat format = kPrintRegVnQ); // Print a single Q-sized part of a Z register, or the corresponding two-byte // part of a P register. These print single lines, and therefore allow the // suffix to be overridden. The format must include the kPrintRegPartial flag. void PrintPartialZRegister(int code, int q_index, PrintRegisterFormat format = kPrintRegVnQ, const char* suffix = "\n"); void PrintPartialPRegister(int code, int q_index, PrintRegisterFormat format = kPrintRegVnQ, const char* suffix = "\n"); void PrintPartialPRegister(const char* name, const SimPRegister& reg, int q_index, PrintRegisterFormat format = kPrintRegVnQ, const char* suffix = "\n"); // Like Print*Register (above), but respect trace parameters. void LogRegister(unsigned code, PrintRegisterFormat format) { if (ShouldTraceRegs()) PrintRegister(code, format); } void LogVRegister(unsigned code, PrintRegisterFormat format) { if (ShouldTraceVRegs()) PrintVRegister(code, format); } void LogZRegister(unsigned code, PrintRegisterFormat format) { if (ShouldTraceVRegs()) PrintZRegister(code, format); } void LogPRegister(unsigned code, PrintRegisterFormat format) { if (ShouldTraceVRegs()) PrintPRegister(code, format); } void LogFFR(PrintRegisterFormat format) { if (ShouldTraceVRegs()) PrintFFR(format); } // Other state updates, including system registers. void PrintSystemRegister(SystemRegister id); void PrintTakenBranch(const Instruction* target); void LogSystemRegister(SystemRegister id) { if (ShouldTraceSysRegs()) PrintSystemRegister(id); } void LogTakenBranch(const Instruction* target) { if (ShouldTraceBranches()) PrintTakenBranch(target); } // Trace memory accesses. // Common, contiguous register accesses (such as for scalars). // The *Write variants automatically set kPrintRegPartial on the format. void PrintRead(int rt_code, PrintRegisterFormat format, uintptr_t address); void PrintExtendingRead(int rt_code, PrintRegisterFormat format, int access_size_in_bytes, uintptr_t address); void PrintWrite(int rt_code, PrintRegisterFormat format, uintptr_t address); void PrintVRead(int rt_code, PrintRegisterFormat format, uintptr_t address); void PrintVWrite(int rt_code, PrintRegisterFormat format, uintptr_t address); // Simple, unpredicated SVE accesses always access the whole vector, and never // know the lane type, so there's no need to accept a `format`. void PrintZRead(int rt_code, uintptr_t address) { vregisters_[rt_code].NotifyRegisterLogged(); PrintZAccess(rt_code, "<-", address); } void PrintZWrite(int rt_code, uintptr_t address) { PrintZAccess(rt_code, "->", address); } void PrintPRead(int rt_code, uintptr_t address) { pregisters_[rt_code].NotifyRegisterLogged(); PrintPAccess(rt_code, "<-", address); } void PrintPWrite(int rt_code, uintptr_t address) { PrintPAccess(rt_code, "->", address); } // Like Print* (above), but respect GetTraceParameters(). void LogRead(int rt_code, PrintRegisterFormat format, uintptr_t address) { if (ShouldTraceRegs()) PrintRead(rt_code, format, address); } void LogExtendingRead(int rt_code, PrintRegisterFormat format, int access_size_in_bytes, uintptr_t address) { if (ShouldTraceRegs()) { PrintExtendingRead(rt_code, format, access_size_in_bytes, address); } } void LogWrite(int rt_code, PrintRegisterFormat format, uintptr_t address) { if (ShouldTraceWrites()) PrintWrite(rt_code, format, address); } void LogVRead(int rt_code, PrintRegisterFormat format, uintptr_t address) { if (ShouldTraceVRegs()) PrintVRead(rt_code, format, address); } void LogVWrite(int rt_code, PrintRegisterFormat format, uintptr_t address) { if (ShouldTraceWrites()) PrintVWrite(rt_code, format, address); } void LogZRead(int rt_code, uintptr_t address) { if (ShouldTraceVRegs()) PrintZRead(rt_code, address); } void LogZWrite(int rt_code, uintptr_t address) { if (ShouldTraceWrites()) PrintZWrite(rt_code, address); } void LogPRead(int rt_code, uintptr_t address) { if (ShouldTraceVRegs()) PrintPRead(rt_code, address); } void LogPWrite(int rt_code, uintptr_t address) { if (ShouldTraceWrites()) PrintPWrite(rt_code, address); } void LogMemTransfer(uintptr_t dst, uintptr_t src, uint8_t value) { if (ShouldTraceWrites()) PrintMemTransfer(dst, src, value); } // Helpers for the above, where the access operation is parameterised. // - For loads, set op = "<-". // - For stores, set op = "->". void PrintAccess(int rt_code, PrintRegisterFormat format, const char* op, uintptr_t address); void PrintVAccess(int rt_code, PrintRegisterFormat format, const char* op, uintptr_t address); void PrintMemTransfer(uintptr_t dst, uintptr_t src, uint8_t value); // Simple, unpredicated SVE accesses always access the whole vector, and never // know the lane type, so these don't accept a `format`. void PrintZAccess(int rt_code, const char* op, uintptr_t address); void PrintPAccess(int rt_code, const char* op, uintptr_t address); // Multiple-structure accesses. void PrintVStructAccess(int rt_code, int reg_count, PrintRegisterFormat format, const char* op, uintptr_t address); // Single-structure (single-lane) accesses. void PrintVSingleStructAccess(int rt_code, int reg_count, int lane, PrintRegisterFormat format, const char* op, uintptr_t address); // Replicating accesses. void PrintVReplicatingStructAccess(int rt_code, int reg_count, PrintRegisterFormat format, const char* op, uintptr_t address); // Multiple-structure accesses. void PrintZStructAccess(int rt_code, int reg_count, const LogicPRegister& pg, PrintRegisterFormat format, int msize_in_bytes, const char* op, const LogicSVEAddressVector& addr); // Register-printing helper for all structured accessors. // // All lanes (according to `format`) are printed, but lanes indicated by // `focus_mask` are of particular interest. Each bit corresponds to a byte in // the printed register, in a manner similar to SVE's predicates. Currently, // this is used to determine when to print human-readable FP annotations. void PrintVRegistersForStructuredAccess(int rt_code, int reg_count, uint16_t focus_mask, PrintRegisterFormat format); // As for the VRegister variant, but print partial Z register names. void PrintZRegistersForStructuredAccess(int rt_code, int q_index, int reg_count, uint16_t focus_mask, PrintRegisterFormat format); // Print part of a memory access. This should be used for annotating // non-trivial accesses, such as structured or sign-extending loads. Call // Print*Register (or Print*RegistersForStructuredAccess), then // PrintPartialAccess for each contiguous access that makes up the // instruction. // // access_mask: // The lanes to be printed. Each bit corresponds to a byte in the printed // register, in a manner similar to SVE's predicates, except that the // lane size is not respected when interpreting lane_mask: unaligned bits // must be zeroed. // // This function asserts that this mask is non-zero. // // future_access_mask: // The lanes to be printed by a future invocation. This must be specified // because vertical lines are drawn for partial accesses that haven't yet // been printed. The format is the same as for accessed_mask. // // If a lane is active in both `access_mask` and `future_access_mask`, // `access_mask` takes precedence. // // struct_element_count: // The number of elements in each structure. For non-structured accesses, // set this to one. Along with lane_size_in_bytes, this is used determine // the size of each access, and to format the accessed value. // // op: // For stores, use "->". For loads, use "<-". // // address: // The address of this partial access. (Not the base address of the whole // instruction.) The traced value is read from this address (according to // part_count and lane_size_in_bytes) so it must be accessible, and when // tracing stores, the store must have been executed before this function // is called. // // reg_size_in_bytes: // The size of the register being accessed. This helper is usually used // for V registers or Q-sized chunks of Z registers, so that is the // default, but it is possible to use this to annotate X register // accesses by specifying kXRegSizeInBytes. // // The return value is a future_access_mask suitable for the next iteration, // so that it is possible to execute this in a loop, until the mask is zero. // Note that accessed_mask must still be updated by the caller for each call. uint16_t PrintPartialAccess(uint16_t access_mask, uint16_t future_access_mask, int struct_element_count, int lane_size_in_bytes, const char* op, uintptr_t address, int reg_size_in_bytes = kQRegSizeInBytes); // Print an abstract register value. This works for all register types, and // can print parts of registers. This exists to ensure consistent formatting // of values. void PrintRegisterValue(const uint8_t* value, int value_size, PrintRegisterFormat format); template void PrintRegisterValue(const T& sim_register, PrintRegisterFormat format) { PrintRegisterValue(sim_register.GetBytes(), std::min(sim_register.GetSizeInBytes(), kQRegSizeInBytes), format); } // As above, but format as an SVE predicate value, using binary notation with // spaces between each bit so that they align with the Z register bytes that // they predicate. void PrintPRegisterValue(uint16_t value); void PrintRegisterValueFPAnnotations(const uint8_t* value, uint16_t lane_mask, PrintRegisterFormat format); template void PrintRegisterValueFPAnnotations(const T& sim_register, uint16_t lane_mask, PrintRegisterFormat format) { PrintRegisterValueFPAnnotations(sim_register.GetBytes(), lane_mask, format); } template void PrintRegisterValueFPAnnotations(const T& sim_register, PrintRegisterFormat format) { PrintRegisterValueFPAnnotations(sim_register.GetBytes(), GetPrintRegLaneMask(format), format); } VIXL_NO_RETURN void DoUnreachable(const Instruction* instr); void DoTrace(const Instruction* instr); void DoLog(const Instruction* instr); static const char* WRegNameForCode(unsigned code, Reg31Mode mode = Reg31IsZeroRegister); static const char* XRegNameForCode(unsigned code, Reg31Mode mode = Reg31IsZeroRegister); static const char* BRegNameForCode(unsigned code); static const char* HRegNameForCode(unsigned code); static const char* SRegNameForCode(unsigned code); static const char* DRegNameForCode(unsigned code); static const char* VRegNameForCode(unsigned code); static const char* ZRegNameForCode(unsigned code); static const char* PRegNameForCode(unsigned code); bool IsColouredTrace() const { return coloured_trace_; } VIXL_DEPRECATED("IsColouredTrace", bool coloured_trace() const) { return IsColouredTrace(); } void SetColouredTrace(bool value); VIXL_DEPRECATED("SetColouredTrace", void set_coloured_trace(bool value)) { SetColouredTrace(value); } // Values for traces parameters defined in simulator-constants-aarch64.h in // enum TraceParameters. int GetTraceParameters() const { return trace_parameters_; } VIXL_DEPRECATED("GetTraceParameters", int trace_parameters() const) { return GetTraceParameters(); } bool ShouldTraceWrites() const { return (GetTraceParameters() & LOG_WRITE) != 0; } bool ShouldTraceRegs() const { return (GetTraceParameters() & LOG_REGS) != 0; } bool ShouldTraceVRegs() const { return (GetTraceParameters() & LOG_VREGS) != 0; } bool ShouldTraceSysRegs() const { return (GetTraceParameters() & LOG_SYSREGS) != 0; } bool ShouldTraceBranches() const { return (GetTraceParameters() & LOG_BRANCH) != 0; } void SetTraceParameters(int parameters); VIXL_DEPRECATED("SetTraceParameters", void set_trace_parameters(int parameters)) { SetTraceParameters(parameters); } // Clear the simulated local monitor to force the next store-exclusive // instruction to fail. void ClearLocalMonitor() { local_monitor_.Clear(); } void SilenceExclusiveAccessWarning() { print_exclusive_access_warning_ = false; } void CheckIsValidUnalignedAtomicAccess(int rn, uint64_t address, unsigned access_size) { // Verify that the address is available to the host. VIXL_ASSERT(address == static_cast(address)); if (GetCPUFeatures()->Has(CPUFeatures::kUSCAT)) { // Check that the access falls entirely within one atomic access granule. if (AlignDown(address, kAtomicAccessGranule) != AlignDown(address + access_size - 1, kAtomicAccessGranule)) { VIXL_ALIGNMENT_EXCEPTION(); } } else { // Check that the access is aligned. if (AlignDown(address, access_size) != address) { VIXL_ALIGNMENT_EXCEPTION(); } } // The sp must be aligned to 16 bytes when it is accessed. if ((rn == kSpRegCode) && (AlignDown(address, 16) != address)) { VIXL_ALIGNMENT_EXCEPTION(); } } enum PointerType { kDataPointer, kInstructionPointer }; struct PACKey { uint64_t high; uint64_t low; int number; }; // Current implementation is that all pointers are tagged. bool HasTBI(uint64_t ptr, PointerType type) { USE(ptr, type); return true; } // Current implementation uses 48-bit virtual addresses. int GetBottomPACBit(uint64_t ptr, int ttbr) { USE(ptr, ttbr); VIXL_ASSERT((ttbr == 0) || (ttbr == 1)); return 48; } // The top PAC bit is 55 for the purposes of relative bit fields with TBI, // however bit 55 is the TTBR bit regardless of TBI so isn't part of the PAC // codes in pointers. int GetTopPACBit(uint64_t ptr, PointerType type) { return HasTBI(ptr, type) ? 55 : 63; } // Armv8.3 Pointer authentication helpers. uint64_t CalculatePACMask(uint64_t ptr, PointerType type, int ext_bit); uint64_t ComputePAC(uint64_t data, uint64_t context, PACKey key); uint64_t AuthPAC(uint64_t ptr, uint64_t context, PACKey key, PointerType type); uint64_t AddPAC(uint64_t ptr, uint64_t context, PACKey key, PointerType type); uint64_t StripPAC(uint64_t ptr, PointerType type); void PACHelper(int dst, int src, PACKey key, decltype(&Simulator::AddPAC) pac_fn); // Armv8.5 MTE helpers. uint64_t ChooseNonExcludedTag(uint64_t tag, uint64_t offset, uint64_t exclude = 0) { VIXL_ASSERT(IsUint4(tag) && IsUint4(offset) && IsUint16(exclude)); if (exclude == 0xffff) { return 0; } if (offset == 0) { while ((exclude & (1 << tag)) != 0) { tag = (tag + 1) % 16; } } while (offset > 0) { offset--; tag = (tag + 1) % 16; while ((exclude & (1 << tag)) != 0) { tag = (tag + 1) % 16; } } return tag; } uint64_t GetAddressWithAllocationTag(uint64_t addr, uint64_t tag) { VIXL_ASSERT(IsUint4(tag)); return (addr & ~(UINT64_C(0xf) << 56)) | (tag << 56); } // Create or remove a mapping with memory protection. Memory attributes such // as MTE and BTI are represented by metadata in Simulator. void* Mmap( void* address, size_t length, int prot, int flags, int fd, off_t offset); int Munmap(void* address, size_t length, int prot); // The common CPUFeatures interface with the set of available features. CPUFeatures* GetCPUFeatures() { return cpu_features_auditor_.GetCPUFeatures(); } void SetCPUFeatures(const CPUFeatures& cpu_features) { cpu_features_auditor_.SetCPUFeatures(cpu_features); } // The set of features that the simulator has encountered. const CPUFeatures& GetSeenFeatures() { return cpu_features_auditor_.GetSeenFeatures(); } void ResetSeenFeatures() { cpu_features_auditor_.ResetSeenFeatures(); } // Runtime call emulation support. // It requires VIXL's ABI features, and C++11 or greater. // Also, the initialisation of the tuples in RuntimeCall(Non)Void is incorrect // in GCC before 4.9.1: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51253 #if defined(VIXL_HAS_ABI_SUPPORT) && __cplusplus >= 201103L && \ (defined(__clang__) || GCC_VERSION_OR_NEWER(4, 9, 1)) #define VIXL_HAS_SIMULATED_RUNTIME_CALL_SUPPORT // The implementation of the runtime call helpers require the functionality // provided by `std::index_sequence`. It is only available from C++14, but // we want runtime call simulation to work from C++11, so we emulate if // necessary. #if __cplusplus >= 201402L template using local_index_sequence = std::index_sequence; template using __local_index_sequence_for = std::index_sequence_for; #else // Emulate the behaviour of `std::index_sequence` and // `std::index_sequence_for`. // Naming follow the `std` names, prefixed with `emulated_`. template struct emulated_index_sequence {}; // A recursive template to create a sequence of indexes. // The base case (for `N == 0`) is declared outside of the class scope, as // required by C++. template struct emulated_make_index_sequence_helper : emulated_make_index_sequence_helper {}; template struct emulated_make_index_sequence : emulated_make_index_sequence_helper { }; template struct emulated_index_sequence_for : emulated_make_index_sequence {}; template using local_index_sequence = emulated_index_sequence; template using __local_index_sequence_for = emulated_index_sequence_for; #endif // Expand the argument tuple and perform the call. template R DoRuntimeCall(R (*function)(P...), std::tuple arguments, local_index_sequence) { return function(std::get(arguments)...); } template void RuntimeCallNonVoid(R (*function)(P...)) { ABI abi; std::tuple argument_operands{ ReadGenericOperand

(abi.GetNextParameterGenericOperand

())...}; R return_value = DoRuntimeCall(function, argument_operands, __local_index_sequence_for{}); WriteGenericOperand(abi.GetReturnGenericOperand(), return_value); } template void RuntimeCallVoid(R (*function)(P...)) { ABI abi; std::tuple argument_operands{ ReadGenericOperand

(abi.GetNextParameterGenericOperand

())...}; DoRuntimeCall(function, argument_operands, __local_index_sequence_for{}); } // We use `struct` for `void` return type specialisation. template struct RuntimeCallStructHelper { static void Wrapper(Simulator* simulator, uintptr_t function_pointer) { R (*function)(P...) = reinterpret_cast(function_pointer); simulator->RuntimeCallNonVoid(function); } }; // Partial specialization when the return type is `void`. template struct RuntimeCallStructHelper { static void Wrapper(Simulator* simulator, uintptr_t function_pointer) { void (*function)(P...) = reinterpret_cast(function_pointer); simulator->RuntimeCallVoid(function); } }; #endif // Configure the simulated value of 'VL', which is the size of a Z register. // Because this cannot occur during a program's lifetime, this function also // resets the SVE registers. void SetVectorLengthInBits(unsigned vector_length); unsigned GetVectorLengthInBits() const { return vector_length_; } unsigned GetVectorLengthInBytes() const { VIXL_ASSERT((vector_length_ % kBitsPerByte) == 0); return vector_length_ / kBitsPerByte; } unsigned GetPredicateLengthInBits() const { VIXL_ASSERT((GetVectorLengthInBits() % kZRegBitsPerPRegBit) == 0); return GetVectorLengthInBits() / kZRegBitsPerPRegBit; } unsigned GetPredicateLengthInBytes() const { VIXL_ASSERT((GetVectorLengthInBytes() % kZRegBitsPerPRegBit) == 0); return GetVectorLengthInBytes() / kZRegBitsPerPRegBit; } unsigned RegisterSizeInBitsFromFormat(VectorFormat vform) const { if (IsSVEFormat(vform)) { return GetVectorLengthInBits(); } else { return vixl::aarch64::RegisterSizeInBitsFromFormat(vform); } } unsigned RegisterSizeInBytesFromFormat(VectorFormat vform) const { unsigned size_in_bits = RegisterSizeInBitsFromFormat(vform); VIXL_ASSERT((size_in_bits % kBitsPerByte) == 0); return size_in_bits / kBitsPerByte; } int LaneCountFromFormat(VectorFormat vform) const { if (IsSVEFormat(vform)) { return GetVectorLengthInBits() / LaneSizeInBitsFromFormat(vform); } else { return vixl::aarch64::LaneCountFromFormat(vform); } } bool IsFirstActive(VectorFormat vform, const LogicPRegister& mask, const LogicPRegister& bits) { for (int i = 0; i < LaneCountFromFormat(vform); i++) { if (mask.IsActive(vform, i)) { return bits.IsActive(vform, i); } } return false; } bool AreNoneActive(VectorFormat vform, const LogicPRegister& mask, const LogicPRegister& bits) { for (int i = 0; i < LaneCountFromFormat(vform); i++) { if (mask.IsActive(vform, i) && bits.IsActive(vform, i)) { return false; } } return true; } bool IsLastActive(VectorFormat vform, const LogicPRegister& mask, const LogicPRegister& bits) { for (int i = LaneCountFromFormat(vform) - 1; i >= 0; i--) { if (mask.IsActive(vform, i)) { return bits.IsActive(vform, i); } } return false; } void PredTest(VectorFormat vform, const LogicPRegister& mask, const LogicPRegister& bits) { ReadNzcv().SetN(IsFirstActive(vform, mask, bits)); ReadNzcv().SetZ(AreNoneActive(vform, mask, bits)); ReadNzcv().SetC(!IsLastActive(vform, mask, bits)); ReadNzcv().SetV(0); LogSystemRegister(NZCV); } SimPRegister& GetPTrue() { return pregister_all_true_; } template size_t CleanGranuleTag(T address, size_t length = kMTETagGranuleInBytes) { size_t count = 0; for (size_t offset = 0; offset < length; offset += kMTETagGranuleInBytes) { count += meta_data_.CleanMTETag(reinterpret_cast(address) + offset); } size_t expected = length / kMTETagGranuleInBytes + (length % kMTETagGranuleInBytes != 0); // Give a warning when the memory region that is being unmapped isn't all // either MTE protected or not. if (count != expected) { std::stringstream sstream; sstream << std::hex << "MTE WARNING : the memory region being unmapped " "starting at address 0x" << reinterpret_cast(address) << "is not fully MTE protected.\n"; VIXL_WARNING(sstream.str().c_str()); } return count; } template void SetGranuleTag(T address, int tag, size_t length = kMTETagGranuleInBytes) { for (size_t offset = 0; offset < length; offset += kMTETagGranuleInBytes) { meta_data_.SetMTETag((uintptr_t)(address) + offset, tag); } } template int GetGranuleTag(T address) { return meta_data_.GetMTETag(address); } // Generate a random address tag, and any tags specified in the input are // excluded from the selection. uint64_t GenerateRandomTag(uint16_t exclude = 0); protected: const char* clr_normal; const char* clr_flag_name; const char* clr_flag_value; const char* clr_reg_name; const char* clr_reg_value; const char* clr_vreg_name; const char* clr_vreg_value; const char* clr_preg_name; const char* clr_preg_value; const char* clr_memory_address; const char* clr_warning; const char* clr_warning_message; const char* clr_printf; const char* clr_branch_marker; // Simulation helpers ------------------------------------ void ResetSystemRegisters(); void ResetRegisters(); void ResetVRegisters(); void ResetPRegisters(); void ResetFFR(); bool ConditionPassed(Condition cond) { switch (cond) { case eq: return ReadZ(); case ne: return !ReadZ(); case hs: return ReadC(); case lo: return !ReadC(); case mi: return ReadN(); case pl: return !ReadN(); case vs: return ReadV(); case vc: return !ReadV(); case hi: return ReadC() && !ReadZ(); case ls: return !(ReadC() && !ReadZ()); case ge: return ReadN() == ReadV(); case lt: return ReadN() != ReadV(); case gt: return !ReadZ() && (ReadN() == ReadV()); case le: return !(!ReadZ() && (ReadN() == ReadV())); case nv: VIXL_FALLTHROUGH(); case al: return true; default: VIXL_UNREACHABLE(); return false; } } bool ConditionPassed(Instr cond) { return ConditionPassed(static_cast(cond)); } bool ConditionFailed(Condition cond) { return !ConditionPassed(cond); } void AddSubHelper(const Instruction* instr, int64_t op2); uint64_t AddWithCarry(unsigned reg_size, bool set_flags, uint64_t left, uint64_t right, int carry_in = 0); std::pair AddWithCarry(unsigned reg_size, uint64_t left, uint64_t right, int carry_in); using vixl_uint128_t = std::pair; vixl_uint128_t Add128(vixl_uint128_t x, vixl_uint128_t y); vixl_uint128_t Mul64(uint64_t x, uint64_t y); vixl_uint128_t Neg128(vixl_uint128_t x); void LogicalHelper(const Instruction* instr, int64_t op2); void ConditionalCompareHelper(const Instruction* instr, int64_t op2); void LoadStoreHelper(const Instruction* instr, int64_t offset, AddrMode addrmode); void LoadStorePairHelper(const Instruction* instr, AddrMode addrmode); template void CompareAndSwapHelper(const Instruction* instr); template void CompareAndSwapPairHelper(const Instruction* instr); template void AtomicMemorySimpleHelper(const Instruction* instr); template void AtomicMemorySwapHelper(const Instruction* instr); template void LoadAcquireRCpcHelper(const Instruction* instr); template void LoadAcquireRCpcUnscaledOffsetHelper(const Instruction* instr); template void StoreReleaseUnscaledOffsetHelper(const Instruction* instr); uintptr_t AddressModeHelper(unsigned addr_reg, int64_t offset, AddrMode addrmode); void NEONLoadStoreMultiStructHelper(const Instruction* instr, AddrMode addr_mode); void NEONLoadStoreSingleStructHelper(const Instruction* instr, AddrMode addr_mode); template void MOPSPHelper(const Instruction* instr) { VIXL_ASSERT(instr->IsConsistentMOPSTriplet()); int d = instr->GetRd(); int n = instr->GetRn(); int s = instr->GetRs(); // Aliased registers and xzr are disallowed for Xd and Xn. if ((d == n) || (d == s) || (n == s) || (d == 31) || (n == 31)) { VisitUnallocated(instr); } // Additionally, Xs may not be xzr for cpy. if ((mops_type == "cpy"_h) && (s == 31)) { VisitUnallocated(instr); } // Bits 31 and 30 must be zero. if (instr->ExtractBits(31, 30) != 0) { VisitUnallocated(instr); } // Saturate copy count. uint64_t xn = ReadXRegister(n); int saturation_bits = (mops_type == "cpy"_h) ? 55 : 63; if ((xn >> saturation_bits) != 0) { xn = (UINT64_C(1) << saturation_bits) - 1; if (mops_type == "setg"_h) { // Align saturated value to granule. xn &= ~UINT64_C(kMTETagGranuleInBytes - 1); } WriteXRegister(n, xn); } ReadNzcv().SetN(0); ReadNzcv().SetZ(0); ReadNzcv().SetC(1); // Indicates "option B" implementation. ReadNzcv().SetV(0); } int64_t ShiftOperand(unsigned reg_size, uint64_t value, Shift shift_type, unsigned amount) const; int64_t ExtendValue(unsigned reg_width, int64_t value, Extend extend_type, unsigned left_shift = 0) const; uint64_t PolynomialMult(uint64_t op1, uint64_t op2, int lane_size_in_bits) const; void ld1(VectorFormat vform, LogicVRegister dst, uint64_t addr); void ld1(VectorFormat vform, LogicVRegister dst, int index, uint64_t addr); void ld1r(VectorFormat vform, LogicVRegister dst, uint64_t addr); void ld1r(VectorFormat vform, VectorFormat unpack_vform, LogicVRegister dst, uint64_t addr, bool is_signed = false); void ld2(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2, uint64_t addr); void ld2(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2, int index, uint64_t addr); void ld2r(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2, uint64_t addr); void ld3(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2, LogicVRegister dst3, uint64_t addr); void ld3(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2, LogicVRegister dst3, int index, uint64_t addr); void ld3r(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2, LogicVRegister dst3, uint64_t addr); void ld4(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2, LogicVRegister dst3, LogicVRegister dst4, uint64_t addr); void ld4(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2, LogicVRegister dst3, LogicVRegister dst4, int index, uint64_t addr); void ld4r(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2, LogicVRegister dst3, LogicVRegister dst4, uint64_t addr); void st1(VectorFormat vform, LogicVRegister src, uint64_t addr); void st1(VectorFormat vform, LogicVRegister src, int index, uint64_t addr); void st2(VectorFormat vform, LogicVRegister src, LogicVRegister src2, uint64_t addr); void st2(VectorFormat vform, LogicVRegister src, LogicVRegister src2, int index, uint64_t addr); void st3(VectorFormat vform, LogicVRegister src, LogicVRegister src2, LogicVRegister src3, uint64_t addr); void st3(VectorFormat vform, LogicVRegister src, LogicVRegister src2, LogicVRegister src3, int index, uint64_t addr); void st4(VectorFormat vform, LogicVRegister src, LogicVRegister src2, LogicVRegister src3, LogicVRegister src4, uint64_t addr); void st4(VectorFormat vform, LogicVRegister src, LogicVRegister src2, LogicVRegister src3, LogicVRegister src4, int index, uint64_t addr); LogicVRegister cmp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, Condition cond); LogicVRegister cmp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, int imm, Condition cond); LogicVRegister cmptst(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister add(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); // Add `value` to each lane of `src1`, treating `value` as unsigned for the // purposes of setting the saturation flags. LogicVRegister add_uint(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, uint64_t value); LogicVRegister addp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicPRegister brka(LogicPRegister pd, const LogicPRegister& pg, const LogicPRegister& pn); LogicPRegister brkb(LogicPRegister pd, const LogicPRegister& pg, const LogicPRegister& pn); LogicPRegister brkn(LogicPRegister pdm, const LogicPRegister& pg, const LogicPRegister& pn); LogicPRegister brkpa(LogicPRegister pd, const LogicPRegister& pg, const LogicPRegister& pn, const LogicPRegister& pm); LogicPRegister brkpb(LogicPRegister pd, const LogicPRegister& pg, const LogicPRegister& pn, const LogicPRegister& pm); // dst = srca + src1 * src2 LogicVRegister mla(VectorFormat vform, LogicVRegister dst, const LogicVRegister& srca, const LogicVRegister& src1, const LogicVRegister& src2); // dst = srca - src1 * src2 LogicVRegister mls(VectorFormat vform, LogicVRegister dst, const LogicVRegister& srca, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister mul(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister mul(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister mla(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister mls(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister pmul(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister sdiv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister udiv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); typedef LogicVRegister (Simulator::*ByElementOp)(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister fmul(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister fmla(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister fmlal(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister fmlal2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister fmls(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister fmlsl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister fmlsl2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister fmulx(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister smulh(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister umulh(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister sqdmull(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister sqdmlal(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister sqdmlsl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister sqdmulh(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister sqrdmulh(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister sqrdmlah(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister sqrdmlsh(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister sub(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); // Subtract `value` from each lane of `src1`, treating `value` as unsigned for // the purposes of setting the saturation flags. LogicVRegister sub_uint(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, uint64_t value); LogicVRegister and_(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister orr(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister orn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister eor(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister bic(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister bic(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, uint64_t imm); LogicVRegister bif(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister bit(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister bsl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src_mask, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister cls(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister clz(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister cnot(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister cnt(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister not_(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister rbit(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister rev(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister rev_byte(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int rev_size); LogicVRegister rev16(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister rev32(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister rev64(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister addlp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, bool is_signed, bool do_accumulate); LogicVRegister saddlp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister uaddlp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister sadalp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister uadalp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister ror(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int rotation); LogicVRegister ext(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index); LogicVRegister rotate_elements_right(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int index); template LogicVRegister fcadd(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int rot); LogicVRegister fcadd(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int rot); template LogicVRegister fcmla(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, const LogicVRegister& acc, int index, int rot); LogicVRegister fcmla(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int index, int rot); LogicVRegister fcmla(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, const LogicVRegister& acc, int rot); template LogicVRegister fadda(VectorFormat vform, LogicVRegister acc, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister fadda(VectorFormat vform, LogicVRegister acc, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister cadd(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, int rot, bool saturate = false); LogicVRegister cmla(VectorFormat vform, LogicVRegister dst, const LogicVRegister& srca, const LogicVRegister& src1, const LogicVRegister& src2, int rot); LogicVRegister cmla(VectorFormat vform, LogicVRegister dst, const LogicVRegister& srca, const LogicVRegister& src1, const LogicVRegister& src2, int index, int rot); LogicVRegister bgrp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool do_bext = false); LogicVRegister bdep(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister histogram(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src1, const LogicVRegister& src2, bool do_segmented = false); LogicVRegister index(VectorFormat vform, LogicVRegister dst, uint64_t start, uint64_t step); LogicVRegister ins_element(VectorFormat vform, LogicVRegister dst, int dst_index, const LogicVRegister& src, int src_index); LogicVRegister ins_immediate(VectorFormat vform, LogicVRegister dst, int dst_index, uint64_t imm); LogicVRegister insr(VectorFormat vform, LogicVRegister dst, uint64_t imm); LogicVRegister dup_element(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int src_index); LogicVRegister dup_elements_to_segments(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int src_index); LogicVRegister dup_elements_to_segments( VectorFormat vform, LogicVRegister dst, const std::pair& src_and_index); LogicVRegister dup_immediate(VectorFormat vform, LogicVRegister dst, uint64_t imm); LogicVRegister mov(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicPRegister mov(LogicPRegister dst, const LogicPRegister& src); LogicVRegister mov_merging(VectorFormat vform, LogicVRegister dst, const SimPRegister& pg, const LogicVRegister& src); LogicVRegister mov_zeroing(VectorFormat vform, LogicVRegister dst, const SimPRegister& pg, const LogicVRegister& src); LogicVRegister mov_alternating(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int start_at); LogicPRegister mov_merging(LogicPRegister dst, const LogicPRegister& pg, const LogicPRegister& src); LogicPRegister mov_zeroing(LogicPRegister dst, const LogicPRegister& pg, const LogicPRegister& src); LogicVRegister movi(VectorFormat vform, LogicVRegister dst, uint64_t imm); LogicVRegister mvni(VectorFormat vform, LogicVRegister dst, uint64_t imm); LogicVRegister orr(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, uint64_t imm); LogicVRegister sshl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool shift_is_8bit = true); LogicVRegister ushl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool shift_is_8bit = true); LogicVRegister sshr(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister ushr(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); // Perform a "conditional last" operation. The first part of the pair is true // if any predicate lane is active, false otherwise. The second part takes the // value of the last active (plus offset) lane, or last (plus offset) lane if // none active. std::pair clast(VectorFormat vform, const LogicPRegister& pg, const LogicVRegister& src2, int offset_from_last_active); LogicPRegister match(VectorFormat vform, LogicPRegister dst, const LogicVRegister& haystack, const LogicVRegister& needles, bool negate_match); LogicVRegister compact(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister splice(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister sel(VectorFormat vform, LogicVRegister dst, const SimPRegister& pg, const LogicVRegister& src1, const LogicVRegister& src2); LogicPRegister sel(LogicPRegister dst, const LogicPRegister& pg, const LogicPRegister& src1, const LogicPRegister& src2); LogicVRegister sminmax(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool max); LogicVRegister smax(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister smin(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister sminmaxp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool max); LogicVRegister smaxp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister sminp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister addp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister addv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister uaddlv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister saddlv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister sminmaxv(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src, bool max); LogicVRegister smaxv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister sminv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister uxtl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, bool is_2 = false); LogicVRegister uxtl2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister sxtl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, bool is_2 = false); LogicVRegister sxtl2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister uxt(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, unsigned from_size_in_bits); LogicVRegister sxt(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, unsigned from_size_in_bits); LogicVRegister tbl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& tab, const LogicVRegister& ind); LogicVRegister tbl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& tab, const LogicVRegister& tab2, const LogicVRegister& ind); LogicVRegister tbl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& tab, const LogicVRegister& tab2, const LogicVRegister& tab3, const LogicVRegister& ind); LogicVRegister tbl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& tab, const LogicVRegister& tab2, const LogicVRegister& tab3, const LogicVRegister& tab4, const LogicVRegister& ind); LogicVRegister Table(VectorFormat vform, LogicVRegister dst, const LogicVRegister& ind, bool zero_out_of_bounds, const LogicVRegister* tab1, const LogicVRegister* tab2 = NULL, const LogicVRegister* tab3 = NULL, const LogicVRegister* tab4 = NULL); LogicVRegister tbx(VectorFormat vform, LogicVRegister dst, const LogicVRegister& tab, const LogicVRegister& ind); LogicVRegister tbx(VectorFormat vform, LogicVRegister dst, const LogicVRegister& tab, const LogicVRegister& tab2, const LogicVRegister& ind); LogicVRegister tbx(VectorFormat vform, LogicVRegister dst, const LogicVRegister& tab, const LogicVRegister& tab2, const LogicVRegister& tab3, const LogicVRegister& ind); LogicVRegister tbx(VectorFormat vform, LogicVRegister dst, const LogicVRegister& tab, const LogicVRegister& tab2, const LogicVRegister& tab3, const LogicVRegister& tab4, const LogicVRegister& ind); LogicVRegister uaddl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister uaddl2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister uaddw(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister uaddw2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister saddl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister saddl2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister saddw(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister saddw2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister usubl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister usubl2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister usubw(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister usubw2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister ssubl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister ssubl2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister ssubw(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister ssubw2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister uminmax(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool max); LogicVRegister umax(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister umin(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister uminmaxp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool max); LogicVRegister umaxp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister uminp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister uminmaxv(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src, bool max); LogicVRegister umaxv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister uminv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister trn1(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister trn2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister zip1(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister zip2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister uzp1(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister uzp2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister shl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister scvtf(VectorFormat vform, unsigned dst_data_size_in_bits, unsigned src_data_size_in_bits, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src, FPRounding round, int fbits = 0); LogicVRegister scvtf(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int fbits, FPRounding rounding_mode); LogicVRegister ucvtf(VectorFormat vform, unsigned dst_data_size, unsigned src_data_size, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src, FPRounding round, int fbits = 0); LogicVRegister ucvtf(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int fbits, FPRounding rounding_mode); LogicVRegister sshll(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sshll2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister shll(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister shll2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister ushll(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister ushll2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sli(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sri(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sshr(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister ushr(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister ssra(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister usra(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister srsra(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister ursra(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister suqadd(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister usqadd(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister sqshl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister uqshl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sqshlu(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister abs(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister neg(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister extractnarrow(VectorFormat vform, LogicVRegister dst, bool dst_is_signed, const LogicVRegister& src, bool src_is_signed); LogicVRegister xtn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister sqxtn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister uqxtn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister sqxtun(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister absdiff(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool is_signed); LogicVRegister saba(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister uaba(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister shrn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister shrn2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister rshrn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister rshrn2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister uqshrn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister uqshrn2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister uqrshrn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister uqrshrn2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sqshrn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sqshrn2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sqrshrn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sqrshrn2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sqshrun(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sqshrun2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sqrshrun(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sqrshrun2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, int shift); LogicVRegister sqrdmulh(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool round = true); LogicVRegister dot(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool is_src1_signed, bool is_src2_signed); LogicVRegister sdot(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister udot(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister usdot(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister cdot(VectorFormat vform, LogicVRegister dst, const LogicVRegister& acc, const LogicVRegister& src1, const LogicVRegister& src2, int rot); LogicVRegister sqrdcmlah(VectorFormat vform, LogicVRegister dst, const LogicVRegister& srca, const LogicVRegister& src1, const LogicVRegister& src2, int rot); LogicVRegister sqrdcmlah(VectorFormat vform, LogicVRegister dst, const LogicVRegister& srca, const LogicVRegister& src1, const LogicVRegister& src2, int index, int rot); LogicVRegister sqrdmlash(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool round = true, bool sub_op = false); LogicVRegister sqrdmlash_d(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool round = true, bool sub_op = false); LogicVRegister sqrdmlah(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool round = true); LogicVRegister sqrdmlsh(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool round = true); LogicVRegister sqdmulh(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister matmul(VectorFormat vform_dst, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool src1_signed, bool src2_signed); template LogicVRegister fmatmul(VectorFormat vform, LogicVRegister srcdst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister fmatmul(VectorFormat vform, LogicVRegister srcdst, const LogicVRegister& src1, const LogicVRegister& src2); #define NEON_3VREG_LOGIC_LIST(V) \ V(addhn) \ V(addhn2) \ V(raddhn) \ V(raddhn2) \ V(subhn) \ V(subhn2) \ V(rsubhn) \ V(rsubhn2) \ V(pmull) \ V(pmull2) \ V(sabal) \ V(sabal2) \ V(uabal) \ V(uabal2) \ V(sabdl) \ V(sabdl2) \ V(uabdl) \ V(uabdl2) \ V(smull2) \ V(umull2) \ V(smlal2) \ V(umlal2) \ V(smlsl2) \ V(umlsl2) \ V(sqdmlal2) \ V(sqdmlsl2) \ V(sqdmull2) #define DEFINE_LOGIC_FUNC(FXN) \ LogicVRegister FXN(VectorFormat vform, \ LogicVRegister dst, \ const LogicVRegister& src1, \ const LogicVRegister& src2); NEON_3VREG_LOGIC_LIST(DEFINE_LOGIC_FUNC) #undef DEFINE_LOGIC_FUNC #define NEON_MULL_LIST(V) \ V(smull) \ V(umull) \ V(smlal) \ V(umlal) \ V(smlsl) \ V(umlsl) \ V(sqdmlal) \ V(sqdmlsl) \ V(sqdmull) #define DECLARE_NEON_MULL_OP(FN) \ LogicVRegister FN(VectorFormat vform, \ LogicVRegister dst, \ const LogicVRegister& src1, \ const LogicVRegister& src2, \ bool is_2 = false); NEON_MULL_LIST(DECLARE_NEON_MULL_OP) #undef DECLARE_NEON_MULL_OP #define NEON_FP3SAME_LIST(V) \ V(fadd, FPAdd, false) \ V(fsub, FPSub, true) \ V(fmul, FPMul, true) \ V(fmulx, FPMulx, true) \ V(fdiv, FPDiv, true) \ V(fmax, FPMax, false) \ V(fmin, FPMin, false) \ V(fmaxnm, FPMaxNM, false) \ V(fminnm, FPMinNM, false) #define DECLARE_NEON_FP_VECTOR_OP(FN, OP, PROCNAN) \ template \ LogicVRegister FN(VectorFormat vform, \ LogicVRegister dst, \ const LogicVRegister& src1, \ const LogicVRegister& src2); \ LogicVRegister FN(VectorFormat vform, \ LogicVRegister dst, \ const LogicVRegister& src1, \ const LogicVRegister& src2); NEON_FP3SAME_LIST(DECLARE_NEON_FP_VECTOR_OP) #undef DECLARE_NEON_FP_VECTOR_OP #define NEON_FPPAIRWISE_LIST(V) \ V(faddp, fadd, FPAdd) \ V(fmaxp, fmax, FPMax) \ V(fmaxnmp, fmaxnm, FPMaxNM) \ V(fminp, fmin, FPMin) \ V(fminnmp, fminnm, FPMinNM) #define DECLARE_NEON_FP_PAIR_OP(FNP, FN, OP) \ LogicVRegister FNP(VectorFormat vform, \ LogicVRegister dst, \ const LogicVRegister& src1, \ const LogicVRegister& src2); \ LogicVRegister FNP(VectorFormat vform, \ LogicVRegister dst, \ const LogicVRegister& src); NEON_FPPAIRWISE_LIST(DECLARE_NEON_FP_PAIR_OP) #undef DECLARE_NEON_FP_PAIR_OP enum FrintMode { kFrintToInteger = 0, kFrintToInt32 = 32, kFrintToInt64 = 64 }; template LogicVRegister frecps(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister frecps(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); template LogicVRegister frsqrts(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister frsqrts(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); template LogicVRegister fmla(VectorFormat vform, LogicVRegister dst, const LogicVRegister& srca, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister fmla(VectorFormat vform, LogicVRegister dst, const LogicVRegister& srca, const LogicVRegister& src1, const LogicVRegister& src2); template LogicVRegister fmls(VectorFormat vform, LogicVRegister dst, const LogicVRegister& srca, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister fmls(VectorFormat vform, LogicVRegister dst, const LogicVRegister& srca, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister fnmul(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister fmlal(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister fmlal2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister fmlsl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister fmlsl2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); template LogicVRegister fcmp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, Condition cond); LogicVRegister fcmp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, Condition cond); LogicVRegister fabscmp(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, Condition cond); LogicVRegister fcmp_zero(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, Condition cond); template LogicVRegister fneg(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fneg(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); template LogicVRegister frecpx(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister frecpx(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister ftsmul(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister ftssel(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister ftmad(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, unsigned index); LogicVRegister fexpa(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister flogb(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); template LogicVRegister fscale(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister fscale(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); template LogicVRegister fabs_(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fabs_(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fabd(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2); LogicVRegister frint(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, FPRounding rounding_mode, bool inexact_exception = false, FrintMode frint_mode = kFrintToInteger); LogicVRegister fcvt(VectorFormat dst_vform, VectorFormat src_vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister fcvts(VectorFormat vform, unsigned dst_data_size_in_bits, unsigned src_data_size_in_bits, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src, FPRounding round, int fbits = 0); LogicVRegister fcvts(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, FPRounding rounding_mode, int fbits = 0); LogicVRegister fcvtu(VectorFormat vform, unsigned dst_data_size_in_bits, unsigned src_data_size_in_bits, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src, FPRounding round, int fbits = 0); LogicVRegister fcvtu(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, FPRounding rounding_mode, int fbits = 0); LogicVRegister fcvtl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fcvtl2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fcvtn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fcvtn2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fcvtxn(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fcvtxn2(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fsqrt(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister frsqrte(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister frecpe(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, FPRounding rounding); LogicVRegister ursqrte(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister urecpe(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicPRegister pfalse(LogicPRegister dst); LogicPRegister pfirst(LogicPRegister dst, const LogicPRegister& pg, const LogicPRegister& src); LogicPRegister ptrue(VectorFormat vform, LogicPRegister dst, int pattern); LogicPRegister pnext(VectorFormat vform, LogicPRegister dst, const LogicPRegister& pg, const LogicPRegister& src); LogicVRegister asrd(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, int shift); LogicVRegister andv(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister eorv(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister orv(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister saddv(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister sminv(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister smaxv(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister uaddv(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister uminv(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister umaxv(VectorFormat vform, LogicVRegister dst, const LogicPRegister& pg, const LogicVRegister& src); LogicVRegister interleave_top_bottom(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); template struct TFPPairOp { typedef T (Simulator::*type)(T a, T b); }; template LogicVRegister FPPairedAcrossHelper(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, typename TFPPairOp::type fn, uint64_t inactive_value); LogicVRegister FPPairedAcrossHelper( VectorFormat vform, LogicVRegister dst, const LogicVRegister& src, typename TFPPairOp::type fn16, typename TFPPairOp::type fn32, typename TFPPairOp::type fn64, uint64_t inactive_value); LogicVRegister fminv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fmaxv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fminnmv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister fmaxnmv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister faddv(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); static const uint32_t CRC32_POLY = 0x04C11DB7; static const uint32_t CRC32C_POLY = 0x1EDC6F41; uint32_t Poly32Mod2(unsigned n, uint64_t data, uint32_t poly); template uint32_t Crc32Checksum(uint32_t acc, T val, uint32_t poly); uint32_t Crc32Checksum(uint32_t acc, uint64_t val, uint32_t poly); void SysOp_W(int op, int64_t val); template T FPRecipSqrtEstimate(T op); template T FPRecipEstimate(T op, FPRounding rounding); template R FPToFixed(T op, int fbits, bool is_signed, FPRounding rounding); void FPCompare(double val0, double val1, FPTrapFlags trap); double FPRoundInt(double value, FPRounding round_mode); double FPRoundInt(double value, FPRounding round_mode, FrintMode frint_mode); double FPRoundIntCommon(double value, FPRounding round_mode); double recip_sqrt_estimate(double a); double recip_estimate(double a); double FPRecipSqrtEstimate(double a); double FPRecipEstimate(double a); double FixedToDouble(int64_t src, int fbits, FPRounding round_mode); double UFixedToDouble(uint64_t src, int fbits, FPRounding round_mode); float FixedToFloat(int64_t src, int fbits, FPRounding round_mode); float UFixedToFloat(uint64_t src, int fbits, FPRounding round_mode); ::vixl::internal::SimFloat16 FixedToFloat16(int64_t src, int fbits, FPRounding round_mode); ::vixl::internal::SimFloat16 UFixedToFloat16(uint64_t src, int fbits, FPRounding round_mode); int16_t FPToInt16(double value, FPRounding rmode); int32_t FPToInt32(double value, FPRounding rmode); int64_t FPToInt64(double value, FPRounding rmode); uint16_t FPToUInt16(double value, FPRounding rmode); uint32_t FPToUInt32(double value, FPRounding rmode); uint64_t FPToUInt64(double value, FPRounding rmode); int32_t FPToFixedJS(double value); template T FPAdd(T op1, T op2); template T FPNeg(T op); template T FPDiv(T op1, T op2); template T FPMax(T a, T b); template T FPMaxNM(T a, T b); template T FPMin(T a, T b); template T FPMinNM(T a, T b); template T FPMulNaNs(T op1, T op2); template T FPMul(T op1, T op2); template T FPMulx(T op1, T op2); template T FPMulAdd(T a, T op1, T op2); template T FPSqrt(T op); template T FPSub(T op1, T op2); template T FPRecipStepFused(T op1, T op2); template T FPRSqrtStepFused(T op1, T op2); // This doesn't do anything at the moment. We'll need it if we want support // for cumulative exception bits or floating-point exceptions. void FPProcessException() {} bool FPProcessNaNs(const Instruction* instr); // Pseudo Printf instruction void DoPrintf(const Instruction* instr); // Pseudo-instructions to configure CPU features dynamically. void DoConfigureCPUFeatures(const Instruction* instr); void DoSaveCPUFeatures(const Instruction* instr); void DoRestoreCPUFeatures(const Instruction* instr); // General arithmetic helpers ---------------------------- // Add `delta` to the accumulator (`acc`), optionally saturate, then zero- or // sign-extend. Initial `acc` bits outside `n` are ignored, but the delta must // be a valid int_t. uint64_t IncDecN(uint64_t acc, int64_t delta, unsigned n, bool is_saturating = false, bool is_signed = false); // SVE helpers ------------------------------------------- LogicVRegister SVEBitwiseLogicalUnpredicatedHelper(LogicalOp op, VectorFormat vform, LogicVRegister zd, const LogicVRegister& zn, const LogicVRegister& zm); LogicPRegister SVEPredicateLogicalHelper(SVEPredicateLogicalOp op, LogicPRegister Pd, const LogicPRegister& pn, const LogicPRegister& pm); LogicVRegister SVEBitwiseImmHelper(SVEBitwiseLogicalWithImm_UnpredicatedOp op, VectorFormat vform, LogicVRegister zd, uint64_t imm); enum UnpackType { kHiHalf, kLoHalf }; enum ExtendType { kSignedExtend, kUnsignedExtend }; LogicVRegister unpk(VectorFormat vform, LogicVRegister zd, const LogicVRegister& zn, UnpackType unpack_type, ExtendType extend_type); LogicPRegister SVEIntCompareVectorsHelper(Condition cc, VectorFormat vform, LogicPRegister dst, const LogicPRegister& mask, const LogicVRegister& src1, const LogicVRegister& src2, bool is_wide_elements = false, FlagsUpdate flags = SetFlags); void SVEGatherLoadScalarPlusVectorHelper(const Instruction* instr, VectorFormat vform, SVEOffsetModifier mod); // Store each active zt[lane] to `addr.GetElementAddress(lane, ...)`. // // `zt_code` specifies the code of the first register (zt). Each additional // register (up to `reg_count`) is `(zt_code + i) % 32`. // // This helper calls LogZWrite in the proper way, according to `addr`. void SVEStructuredStoreHelper(VectorFormat vform, const LogicPRegister& pg, unsigned zt_code, const LogicSVEAddressVector& addr); // Load each active zt[lane] from `addr.GetElementAddress(lane, ...)`. void SVEStructuredLoadHelper(VectorFormat vform, const LogicPRegister& pg, unsigned zt_code, const LogicSVEAddressVector& addr, bool is_signed = false); enum SVEFaultTolerantLoadType { // - Elements active in both FFR and pg are accessed as usual. If the access // fails, the corresponding lane and all subsequent lanes are filled with // an unpredictable value, and made inactive in FFR. // // - Elements active in FFR but not pg are set to zero. // // - Elements that are not active in FFR are filled with an unpredictable // value, regardless of pg. kSVENonFaultLoad, // If type == kSVEFirstFaultLoad, the behaviour is the same, except that the // first active element is always accessed, regardless of FFR, and will // generate a real fault if it is inaccessible. If the lane is not active in // FFR, the actual value loaded into the result is still unpredictable. kSVEFirstFaultLoad }; // Load with first-faulting or non-faulting load semantics, respecting and // updating FFR. void SVEFaultTolerantLoadHelper(VectorFormat vform, const LogicPRegister& pg, unsigned zt_code, const LogicSVEAddressVector& addr, SVEFaultTolerantLoadType type, bool is_signed); LogicVRegister SVEBitwiseShiftHelper(Shift shift_op, VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool is_wide_elements); // Pack all even- or odd-numbered elements of source vector side by side and // place in elements of lower half the destination vector, and leave the upper // half all zero. // [...| H | G | F | E | D | C | B | A ] // => [...................| G | E | C | A ] LogicVRegister pack_even_elements(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); // [...| H | G | F | E | D | C | B | A ] // => [...................| H | F | D | B ] LogicVRegister pack_odd_elements(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src); LogicVRegister adcl(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, bool top); template LogicVRegister FTMaddHelper(VectorFormat vform, LogicVRegister dst, const LogicVRegister& src1, const LogicVRegister& src2, uint64_t coeff_pos, uint64_t coeff_neg); // Return the first or last active lane, or -1 if none are active. int GetFirstActive(VectorFormat vform, const LogicPRegister& pg) const; int GetLastActive(VectorFormat vform, const LogicPRegister& pg) const; int CountActiveLanes(VectorFormat vform, const LogicPRegister& pg) const; // Count active and true lanes in `pn`. int CountActiveAndTrueLanes(VectorFormat vform, const LogicPRegister& pg, const LogicPRegister& pn) const; // Count the number of lanes referred to by `pattern`, given the vector // length. If `pattern` is not a recognised SVEPredicateConstraint, this // returns zero. int GetPredicateConstraintLaneCount(VectorFormat vform, int pattern) const; // Simulate a runtime call. void DoRuntimeCall(const Instruction* instr); // Processor state --------------------------------------- // Simulated monitors for exclusive access instructions. SimExclusiveLocalMonitor local_monitor_; SimExclusiveGlobalMonitor global_monitor_; // Output stream. FILE* stream_; PrintDisassembler* print_disasm_; // General purpose registers. Register 31 is the stack pointer. SimRegister registers_[kNumberOfRegisters]; // Vector registers SimVRegister vregisters_[kNumberOfVRegisters]; // SVE predicate registers. SimPRegister pregisters_[kNumberOfPRegisters]; // SVE first-fault register. SimFFRRegister ffr_register_; // A pseudo SVE predicate register with all bits set to true. SimPRegister pregister_all_true_; // Program Status Register. // bits[31, 27]: Condition flags N, Z, C, and V. // (Negative, Zero, Carry, Overflow) SimSystemRegister nzcv_; // Floating-Point Control Register SimSystemRegister fpcr_; // Only a subset of FPCR features are supported by the simulator. This helper // checks that the FPCR settings are supported. // // This is checked when floating-point instructions are executed, not when // FPCR is set. This allows generated code to modify FPCR for external // functions, or to save and restore it when entering and leaving generated // code. void AssertSupportedFPCR() { // No flush-to-zero support. VIXL_ASSERT(ReadFpcr().GetFZ() == 0); // Ties-to-even rounding only. VIXL_ASSERT(ReadFpcr().GetRMode() == FPTieEven); // No alternative half-precision support. VIXL_ASSERT(ReadFpcr().GetAHP() == 0); } static int CalcNFlag(uint64_t result, unsigned reg_size) { return (result >> (reg_size - 1)) & 1; } static int CalcZFlag(uint64_t result) { return (result == 0) ? 1 : 0; } static const uint32_t kConditionFlagsMask = 0xf0000000; Memory memory_; static const size_t kDefaultStackGuardStartSize = 0; static const size_t kDefaultStackGuardEndSize = 4 * 1024; static const size_t kDefaultStackUsableSize = 8 * 1024; Decoder* decoder_; // Indicates if the pc has been modified by the instruction and should not be // automatically incremented. bool pc_modified_; const Instruction* pc_; // Pointer to the last simulated instruction, used for checking the validity // of the current instruction with the previous instruction, such as movprfx. Instruction const* last_instr_; // Branch type register, used for branch target identification. BType btype_; // Next value of branch type register after the current instruction has been // decoded. BType next_btype_; // Global flag for enabling guarded pages. // TODO: implement guarding at page granularity, rather than globally. bool guard_pages_; static const char* xreg_names[]; static const char* wreg_names[]; static const char* breg_names[]; static const char* hreg_names[]; static const char* sreg_names[]; static const char* dreg_names[]; static const char* vreg_names[]; static const char* zreg_names[]; static const char* preg_names[]; private: using FormToVisitorFnMap = std::unordered_map>; static const FormToVisitorFnMap* GetFormToVisitorFnMap(); uint32_t form_hash_; static const PACKey kPACKeyIA; static const PACKey kPACKeyIB; static const PACKey kPACKeyDA; static const PACKey kPACKeyDB; static const PACKey kPACKeyGA; bool CanReadMemory(uintptr_t address, size_t size); // CanReadMemory needs placeholder file descriptors, so we use a pipe. We can // save some system call overhead by opening them on construction, rather than // on every call to CanReadMemory. int placeholder_pipe_fd_[2]; template static T FPDefaultNaN(); // Standard NaN processing. template T FPProcessNaN(T op) { VIXL_ASSERT(IsNaN(op)); if (IsSignallingNaN(op)) { FPProcessException(); } return (ReadDN() == kUseDefaultNaN) ? FPDefaultNaN() : ToQuietNaN(op); } template T FPProcessNaNs(T op1, T op2) { if (IsSignallingNaN(op1)) { return FPProcessNaN(op1); } else if (IsSignallingNaN(op2)) { return FPProcessNaN(op2); } else if (IsNaN(op1)) { VIXL_ASSERT(IsQuietNaN(op1)); return FPProcessNaN(op1); } else if (IsNaN(op2)) { VIXL_ASSERT(IsQuietNaN(op2)); return FPProcessNaN(op2); } else { return 0.0; } } template T FPProcessNaNs3(T op1, T op2, T op3) { if (IsSignallingNaN(op1)) { return FPProcessNaN(op1); } else if (IsSignallingNaN(op2)) { return FPProcessNaN(op2); } else if (IsSignallingNaN(op3)) { return FPProcessNaN(op3); } else if (IsNaN(op1)) { VIXL_ASSERT(IsQuietNaN(op1)); return FPProcessNaN(op1); } else if (IsNaN(op2)) { VIXL_ASSERT(IsQuietNaN(op2)); return FPProcessNaN(op2); } else if (IsNaN(op3)) { VIXL_ASSERT(IsQuietNaN(op3)); return FPProcessNaN(op3); } else { return 0.0; } } // Construct a SimVRegister from a SimPRegister, where each byte-sized lane of // the destination is set to all true (0xff) when the corresponding // predicate flag is set, and false (0x00) otherwise. SimVRegister ExpandToSimVRegister(const SimPRegister& preg); // Set each predicate flag in pd where the corresponding assigned-sized lane // in vreg is non-zero. Clear the flag, otherwise. This is almost the opposite // operation to ExpandToSimVRegister(), except that any non-zero lane is // interpreted as true. void ExtractFromSimVRegister(VectorFormat vform, SimPRegister& pd, // NOLINT(runtime/references) SimVRegister vreg); bool coloured_trace_; // A set of TraceParameters flags. int trace_parameters_; // Indicates whether the exclusive-access warning has been printed. bool print_exclusive_access_warning_; void PrintExclusiveAccessWarning(); CPUFeaturesAuditor cpu_features_auditor_; std::vector saved_cpu_features_; // State for *rand48 functions, used to simulate randomness with repeatable // behaviour (so that tests are deterministic). This is used to simulate RNDR // and RNDRRS, as well as to simulate a source of entropy for architecturally // undefined behaviour. uint16_t rand_state_[3]; // A configurable size of SVE vector registers. unsigned vector_length_; // Representation of memory attribute such as MTE tagging and BTI page // protection. MetaDataDepot meta_data_; }; #if defined(VIXL_HAS_SIMULATED_RUNTIME_CALL_SUPPORT) && __cplusplus < 201402L // Base case of the recursive template used to emulate C++14 // `std::index_sequence`. template struct Simulator::emulated_make_index_sequence_helper<0, I...> : Simulator::emulated_index_sequence {}; #endif } // namespace aarch64 } // namespace vixl #endif // VIXL_INCLUDE_SIMULATOR_AARCH64 #endif // VIXL_AARCH64_SIMULATOR_AARCH64_H_