// 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.
constchar* Simulator::WRegNameForCode(unsigned code, Reg31Mode mode) {
VIXL_ASSERT(code < kNumberOfRegisters); // If the code represents the stack pointer, index the name after zr. if ((code == kZeroRegCode) && (mode == Reg31IsStackPointer)) {
code = kZeroRegCode + 1;
} return wreg_names[code];
}
constchar* Simulator::XRegNameForCode(unsigned code, Reg31Mode mode) {
VIXL_ASSERT(code < kNumberOfRegisters); // If the code represents the stack pointer, index the name after zr. if ((code == kZeroRegCode) && (mode == Reg31IsStackPointer)) {
code = kZeroRegCode + 1;
} return xreg_names[code];
}
left &= reg_mask;
right &= reg_mask;
uint64_t result = (left + right + carry_in) & reg_mask;
if (set_flags) {
nzcv().SetN(CalcNFlag(result, reg_size));
nzcv().SetZ(CalcZFlag(result));
// Compute the C flag by comparing the result to the max unsigned integer.
uint64_t max_uint_2op = max_uint - carry_in; bool C = (left > max_uint_2op) || ((max_uint_2op - left) < right);
nzcv().SetC(C ? 1 : 0);
// Overflow iff the sign bit is the same for the two inputs and different // for the result.
uint64_t left_sign = left & sign_mask;
uint64_t right_sign = right & sign_mask;
uint64_t result_sign = result & sign_mask; bool V = (left_sign == right_sign) && (left_sign != result_sign);
nzcv().SetV(V ? 1 : 0);
LogSystemRegister(NZCV);
} return result;
}
int64_t Simulator::ShiftOperand(unsigned reg_size,
int64_t value,
Shift shift_type, unsigned amount) { if (amount == 0) { return value;
}
int64_t mask = reg_size == kXRegSize ? kXRegMask : kWRegMask; switch (shift_type) { case LSL: return (value << amount) & mask; case LSR: returnstatic_cast<uint64_t>(value) >> amount; case ASR: { // Shift used to restore the sign. unsigned s_shift = kXRegSize - reg_size; // Value with its sign restored.
int64_t s_value = (value << s_shift) >> s_shift; return (s_value >> amount) & mask;
} case ROR: { if (reg_size == kWRegSize) {
value &= kWRegMask;
} return (static_cast<uint64_t>(value) >> amount) |
((value & ((INT64_C(1) << amount) - 1)) <<
(reg_size - amount));
} default:
VIXL_UNIMPLEMENTED(); return 0;
}
}
int64_t Simulator::ExtendValue(unsigned reg_size,
int64_t value,
Extend extend_type, unsigned left_shift) { switch (extend_type) { case UXTB:
value &= kByteMask; break; case UXTH:
value &= kHalfWordMask; break; case UXTW:
value &= kWordMask; break; case SXTB:
value = (value << 56) >> 56; break; case SXTH:
value = (value << 48) >> 48; break; case SXTW:
value = (value << 32) >> 32; break; case UXTX: case SXTX: break; default:
VIXL_UNREACHABLE();
}
int64_t mask = (reg_size == kXRegSize) ? kXRegMask : kWRegMask; return (value << left_shift) & mask;
}
uint32_t format = 0; if (reg_size != lane_size) { switch (reg_size) { default: VIXL_UNREACHABLE(); break; case kQRegSizeInBytes: format = kPrintRegAsQVector; break; case kDRegSizeInBytes: format = kPrintRegAsDVector; break;
}
}
switch (lane_size) { default: VIXL_UNREACHABLE(); break; case kQRegSizeInBytes: format |= kPrintReg1Q; break; case kDRegSizeInBytes: format |= kPrintReg1D; break; case kSRegSizeInBytes: format |= kPrintReg1S; break; case kHRegSizeInBytes: format |= kPrintReg1H; break; case kBRegSizeInBytes: format |= kPrintReg1B; break;
} // These sizes would be duplicate case labels.
VIXL_STATIC_ASSERT(kXRegSizeInBytes == kDRegSizeInBytes);
VIXL_STATIC_ASSERT(kWRegSizeInBytes == kSRegSizeInBytes);
VIXL_STATIC_ASSERT(kPrintXReg == kPrintReg1D);
VIXL_STATIC_ASSERT(kPrintWReg == kPrintReg1S);
returnstatic_cast<PrintRegisterFormat>(format);
}
Simulator::PrintRegisterFormat Simulator::GetPrintRegisterFormat(
VectorFormat vform) { switch (vform) { default: VIXL_UNREACHABLE(); return kPrintReg16B; case kFormat16B: return kPrintReg16B; case kFormat8B: return kPrintReg8B; case kFormat8H: return kPrintReg8H; case kFormat4H: return kPrintReg4H; case kFormat4S: return kPrintReg4S; case kFormat2S: return kPrintReg2S; case kFormat2D: return kPrintReg2D; case kFormat1D: return kPrintReg1D;
}
}
void Simulator::PrintWrittenRegisters() { for (unsigned i = 0; i < kNumberOfRegisters; i++) { if (registers_[i].WrittenSinceLastLog()) PrintRegister(i);
}
}
void Simulator::PrintWrittenVRegisters() { for (unsigned i = 0; i < kNumberOfVRegisters; i++) { // At this point there is no type information, so print as a raw 1Q. if (vregisters_[i].WrittenSinceLastLog()) PrintVRegister(i, kPrintReg1Q);
}
}
void Simulator::PrintRegisters() { for (unsigned i = 0; i < kNumberOfRegisters; i++) {
PrintRegister(i);
}
}
void Simulator::PrintVRegisters() { for (unsigned i = 0; i < kNumberOfVRegisters; i++) { // At this point there is no type information, so print as a raw 1Q.
PrintVRegister(i, kPrintReg1Q);
}
}
// Print a register's name and raw value. // // Only the least-significant `size_in_bytes` bytes of the register are printed, // but the value is aligned as if the whole register had been printed. // // For typical register updates, size_in_bytes should be set to kXRegSizeInBytes // -- the default -- so that the whole register is printed. Other values of // size_in_bytes are intended for use when the register hasn't actually been // updated (such as in PrintWrite). // // No newline is printed. This allows the caller to print more details (such as // a memory access annotation). void Simulator::PrintRegisterRawHelper(unsigned code, Reg31Mode r31mode, int size_in_bytes) { // The template for all supported sizes. // "# x{code}: 0xffeeddccbbaa9988" // "# w{code}: 0xbbaa9988" // "# w{code}<15:0>: 0x9988" // "# w{code}<7:0>: 0x88" unsigned padding_chars = (kXRegSizeInBytes - size_in_bytes) * 2;
constchar * name = ""; constchar * suffix = ""; switch (size_in_bytes) { case kXRegSizeInBytes: name = XRegNameForCode(code, r31mode); break; case kWRegSizeInBytes: name = WRegNameForCode(code, r31mode); break; case 2:
name = WRegNameForCode(code, r31mode);
suffix = "<15:0>";
padding_chars -= strlen(suffix); break; case 1:
name = WRegNameForCode(code, r31mode);
suffix = "<7:0>";
padding_chars -= strlen(suffix); break; default:
VIXL_UNREACHABLE();
}
fprintf(stream_, "# %s%5s%s: ", clr_reg_name, name, suffix);
// Print leading padding spaces.
VIXL_ASSERT(padding_chars < (kXRegSizeInBytes * 2)); for (unsigned i = 0; i < padding_chars; i++) {
putc(' ', stream_);
}
// Print a register's name and raw value. // // The `bytes` and `lsb` arguments can be used to limit the bytes that are // printed. These arguments are intended for use in cases where register hasn't // actually been updated (such as in PrintVWrite). // // No newline is printed. This allows the caller to print more details (such as // a floating-point interpretation or a memory access annotation). void Simulator::PrintVRegisterRawHelper(unsigned code, int bytes, int lsb) { // The template for vector types: // "# v{code}: 0xffeeddccbbaa99887766554433221100". // An example with bytes=4 and lsb=8: // "# v{code}: 0xbbaa9988 ".
fprintf(stream_, "# %s%5s: %s",
clr_vreg_name, VRegNameForCode(code), clr_vreg_value);
int msb = lsb + bytes - 1; int byte = kQRegSizeInBytes - 1;
// Print leading padding spaces. (Two spaces per byte.) while (byte > msb) {
fprintf(stream_, " ");
byte--;
}
// Print the specified part of the value, byte by byte.
qreg_t rawbits = qreg(code);
fprintf(stream_, "0x"); while (byte >= lsb) {
fprintf(stream_, "%02x", rawbits.val[byte]);
byte--;
}
// Print each of the specified lanes of a register as a float or double value. // // The `lane_count` and `lslane` arguments can be used to limit the lanes that // are printed. These arguments are intended for use in cases where register // hasn't actually been updated (such as in PrintVWrite). // // No newline is printed. This allows the caller to print more details (such as // a memory access annotation). void Simulator::PrintVRegisterFPHelper(unsigned code, unsigned lane_size_in_bytes, int lane_count, int rightmost_lane) {
VIXL_ASSERT((lane_size_in_bytes == kSRegSizeInBytes) ||
(lane_size_in_bytes == kDRegSizeInBytes));
int lane_count = 1 << (reg_size_log2 - lane_size_log2); int lane_size = 1 << lane_size_log2;
// The template for vector types: // "# v{code}: 0x{rawbits} (..., {value}, ...)". // The template for scalar types: // "# v{code}: 0x{rawbits} ({reg}:{value})". // The values in parentheses after the bit representations are floating-point // interpretations. They are displayed only if the kPrintVRegAsFP bit is set.
PrintVRegisterRawHelper(code); if (format & kPrintRegAsFP) {
PrintVRegisterFPHelper(code, lane_size, lane_count);
}
fprintf(stream_, "\n");
}
void Simulator::PrintSystemRegister(SystemRegister id) { switch (id) { case NZCV:
fprintf(stream_, "# %sNZCV: %sN:%d Z:%d C:%d V:%d%s\n",
clr_flag_name, clr_flag_value,
nzcv().N(), nzcv().Z(), nzcv().C(), nzcv().V(),
clr_normal); break; case FPCR: { staticconstchar * rmode[] = { "0b00 (Round to Nearest)", "0b01 (Round towards Plus Infinity)", "0b10 (Round towards Minus Infinity)", "0b11 (Round towards Zero)"
};
VIXL_ASSERT(fpcr().RMode() < (sizeof(rmode) / sizeof(rmode[0])));
fprintf(stream_, "# %sFPCR: %sAHP:%d DN:%d FZ:%d RMode:%s%s\n",
clr_flag_name, clr_flag_value,
fpcr().AHP(), fpcr().DN(), fpcr().FZ(), rmode[fpcr().RMode()],
clr_normal); break;
} default:
VIXL_UNREACHABLE();
}
}
// The template is "# v{code}: 0x{value} -> {address}". To keep the trace tidy // and readable, the value is aligned with the values in the register trace.
PrintRegisterRawHelper(reg_code, Reg31IsZeroRegister,
GetPrintRegSizeInBytes(format));
fprintf(stream_, " -> %s0x%016" PRIxPTR "%s\n",
clr_memory_address, address, clr_normal);
}
void Simulator::PrintVWrite(uintptr_t address, unsigned reg_code,
PrintRegisterFormat format, unsigned lane) { // The templates: // "# v{code}: 0x{rawbits} -> {address}" // "# v{code}: 0x{rawbits} (..., {value}, ...) -> {address}". // "# v{code}: 0x{rawbits} ({reg}:{value}) -> {address}" // Because this trace doesn't represent a change to the source register's // value, only the relevant part of the value is printed. To keep the trace // tidy and readable, the raw value is aligned with the other values in the // register trace. int lane_count = GetPrintRegLaneCount(format); int lane_size = GetPrintRegLaneSizeInBytes(format); int reg_size = GetPrintRegSizeInBytes(format);
PrintVRegisterRawHelper(reg_code, reg_size, lane_size * lane); if (format & kPrintRegAsFP) {
PrintVRegisterFPHelper(reg_code, lane_size, lane_count, lane);
}
fprintf(stream_, " -> %s0x%016" PRIxPTR "%s\n",
clr_memory_address, address, clr_normal);
}
// Switch on the logical operation, stripping out the NOT bit, as it has a // different meaning for logical immediate instructions. switch (instr->Mask(LogicalOpMask & ~NOT)) { case ANDS: update_flags = true; VIXL_FALLTHROUGH(); caseAND: result = op1 & op2; break; case ORR: result = op1 | op2; break; case EOR: result = op1 ^ op2; break; default:
VIXL_UNIMPLEMENTED();
}
if (update_flags) {
nzcv().SetN(CalcNFlag(result, reg_size));
nzcv().SetZ(CalcZFlag(result));
nzcv().SetC(0);
nzcv().SetV(0);
LogSystemRegister(NZCV);
}
if (ConditionPassed(instr->Condition())) { // If the condition passes, set the status flags to the result of comparing // the operands. if (instr->Mask(ConditionalCompareMask) == CCMP) {
AddWithCarry(reg_size, true, op1, ~op2, 1);
} else {
VIXL_ASSERT(instr->Mask(ConditionalCompareMask) == CCMN);
AddWithCarry(reg_size, true, op1, op2, 0);
}
} else { // If the condition fails, set the status flags to the nzcv immediate.
nzcv().SetFlags(instr->Nzcv());
LogSystemRegister(NZCV);
}
}
LoadStoreOp op = static_cast<LoadStoreOp>(instr->Mask(LoadStoreMask)); switch (op) { case LDRB_w:
set_wreg(srcdst, Read<uint8_t>(address), NoRegLog); break; case LDRH_w:
set_wreg(srcdst, Read<uint16_t>(address), NoRegLog); break; case LDR_w:
set_wreg(srcdst, Read<uint32_t>(address), NoRegLog); break; case LDR_x:
set_xreg(srcdst, Read<uint64_t>(address), NoRegLog); break; case LDRSB_w:
set_wreg(srcdst, Read<int8_t>(address), NoRegLog); break; case LDRSH_w:
set_wreg(srcdst, Read<int16_t>(address), NoRegLog); break; case LDRSB_x:
set_xreg(srcdst, Read<int8_t>(address), NoRegLog); break; case LDRSH_x:
set_xreg(srcdst, Read<int16_t>(address), NoRegLog); break; case LDRSW_x:
set_xreg(srcdst, Read<int32_t>(address), NoRegLog); break; case LDR_b:
set_breg(srcdst, Read<uint8_t>(address), NoRegLog); break; case LDR_h:
set_hreg(srcdst, Read<uint16_t>(address), NoRegLog); break; case LDR_s:
set_sreg(srcdst, Read<float>(address), NoRegLog); break; case LDR_d:
set_dreg(srcdst, Read<double>(address), NoRegLog); break; case LDR_q:
set_qreg(srcdst, Read<qreg_t>(address), NoRegLog); break;
case STRB_w: Write<uint8_t>(address, wreg(srcdst)); break; case STRH_w: Write<uint16_t>(address, wreg(srcdst)); break; case STR_w: Write<uint32_t>(address, wreg(srcdst)); break; case STR_x: Write<uint64_t>(address, xreg(srcdst)); break; case STR_b: Write<uint8_t>(address, breg(srcdst)); break; case STR_h: Write<uint16_t>(address, hreg(srcdst)); break; case STR_s: Write<float>(address, sreg(srcdst)); break; case STR_d: Write<double>(address, dreg(srcdst)); break; case STR_q: Write<qreg_t>(address, qreg(srcdst)); break;
// Ignore prfm hint instructions. case PRFM: break;
void Simulator::PrintExclusiveAccessWarning() { if (print_exclusive_access_warning_) {
fprintf(
stderr, "%sWARNING:%s VIXL simulator support for load-/store-/clear-exclusive " "instructions is limited. Refer to the README for details.%s\n",
clr_warning, clr_warning_message, clr_normal);
print_exclusive_access_warning_ = false;
}
}
T comparevalue = reg<T>(rs);
T newvalue = reg<T>(rt);
// The architecture permits that the data read clears any exclusive monitors // associated with that location, even if the compare subsequently fails.
local_monitor_.Clear();
T data = Memory::Read<T>(address); if (is_acquire) { // Approximate load-acquire by issuing a full barrier after the load.
__sync_synchronize();
}
if (data == comparevalue) { if (is_release) { // Approximate store-release by issuing a full barrier before the store.
__sync_synchronize();
}
Memory::Write<T>(address, newvalue);
LogWrite(address, rt, GetPrintRegisterFormatForSize(element_size));
}
set_reg<T>(rs, data);
LogRead(address, rs, GetPrintRegisterFormatForSize(element_size));
}
T comparevalue_high = reg<T>(rs + 1);
T comparevalue_low = reg<T>(rs);
T newvalue_high = reg<T>(rt + 1);
T newvalue_low = reg<T>(rt);
// The architecture permits that the data read clears any exclusive monitors // associated with that location, even if the compare subsequently fails.
local_monitor_.Clear();
T data_high = Memory::Read<T>(address);
T data_low = Memory::Read<T>(address2);
if (is_acquire) { // Approximate load-acquire by issuing a full barrier after the load.
__sync_synchronize();
}
bool same =
(data_high == comparevalue_high) && (data_low == comparevalue_low); if (same) { if (is_release) { // Approximate store-release by issuing a full barrier before the store.
__sync_synchronize();
}
void Simulator::VisitLoadStoreExclusive(const Instruction* instr) {
LoadStoreExclusive op = static_cast<LoadStoreExclusive>(instr->Mask(LoadStoreExclusiveMask));
switch (op) { case CAS_w: case CASA_w: case CASL_w: case CASAL_w:
CompareAndSwapHelper<uint32_t>(instr); break; case CAS_x: case CASA_x: case CASL_x: case CASAL_x:
CompareAndSwapHelper<uint64_t>(instr); break; case CASB: case CASAB: case CASLB: case CASALB:
CompareAndSwapHelper<uint8_t>(instr); break; case CASH: case CASAH: case CASLH: case CASALH:
CompareAndSwapHelper<uint16_t>(instr); break; case CASP_w: case CASPA_w: case CASPL_w: case CASPAL_w:
CompareAndSwapPairHelper<uint32_t>(instr); break; case CASP_x: case CASPA_x: case CASPL_x: case CASPAL_x:
CompareAndSwapPairHelper<uint64_t>(instr); break; default:
PrintExclusiveAccessWarning();
// Verify that the address is available to the host.
VIXL_ASSERT(address == static_cast<uintptr_t>(address));
// Check the alignment of `address`. if (AlignDown(address, access_size) != address) {
VIXL_ALIGNMENT_EXCEPTION();
}
// The sp must be aligned to 16 bytes when it is accessed. if ((rn == 31) && (AlignDown(address, 16) != address)) {
VIXL_ALIGNMENT_EXCEPTION();
}
if (is_load) { if (is_exclusive) {
local_monitor_.MarkExclusive(address, access_size);
} else { // Any non-exclusive load can clear the local monitor as a side // effect. We don't need to do this, but it is useful to stress the // simulated code.
local_monitor_.Clear();
}
// Use NoRegLog to suppress the register trace (LOG_REGS, LOG_FP_REGS). // We will print a more detailed log. switch (op) { case LDXRB_w: case LDAXRB_w: case LDARB_w:
set_wreg(rt, Read<uint8_t>(address), NoRegLog); break; case LDXRH_w: case LDAXRH_w: case LDARH_w:
set_wreg(rt, Read<uint16_t>(address), NoRegLog); break; case LDXR_w: case LDAXR_w: case LDAR_w:
set_wreg(rt, Read<uint32_t>(address), NoRegLog); break; case LDXR_x: case LDAXR_x: case LDAR_x:
set_xreg(rt, Read<uint64_t>(address), NoRegLog); break; case LDXP_w: case LDAXP_w:
set_wreg(rt, Read<uint32_t>(address), NoRegLog);
set_wreg(rt2, Read<uint32_t>(address + element_size), NoRegLog); break; case LDXP_x: case LDAXP_x:
set_xreg(rt, Read<uint64_t>(address), NoRegLog);
set_xreg(rt2, Read<uint64_t>(address + element_size), NoRegLog); break; default:
VIXL_UNREACHABLE();
}
if (is_acquire_release) { // Approximate load-acquire by issuing a full barrier after the load.
js::jit::AtomicOperations::fenceSeqCst();
}
LogRead(address, rt, GetPrintRegisterFormatForSize(element_size)); if (is_pair) {
LogRead(address + element_size, rt2,
GetPrintRegisterFormatForSize(element_size));
}
} else { if (is_acquire_release) { // Approximate store-release by issuing a full barrier before the // store.
js::jit::AtomicOperations::fenceSeqCst();
}
// - All exclusive stores explicitly clear the local monitor.
local_monitor_.Clear();
} else { // - Any other store can clear the local monitor as a side effect.
local_monitor_.MaybeClear();
}
if (do_store) { switch (op) { case STXRB_w: case STLXRB_w: case STLRB_w:
Write<uint8_t>(address, wreg(rt)); break; case STXRH_w: case STLXRH_w: case STLRH_w:
Write<uint16_t>(address, wreg(rt)); break; case STXR_w: case STLXR_w: case STLR_w:
Write<uint32_t>(address, wreg(rt)); break; case STXR_x: case STLXR_x: case STLR_x:
Write<uint64_t>(address, xreg(rt)); break; case STXP_w: case STLXP_w:
Write<uint32_t>(address, wreg(rt));
Write<uint32_t>(address + element_size, wreg(rt2)); break; case STXP_x: case STLXP_x:
Write<uint64_t>(address, xreg(rt));
Write<uint64_t>(address + element_size, xreg(rt2)); break; default:
VIXL_UNREACHABLE();
}
// Verify that the address is available to the host.
VIXL_ASSERT(address == static_cast<uintptr_t>(address));
address = Memory::AddressUntag(address); if (handle_wasm_seg_fault(address, sizeof(T))) return;
T value = reg<T>(rs);
T data = Memory::Read<T>(address);
if (is_acquire) { // Approximate load-acquire by issuing a full barrier after the load.
__sync_synchronize();
}
T result = 0; switch (instr->Mask(AtomicMemorySimpleOpMask)) { case LDADDOp:
result = data + value; break; case LDCLROp:
VIXL_ASSERT(!std::numeric_limits<T>::is_signed);
result = data & ~value; break; case LDEOROp:
VIXL_ASSERT(!std::numeric_limits<T>::is_signed);
result = data ^ value; break; case LDSETOp:
VIXL_ASSERT(!std::numeric_limits<T>::is_signed);
result = data | value; break;
// Signed/Unsigned difference is done via the templated type T. case LDSMAXOp: case LDUMAXOp:
result = (data > value) ? data : value; break; case LDSMINOp: case LDUMINOp:
result = (data > value) ? value : data; break;
}
if (is_release) { // Approximate store-release by issuing a full barrier before the store.
__sync_synchronize();
}
// Verify that the address is available to the host.
VIXL_ASSERT(address == static_cast<uintptr_t>(address));
address = Memory::AddressUntag(address); if (handle_wasm_seg_fault(address, sizeof(T))) return;
T data = Memory::Read<T>(address); if (is_acquire) { // Approximate load-acquire by issuing a full barrier after the load.
__sync_synchronize();
}
if (is_release) { // Approximate store-release by issuing a full barrier before the store.
__sync_synchronize();
}
Memory::Write<T>(address, reg<T>(rs));
void Simulator::VisitAtomicMemory(const Instruction* instr) { switch (instr->Mask(AtomicMemoryMask)) { // clang-format off #define SIM_FUNC_B(A) \ case A##B: \ case A##AB: \ case A##LB: \ case A##ALB: #define SIM_FUNC_H(A) \ case A##H: \ case A##AH: \ case A##LH: \ case A##ALH: #define SIM_FUNC_w(A) \ case A##_w: \ case A##A_w: \ case A##L_w: \ case A##AL_w: #define SIM_FUNC_x(A) \ case A##_x: \ case A##A_x: \ case A##L_x: \ case A##AL_x:
case SWPB: case SWPAB: case SWPLB: case SWPALB:
AtomicMemorySwapHelper<uint8_t>(instr); break; case SWPH: case SWPAH: case SWPLH: case SWPALH:
AtomicMemorySwapHelper<uint16_t>(instr); break; case SWP_w: case SWPA_w: case SWPL_w: case SWPAL_w:
AtomicMemorySwapHelper<uint32_t>(instr); break; case SWP_x: case SWPA_x: case SWPL_x: case SWPAL_x:
AtomicMemorySwapHelper<uint64_t>(instr); break; case LDAPRB:
LoadAcquireRCpcHelper<uint8_t>(instr); break; case LDAPRH:
LoadAcquireRCpcHelper<uint16_t>(instr); break; case LDAPR_w:
LoadAcquireRCpcHelper<uint32_t>(instr); break; case LDAPR_x:
LoadAcquireRCpcHelper<uint64_t>(instr); break;
}
}
if ((addr_reg == 31) && ((address % 16) != 0)) { // When the base register is SP the stack pointer is required to be // quadword aligned prior to the address calculation and write-backs. // Misalignment will cause a stack alignment fault.
VIXL_ALIGNMENT_EXCEPTION();
}
if ((addrmode == PreIndex) || (addrmode == PostIndex)) {
VIXL_ASSERT(offset != 0); // Only preindex should log the register update here. For Postindex, the // update will be printed automatically by LogWrittenRegisters _after_ the // memory access itself is logged.
RegLogMode log_mode = (addrmode == PreIndex) ? LogRegWrites : NoRegLog;
set_xreg(addr_reg, address + offset, log_mode, Reg31IsStackPointer);
}
switch (instr->Mask(DataProcessing2SourceMask)) { case SDIV_w: {
int32_t rn = wreg(instr->Rn());
int32_t rm = wreg(instr->Rm()); if ((rn == kWMinInt) && (rm == -1)) {
result = kWMinInt;
} elseif (rm == 0) { // Division by zero can be trapped, but not on A-class processors.
result = 0;
} else {
result = rn / rm;
} break;
} case SDIV_x: {
int64_t rn = xreg(instr->Rn());
int64_t rm = xreg(instr->Rm()); if ((rn == kXMinInt) && (rm == -1)) {
result = kXMinInt;
} elseif (rm == 0) { // Division by zero can be trapped, but not on A-class processors.
result = 0;
} else {
result = rn / rm;
} break;
} case UDIV_w: {
uint32_t rn = static_cast<uint32_t>(wreg(instr->Rn()));
uint32_t rm = static_cast<uint32_t>(wreg(instr->Rm())); if (rm == 0) { // Division by zero can be trapped, but not on A-class processors.
result = 0;
} else {
result = rn / rm;
} break;
} case UDIV_x: {
uint64_t rn = static_cast<uint64_t>(xreg(instr->Rn()));
uint64_t rm = static_cast<uint64_t>(xreg(instr->Rm())); if (rm == 0) { // Division by zero can be trapped, but not on A-class processors.
result = 0;
} else {
result = rn / rm;
} break;
} case LSLV_w: case LSLV_x: shift_op = LSL; break; case LSRV_w: case LSRV_x: shift_op = LSR; break; case ASRV_w: case ASRV_x: shift_op = ASR; break; case RORV_w: case RORV_x: shift_op = ROR; break; case CRC32B: {
uint32_t acc = reg<uint32_t>(instr->Rn());
uint8_t val = reg<uint8_t>(instr->Rm());
result = Crc32Checksum(acc, val, CRC32_POLY); break;
} case CRC32H: {
uint32_t acc = reg<uint32_t>(instr->Rn());
uint16_t val = reg<uint16_t>(instr->Rm());
result = Crc32Checksum(acc, val, CRC32_POLY); break;
} case CRC32W: {
uint32_t acc = reg<uint32_t>(instr->Rn());
uint32_t val = reg<uint32_t>(instr->Rm());
result = Crc32Checksum(acc, val, CRC32_POLY); break;
} case CRC32X: {
uint32_t acc = reg<uint32_t>(instr->Rn());
uint64_t val = reg<uint64_t>(instr->Rm());
result = Crc32Checksum(acc, val, CRC32_POLY);
reg_size = kWRegSize; break;
} case CRC32CB: {
uint32_t acc = reg<uint32_t>(instr->Rn());
uint8_t val = reg<uint8_t>(instr->Rm());
result = Crc32Checksum(acc, val, CRC32C_POLY); break;
} case CRC32CH: {
uint32_t acc = reg<uint32_t>(instr->Rn());
uint16_t val = reg<uint16_t>(instr->Rm());
result = Crc32Checksum(acc, val, CRC32C_POLY); break;
} case CRC32CW: {
uint32_t acc = reg<uint32_t>(instr->Rn());
uint32_t val = reg<uint32_t>(instr->Rm());
result = Crc32Checksum(acc, val, CRC32C_POLY); break;
} case CRC32CX: {
uint32_t acc = reg<uint32_t>(instr->Rn());
uint64_t val = reg<uint64_t>(instr->Rm());
result = Crc32Checksum(acc, val, CRC32C_POLY);
reg_size = kWRegSize; break;
} default: VIXL_UNIMPLEMENTED();
}
if (shift_op != NO_SHIFT) { // Shift distance encoded in the least-significant five/six bits of the // register. int mask = (instr->SixtyFourBits() == 1) ? 0x3f : 0x1f; unsigned shift = wreg(instr->Rm()) & mask;
result = ShiftOperand(reg_size, reg(reg_size, instr->Rn()), shift_op,
shift);
}
set_reg(reg_size, instr->Rd(), result);
}
// The algorithm used is adapted from the one described in section 8.2 of // Hacker's Delight, by Henry S. Warren, Jr. // It assumes that a right shift on a signed integer is an arithmetic shift. // Type T must be either uint64_t or int64_t. template <typename T> static T MultiplyHigh(T u, T v) {
uint64_t u0, v0, w0;
T u1, v1, w1, w2, t;
VIXL_ASSERT(sizeof(u) == sizeof(u0));
u0 = u & 0xffffffff;
u1 = u >> 32;
v0 = v & 0xffffffff;
v1 = v >> 32;
// inzero indicates if the extracted bitfield is inserted into the // destination register value or in zero. // If extend is true, extend the sign of the extracted bitfield. bool inzero = false; bool extend = false; switch (instr->Mask(BitfieldMask)) { case BFM_x: case BFM_w: break; case SBFM_x: case SBFM_w:
inzero = true;
extend = true; break; case UBFM_x: case UBFM_w:
inzero = true; break; default:
VIXL_UNIMPLEMENTED();
}
switch (instr->Mask(FPIntegerConvertMask)) { case FCVTAS_ws: set_wreg(dst, FPToInt32(sreg(src), FPTieAway)); break; case FCVTAS_xs: set_xreg(dst, FPToInt64(sreg(src), FPTieAway)); break; case FCVTAS_wd: set_wreg(dst, FPToInt32(dreg(src), FPTieAway)); break; case FCVTAS_xd: set_xreg(dst, FPToInt64(dreg(src), FPTieAway)); break; case FCVTAU_ws: set_wreg(dst, FPToUInt32(sreg(src), FPTieAway)); break; case FCVTAU_xs: set_xreg(dst, FPToUInt64(sreg(src), FPTieAway)); break; case FCVTAU_wd: set_wreg(dst, FPToUInt32(dreg(src), FPTieAway)); break; case FCVTAU_xd: set_xreg(dst, FPToUInt64(dreg(src), FPTieAway)); break; case FCVTMS_ws:
set_wreg(dst, FPToInt32(sreg(src), FPNegativeInfinity)); break; case FCVTMS_xs:
set_xreg(dst, FPToInt64(sreg(src), FPNegativeInfinity)); break; case FCVTMS_wd:
set_wreg(dst, FPToInt32(dreg(src), FPNegativeInfinity)); break; case FCVTMS_xd:
set_xreg(dst, FPToInt64(dreg(src), FPNegativeInfinity)); break; case FCVTMU_ws:
set_wreg(dst, FPToUInt32(sreg(src), FPNegativeInfinity)); break; case FCVTMU_xs:
set_xreg(dst, FPToUInt64(sreg(src), FPNegativeInfinity)); break; case FCVTMU_wd:
set_wreg(dst, FPToUInt32(dreg(src), FPNegativeInfinity)); break; case FCVTMU_xd:
set_xreg(dst, FPToUInt64(dreg(src), FPNegativeInfinity)); break; case FCVTPS_ws:
set_wreg(dst, FPToInt32(sreg(src), FPPositiveInfinity)); break; case FCVTPS_xs:
set_xreg(dst, FPToInt64(sreg(src), FPPositiveInfinity)); break; case FCVTPS_wd:
set_wreg(dst, FPToInt32(dreg(src), FPPositiveInfinity)); break; case FCVTPS_xd:
set_xreg(dst, FPToInt64(dreg(src), FPPositiveInfinity)); break; case FCVTPU_ws:
set_wreg(dst, FPToUInt32(sreg(src), FPPositiveInfinity)); break; case FCVTPU_xs:
set_xreg(dst, FPToUInt64(sreg(src), FPPositiveInfinity)); break; case FCVTPU_wd:
set_wreg(dst, FPToUInt32(dreg(src), FPPositiveInfinity)); break; case FCVTPU_xd:
set_xreg(dst, FPToUInt64(dreg(src), FPPositiveInfinity)); break; case FCVTNS_ws: set_wreg(dst, FPToInt32(sreg(src), FPTieEven)); break; case FCVTNS_xs: set_xreg(dst, FPToInt64(sreg(src), FPTieEven)); break; case FCVTNS_wd: set_wreg(dst, FPToInt32(dreg(src), FPTieEven)); break; case FCVTNS_xd: set_xreg(dst, FPToInt64(dreg(src), FPTieEven)); break; case FCVTNU_ws: set_wreg(dst, FPToUInt32(sreg(src), FPTieEven)); break; case FCVTNU_xs: set_xreg(dst, FPToUInt64(sreg(src), FPTieEven)); break; case FCVTNU_wd: set_wreg(dst, FPToUInt32(dreg(src), FPTieEven)); break; case FCVTNU_xd: set_xreg(dst, FPToUInt64(dreg(src), FPTieEven)); break; case FCVTZS_ws: set_wreg(dst, FPToInt32(sreg(src), FPZero)); break; case FCVTZS_xs: set_xreg(dst, FPToInt64(sreg(src), FPZero)); break; case FCVTZS_wd: set_wreg(dst, FPToInt32(dreg(src), FPZero)); break; case FCVTZS_xd: set_xreg(dst, FPToInt64(dreg(src), FPZero)); break; case FCVTZU_ws: set_wreg(dst, FPToUInt32(sreg(src), FPZero)); break; case FCVTZU_xs: set_xreg(dst, FPToUInt64(sreg(src), FPZero)); break; case FCVTZU_wd: set_wreg(dst, FPToUInt32(dreg(src), FPZero)); break; case FCVTZU_xd: set_xreg(dst, FPToUInt64(dreg(src), FPZero)); break; case FJCVTZS: set_wreg(dst, FPToFixedJS(dreg(src))); break; case FMOV_ws: set_wreg(dst, sreg_bits(src)); break; case FMOV_xd: set_xreg(dst, dreg_bits(src)); break; case FMOV_sw: set_sreg_bits(dst, wreg(src)); break; case FMOV_dx: set_dreg_bits(dst, xreg(src)); break; case FMOV_d1_x:
LogicVRegister(vreg(dst)).SetUint(kFormatD, 1, xreg(src)); break; case FMOV_x_d1:
set_xreg(dst, LogicVRegister(vreg(src)).Uint(kFormatD, 1)); break;
// A 32-bit input can be handled in the same way as a 64-bit input, since // the sign- or zero-extension will not affect the conversion. case SCVTF_dx: set_dreg(dst, FixedToDouble(xreg(src), 0, round)); break; case SCVTF_dw: set_dreg(dst, FixedToDouble(wreg(src), 0, round)); break; case UCVTF_dx: set_dreg(dst, UFixedToDouble(xreg(src), 0, round)); break; case UCVTF_dw: {
set_dreg(dst, UFixedToDouble(static_cast<uint32_t>(wreg(src)), 0, round)); break;
} case SCVTF_sx: set_sreg(dst, FixedToFloat(xreg(src), 0, round)); break; case SCVTF_sw: set_sreg(dst, FixedToFloat(wreg(src), 0, round)); break; case UCVTF_sx: set_sreg(dst, UFixedToFloat(xreg(src), 0, round)); break; case UCVTF_sw: {
set_sreg(dst, UFixedToFloat(static_cast<uint32_t>(wreg(src)), 0, round)); break;
}
switch (instr->Mask(FPDataProcessing1SourceMask)) { case FMOV_s: set_sreg(fd, sreg(fn)); return; case FMOV_d: set_dreg(fd, dreg(fn)); return; case FABS_s: fabs_(kFormatS, vreg(fd), vreg(fn)); return; case FABS_d: fabs_(kFormatD, vreg(fd), vreg(fn)); return; case FNEG_s: fneg(kFormatS, vreg(fd), vreg(fn)); return; case FNEG_d: fneg(kFormatD, vreg(fd), vreg(fn)); return; case FCVT_ds:
set_dreg(fd, FPToDouble(sreg(fn), ReadDN())); return; case FCVT_sd:
set_sreg(fd, FPToFloat(dreg(fn), FPTieEven, ReadDN())); return; case FCVT_hs:
set_hreg(fd, Float16ToRawbits(FPToFloat16(sreg(fn), FPTieEven, ReadDN()))); return; case FCVT_sh:
set_sreg(fd, FPToFloat(RawbitsToFloat16(hreg(fn)), ReadDN())); return; case FCVT_dh:
set_dreg(fd, FPToDouble(RawbitsToFloat16(hreg(fn)), ReadDN())); return; case FCVT_hd:
set_hreg(fd, Float16ToRawbits(FPToFloat16(dreg(fn), FPTieEven, ReadDN()))); return; case FSQRT_s: case FSQRT_d: fsqrt(vform, rd, rn); return; case FRINTI_s: case FRINTI_d: break; // Use FPCR rounding mode. case FRINTX_s: case FRINTX_d: inexact_exception = true; break; case FRINTA_s: case FRINTA_d: fpcr_rounding = FPTieAway; break; case FRINTM_s: case FRINTM_d: fpcr_rounding = FPNegativeInfinity; break; case FRINTN_s: case FRINTN_d: fpcr_rounding = FPTieEven; break; case FRINTP_s: case FRINTP_d: fpcr_rounding = FPPositiveInfinity; break; case FRINTZ_s: case FRINTZ_d: fpcr_rounding = FPZero; break; default: VIXL_UNIMPLEMENTED();
}
// Only FRINT* instructions fall through the switch above.
frint(vform, rd, rn, fpcr_rounding, inexact_exception);
}
switch (instr->Mask(FPDataProcessing2SourceMask)) { case FADD_s: case FADD_d: fadd(vform, rd, rn, rm); break; case FSUB_s: case FSUB_d: fsub(vform, rd, rn, rm); break; case FMUL_s: case FMUL_d: fmul(vform, rd, rn, rm); break; case FNMUL_s: case FNMUL_d: fnmul(vform, rd, rn, rm); break; case FDIV_s: case FDIV_d: fdiv(vform, rd, rn, rm); break; case FMAX_s: case FMAX_d: fmax(vform, rd, rn, rm); break; case FMIN_s: case FMIN_d: fmin(vform, rd, rn, rm); break; case FMAXNM_s: case FMAXNM_d: fmaxnm(vform, rd, rn, rm); break; case FMINNM_s: case FMINNM_d: fminnm(vform, rd, rn, rm); break; default:
VIXL_UNREACHABLE();
}
}
if (instr->Mask(FP64) == FP64) { double result = FPProcessNaNs(dreg(fn), dreg(fm)); if (std::isnan(result)) {
set_dreg(fd, result);
done = true;
}
} else { float result = FPProcessNaNs(sreg(fn), sreg(fm)); if (std::isnan(result)) {
set_sreg(fd, result);
done = true;
}
}
return done;
}
void Simulator::SysOp_W(int op, int64_t val) { switch (op) { case IVAU: case CVAC: case CVAU: case CIVAC: { // Perform a dummy memory access to ensure that we have read access // to the specified address. volatile uint8_t y = Read<uint8_t>(val);
USE(y); // TODO: Implement "case ZVA:". break;
} default:
VIXL_UNIMPLEMENTED();
}
}
void Simulator::VisitSystem(const Instruction* instr) { // Some system instructions hijack their Op and Cp fields to represent a // range of immediates instead of indicating a different instruction. This // makes the decoding tricky. if (instr->Mask(SystemExclusiveMonitorFMask) == SystemExclusiveMonitorFixed) {
VIXL_ASSERT(instr->Mask(SystemExclusiveMonitorMask) == CLREX); switch (instr->Mask(SystemExclusiveMonitorMask)) { case CLREX: {
PrintExclusiveAccessWarning();
ClearLocalMonitor(); break;
}
}
} elseif (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { switch (instr->Mask(SystemSysRegMask)) { case MRS: { switch (instr->ImmSystemRegister()) { case NZCV: set_xreg(instr->Rt(), nzcv().RawValue()); break; case FPCR: set_xreg(instr->Rt(), fpcr().RawValue()); break; default: VIXL_UNIMPLEMENTED();
} break;
} case MSR: { switch (instr->ImmSystemRegister()) { case NZCV:
nzcv().SetRawValue(wreg(instr->Rt()));
LogSystemRegister(NZCV); break; case FPCR:
fpcr().SetRawValue(wreg(instr->Rt()));
LogSystemRegister(FPCR); break; default: VIXL_UNIMPLEMENTED();
} break;
}
}
} elseif (instr->Mask(SystemHintFMask) == SystemHintFixed) {
VIXL_ASSERT(instr->Mask(SystemHintMask) == HINT); switch (instr->ImmHint()) { case NOP: break; case CSDB: break; default: VIXL_UNIMPLEMENTED();
}
} elseif (instr->Mask(MemBarrierFMask) == MemBarrierFixed) {
js::jit::AtomicOperations::fenceSeqCst();
} elseif ((instr->Mask(SystemSysFMask) == SystemSysFixed)) { switch (instr->Mask(SystemSysMask)) { case SYS: SysOp_W(instr->SysOp(), xreg(instr->Rt())); break; default: VIXL_UNIMPLEMENTED();
}
} else {
VIXL_UNIMPLEMENTED();
}
}
if (instr->Mask(NEON2RegMiscOpcode) <= NEON_NEG_opcode) { // These instructions all use a two bit size field, except NOT and RBIT, // which use the field to encode the operation. switch (instr->Mask(NEON2RegMiscMask)) { case NEON_REV64: rev64(vf, rd, rn); break; case NEON_REV32: rev32(vf, rd, rn); break; case NEON_REV16: rev16(vf, rd, rn); break; case NEON_SUQADD: suqadd(vf, rd, rn); break; case NEON_USQADD: usqadd(vf, rd, rn); break; case NEON_CLS: cls(vf, rd, rn); break; case NEON_CLZ: clz(vf, rd, rn); break; case NEON_CNT: cnt(vf, rd, rn); break; case NEON_SQABS: abs(vf, rd, rn).SignedSaturate(vf); break; case NEON_SQNEG: neg(vf, rd, rn).SignedSaturate(vf); break; case NEON_CMGT_zero: cmp(vf, rd, rn, 0, gt); break; case NEON_CMGE_zero: cmp(vf, rd, rn, 0, ge); break; case NEON_CMEQ_zero: cmp(vf, rd, rn, 0, eq); break; case NEON_CMLE_zero: cmp(vf, rd, rn, 0, le); break; case NEON_CMLT_zero: cmp(vf, rd, rn, 0, lt); break; case NEON_ABS: abs(vf, rd, rn); break; case NEON_NEG: neg(vf, rd, rn); break; case NEON_SADDLP: saddlp(vf_lp, rd, rn); break; case NEON_UADDLP: uaddlp(vf_lp, rd, rn); break; case NEON_SADALP: sadalp(vf_lp, rd, rn); break; case NEON_UADALP: uadalp(vf_lp, rd, rn); break; case NEON_RBIT_NOT:
vf = nfd.GetVectorFormat(nfd.LogicalFormatMap()); switch (instr->FPType()) { case 0: not_(vf, rd, rn); break; case 1: rbit(vf, rd, rn);; break; default:
VIXL_UNIMPLEMENTED();
} break;
}
} else {
VectorFormat fpf = nfd.GetVectorFormat(nfd.FPFormatMap());
FPRounding fpcr_rounding = static_cast<FPRounding>(fpcr().RMode()); bool inexact_exception = false;
// These instructions all use a one bit size field, except XTN, SQXTUN, // SHLL, SQXTN and UQXTN, which use a two bit size field. switch (instr->Mask(NEON2RegMiscFPMask)) { case NEON_FABS: fabs_(fpf, rd, rn); return; case NEON_FNEG: fneg(fpf, rd, rn); return; case NEON_FSQRT: fsqrt(fpf, rd, rn); return; case NEON_FCVTL: if (instr->Mask(NEON_Q)) {
fcvtl2(vf_fcvtl, rd, rn);
} else {
fcvtl(vf_fcvtl, rd, rn);
} return; case NEON_FCVTN: if (instr->Mask(NEON_Q)) {
fcvtn2(vf_fcvtn, rd, rn);
} else {
fcvtn(vf_fcvtn, rd, rn);
} return; case NEON_FCVTXN: if (instr->Mask(NEON_Q)) {
fcvtxn2(vf_fcvtn, rd, rn);
} else {
fcvtxn(vf_fcvtn, rd, rn);
} return;
// The following instructions break from the switch statement, rather // than return. case NEON_FRINTI: break; // Use FPCR rounding mode. case NEON_FRINTX: inexact_exception = true; break; case NEON_FRINTA: fpcr_rounding = FPTieAway; break; case NEON_FRINTM: fpcr_rounding = FPNegativeInfinity; break; case NEON_FRINTN: fpcr_rounding = FPTieEven; break; case NEON_FRINTP: fpcr_rounding = FPPositiveInfinity; break; case NEON_FRINTZ: fpcr_rounding = FPZero; break;
case NEON_FCVTNS: fcvts(fpf, rd, rn, FPTieEven); return; case NEON_FCVTNU: fcvtu(fpf, rd, rn, FPTieEven); return; case NEON_FCVTPS: fcvts(fpf, rd, rn, FPPositiveInfinity); return; case NEON_FCVTPU: fcvtu(fpf, rd, rn, FPPositiveInfinity); return; case NEON_FCVTMS: fcvts(fpf, rd, rn, FPNegativeInfinity); return; case NEON_FCVTMU: fcvtu(fpf, rd, rn, FPNegativeInfinity); return; case NEON_FCVTZS: fcvts(fpf, rd, rn, FPZero); return; case NEON_FCVTZU: fcvtu(fpf, rd, rn, FPZero); return; case NEON_FCVTAS: fcvts(fpf, rd, rn, FPTieAway); return; case NEON_FCVTAU: fcvtu(fpf, rd, rn, FPTieAway); return; case NEON_SCVTF: scvtf(fpf, rd, rn, 0, fpcr_rounding); return; case NEON_UCVTF: ucvtf(fpf, rd, rn, 0, fpcr_rounding); return; case NEON_URSQRTE: ursqrte(fpf, rd, rn); return; case NEON_URECPE: urecpe(fpf, rd, rn); return; case NEON_FRSQRTE: frsqrte(fpf, rd, rn); return; case NEON_FRECPE: frecpe(fpf, rd, rn, fpcr_rounding); return; case NEON_FCMGT_zero: fcmp_zero(fpf, rd, rn, gt); return; case NEON_FCMGE_zero: fcmp_zero(fpf, rd, rn, ge); return; case NEON_FCMEQ_zero: fcmp_zero(fpf, rd, rn, eq); return; case NEON_FCMLE_zero: fcmp_zero(fpf, rd, rn, le); return; case NEON_FCMLT_zero: fcmp_zero(fpf, rd, rn, lt); return; default: if ((NEON_XTN_opcode <= instr->Mask(NEON2RegMiscOpcode)) &&
(instr->Mask(NEON2RegMiscOpcode) <= NEON_UQXTN_opcode)) { switch (instr->Mask(NEON2RegMiscMask)) { case NEON_XTN: xtn(vf, rd, rn); return; case NEON_SQXTN: sqxtn(vf, rd, rn); return; case NEON_UQXTN: uqxtn(vf, rd, rn); return; case NEON_SQXTUN: sqxtun(vf, rd, rn); return; case NEON_SHLL:
vf = nfd.GetVectorFormat(nfd.LongIntegerFormatMap()); if (instr->Mask(NEON_Q)) {
shll2(vf, rd, rn);
} else {
shll(vf, rd, rn);
} return; default:
VIXL_UNIMPLEMENTED();
}
} else {
VIXL_UNIMPLEMENTED();
}
}
// Only FRINT* instructions fall through the switch above.
frint(fpf, rd, rn, fpcr_rounding, inexact_exception);
}
}
// The input operand's VectorFormat is passed for these instructions. if (instr->Mask(NEONAcrossLanesFPFMask) == NEONAcrossLanesFPFixed) {
VectorFormat vf = nfd.GetVectorFormat(nfd.FPFormatMap());
switch (instr->Mask(NEONAcrossLanesFPMask)) { case NEON_FMAXV: fmaxv(vf, rd, rn); break; case NEON_FMINV: fminv(vf, rd, rn); break; case NEON_FMAXNMV: fmaxnmv(vf, rd, rn); break; case NEON_FMINNMV: fminnmv(vf, rd, rn); break; default:
VIXL_UNIMPLEMENTED();
}
} else {
VectorFormat vf = nfd.GetVectorFormat();
switch (instr->Mask(NEONAcrossLanesMask)) { case NEON_ADDV: addv(vf, rd, rn); break; case NEON_SMAXV: smaxv(vf, rd, rn); break; case NEON_SMINV: sminv(vf, rd, rn); break; case NEON_UMAXV: umaxv(vf, rd, rn); break; case NEON_UMINV: uminv(vf, rd, rn); break; case NEON_SADDLV: saddlv(vf, rd, rn); break; case NEON_UADDLV: uaddlv(vf, rd, rn); break; default:
VIXL_UNIMPLEMENTED();
}
}
}
int rm_reg = instr->Rm(); int index = (instr->NEONH() << 1) | instr->NEONL(); if (instr->NEONSize() == 1) {
rm_reg &= 0xf;
index = (index << 1) | instr->NEONM();
}
switch (instr->Mask(NEONByIndexedElementMask)) { case NEON_MUL_byelement: Op = &Simulator::mul; vf = vf_r; break; case NEON_MLA_byelement: Op = &Simulator::mla; vf = vf_r; break; case NEON_MLS_byelement: Op = &Simulator::mls; vf = vf_r; break; case NEON_SQDMULH_byelement: Op = &Simulator::sqdmulh; vf = vf_r; break; case NEON_SQRDMULH_byelement: Op = &Simulator::sqrdmulh; vf = vf_r; break; case NEON_SMULL_byelement: if (instr->Mask(NEON_Q)) {
Op = &Simulator::smull2;
} else {
Op = &Simulator::smull;
} break; case NEON_UMULL_byelement: if (instr->Mask(NEON_Q)) {
Op = &Simulator::umull2;
} else {
Op = &Simulator::umull;
} break; case NEON_SMLAL_byelement: if (instr->Mask(NEON_Q)) {
Op = &Simulator::smlal2;
} else {
Op = &Simulator::smlal;
} break; case NEON_UMLAL_byelement: if (instr->Mask(NEON_Q)) {
Op = &Simulator::umlal2;
} else {
Op = &Simulator::umlal;
} break; case NEON_SMLSL_byelement: if (instr->Mask(NEON_Q)) {
Op = &Simulator::smlsl2;
} else {
Op = &Simulator::smlsl;
} break; case NEON_UMLSL_byelement: if (instr->Mask(NEON_Q)) {
Op = &Simulator::umlsl2;
} else {
Op = &Simulator::umlsl;
} break; case NEON_SQDMULL_byelement: if (instr->Mask(NEON_Q)) {
Op = &Simulator::sqdmull2;
} else {
Op = &Simulator::sqdmull;
} break; case NEON_SQDMLAL_byelement: if (instr->Mask(NEON_Q)) {
Op = &Simulator::sqdmlal2;
} else {
Op = &Simulator::sqdmlal;
} break; case NEON_SQDMLSL_byelement: if (instr->Mask(NEON_Q)) {
Op = &Simulator::sqdmlsl2;
} else {
Op = &Simulator::sqdmlsl;
} break; default:
index = instr->NEONH(); if ((instr->FPType() & 1) == 0) {
index = (index << 1) | instr->NEONL();
}
vf = nfd.GetVectorFormat(nfd.FPFormatMap());
switch (instr->Mask(NEONByIndexedElementFPMask)) { case NEON_FMUL_byelement: Op = &Simulator::fmul; break; case NEON_FMLA_byelement: Op = &Simulator::fmla; break; case NEON_FMLS_byelement: Op = &Simulator::fmls; break; case NEON_FMULX_byelement: Op = &Simulator::fmulx; break; default: VIXL_UNIMPLEMENTED();
}
}
// We use the PostIndex mask here, as it works in this case for both Offset // and PostIndex addressing. switch (instr->Mask(NEONLoadStoreMultiStructPostIndexMask)) { case NEON_LD1_4v: case NEON_LD1_4v_post: ld1(vf, vreg(reg[3]), addr[3]); count++;
VIXL_FALLTHROUGH(); case NEON_LD1_3v: case NEON_LD1_3v_post: ld1(vf, vreg(reg[2]), addr[2]); count++;
VIXL_FALLTHROUGH(); case NEON_LD1_2v: case NEON_LD1_2v_post: ld1(vf, vreg(reg[1]), addr[1]); count++;
VIXL_FALLTHROUGH(); case NEON_LD1_1v: case NEON_LD1_1v_post:
ld1(vf, vreg(reg[0]), addr[0]);
log_read = true; break; case NEON_ST1_4v: case NEON_ST1_4v_post: st1(vf, vreg(reg[3]), addr[3]); count++;
VIXL_FALLTHROUGH(); case NEON_ST1_3v: case NEON_ST1_3v_post: st1(vf, vreg(reg[2]), addr[2]); count++;
VIXL_FALLTHROUGH(); case NEON_ST1_2v: case NEON_ST1_2v_post: st1(vf, vreg(reg[1]), addr[1]); count++;
VIXL_FALLTHROUGH(); case NEON_ST1_1v: case NEON_ST1_1v_post:
st1(vf, vreg(reg[0]), addr[0]);
log_read = false; break; case NEON_LD2_post: case NEON_LD2:
ld2(vf, vreg(reg[0]), vreg(reg[1]), addr[0]);
count = 2; break; case NEON_ST2: case NEON_ST2_post:
st2(vf, vreg(reg[0]), vreg(reg[1]), addr[0]);
count = 2; break; case NEON_LD3_post: case NEON_LD3:
ld3(vf, vreg(reg[0]), vreg(reg[1]), vreg(reg[2]), addr[0]);
count = 3; break; case NEON_ST3: case NEON_ST3_post:
st3(vf, vreg(reg[0]), vreg(reg[1]), vreg(reg[2]), addr[0]);
count = 3; break; case NEON_ST4: case NEON_ST4_post:
st4(vf, vreg(reg[0]), vreg(reg[1]), vreg(reg[2]), vreg(reg[3]),
addr[0]);
count = 4; break; case NEON_LD4_post: case NEON_LD4:
ld4(vf, vreg(reg[0]), vreg(reg[1]), vreg(reg[2]), vreg(reg[3]),
addr[0]);
count = 4; break; default: VIXL_UNIMPLEMENTED();
}
// Explicitly log the register update whilst we have type information. for (int i = 0; i < count; i++) { // For de-interleaving loads, only print the base address. int lane_size = LaneSizeInBytesFromFormat(vf);
PrintRegisterFormat format = GetPrintRegisterFormatTryFP(
GetPrintRegisterFormatForSize(reg_size, lane_size)); if (log_read) {
LogVRead(addr_base, reg[i], format);
} else {
LogVWrite(addr_base, reg[i], format);
}
}
if (addr_mode == PostIndex) { int rm = instr->Rm(); // The immediate post index addressing mode is indicated by rm = 31. // The immediate is implied by the number of vector registers used.
addr_base += (rm == 31) ? RegisterSizeInBytesFromFormat(vf) * count
: xreg(rm);
set_xreg(instr->Rn(), addr_base);
} else {
VIXL_ASSERT(addr_mode == Offset);
}
}
VectorFormat vf = kFormat16B; switch (instr->Mask(NEONLoadStoreSingleStructPostIndexMask)) { case NEON_LD1_b: case NEON_LD1_b_post: case NEON_LD2_b: case NEON_LD2_b_post: case NEON_LD3_b: case NEON_LD3_b_post: case NEON_LD4_b: case NEON_LD4_b_post: do_load = true;
VIXL_FALLTHROUGH(); case NEON_ST1_b: case NEON_ST1_b_post: case NEON_ST2_b: case NEON_ST2_b_post: case NEON_ST3_b: case NEON_ST3_b_post: case NEON_ST4_b: case NEON_ST4_b_post: break;
case NEON_LD1_h: case NEON_LD1_h_post: case NEON_LD2_h: case NEON_LD2_h_post: case NEON_LD3_h: case NEON_LD3_h_post: case NEON_LD4_h: case NEON_LD4_h_post: do_load = true;
VIXL_FALLTHROUGH(); case NEON_ST1_h: case NEON_ST1_h_post: case NEON_ST2_h: case NEON_ST2_h_post: case NEON_ST3_h: case NEON_ST3_h_post: case NEON_ST4_h: case NEON_ST4_h_post: vf = kFormat8H; break; case NEON_LD1_s: case NEON_LD1_s_post: case NEON_LD2_s: case NEON_LD2_s_post: case NEON_LD3_s: case NEON_LD3_s_post: case NEON_LD4_s: case NEON_LD4_s_post: do_load = true;
VIXL_FALLTHROUGH(); case NEON_ST1_s: case NEON_ST1_s_post: case NEON_ST2_s: case NEON_ST2_s_post: case NEON_ST3_s: case NEON_ST3_s_post: case NEON_ST4_s: case NEON_ST4_s_post: {
VIXL_STATIC_ASSERT((NEON_LD1_s | (1 << NEONLSSize_offset)) == NEON_LD1_d);
VIXL_STATIC_ASSERT(
(NEON_LD1_s_post | (1 << NEONLSSize_offset)) == NEON_LD1_d_post);
VIXL_STATIC_ASSERT((NEON_ST1_s | (1 << NEONLSSize_offset)) == NEON_ST1_d);
VIXL_STATIC_ASSERT(
(NEON_ST1_s_post | (1 << NEONLSSize_offset)) == NEON_ST1_d_post);
vf = ((instr->NEONLSSize() & 1) == 0) ? kFormat4S : kFormat2D; break;
}
case NEON_LD1R: case NEON_LD1R_post: case NEON_LD2R: case NEON_LD2R_post: case NEON_LD3R: case NEON_LD3R_post: case NEON_LD4R: case NEON_LD4R_post: {
vf = vf_t;
do_load = true;
replicating = true; break;
} default: VIXL_UNIMPLEMENTED();
}
PrintRegisterFormat print_format =
GetPrintRegisterFormatTryFP(GetPrintRegisterFormat(vf)); // Make sure that the print_format only includes a single lane.
print_format = static_cast<PrintRegisterFormat>(print_format & ~kPrintRegAsVectorMask);
if (instr->Mask(NEON2RegMiscOpcode) <= NEON_NEG_scalar_opcode) { // These instructions all use a two bit size field, except NOT and RBIT, // which use the field to encode the operation. switch (instr->Mask(NEONScalar2RegMiscMask)) { case NEON_CMEQ_zero_scalar: cmp(vf, rd, rn, 0, eq); break; case NEON_CMGE_zero_scalar: cmp(vf, rd, rn, 0, ge); break; case NEON_CMGT_zero_scalar: cmp(vf, rd, rn, 0, gt); break; case NEON_CMLT_zero_scalar: cmp(vf, rd, rn, 0, lt); break; case NEON_CMLE_zero_scalar: cmp(vf, rd, rn, 0, le); break; case NEON_ABS_scalar: abs(vf, rd, rn); break; case NEON_SQABS_scalar: abs(vf, rd, rn).SignedSaturate(vf); break; case NEON_NEG_scalar: neg(vf, rd, rn); break; case NEON_SQNEG_scalar: neg(vf, rd, rn).SignedSaturate(vf); break; case NEON_SUQADD_scalar: suqadd(vf, rd, rn); break; case NEON_USQADD_scalar: usqadd(vf, rd, rn); break; default: VIXL_UNIMPLEMENTED(); break;
}
} else {
VectorFormat fpf = nfd.GetVectorFormat(nfd.FPScalarFormatMap());
FPRounding fpcr_rounding = static_cast<FPRounding>(fpcr().RMode());
// These instructions all use a one bit size field, except SQXTUN, SQXTN // and UQXTN, which use a two bit size field. switch (instr->Mask(NEONScalar2RegMiscFPMask)) { case NEON_FRECPE_scalar: frecpe(fpf, rd, rn, fpcr_rounding); break; case NEON_FRECPX_scalar: frecpx(fpf, rd, rn); break; case NEON_FRSQRTE_scalar: frsqrte(fpf, rd, rn); break; case NEON_FCMGT_zero_scalar: fcmp_zero(fpf, rd, rn, gt); break; case NEON_FCMGE_zero_scalar: fcmp_zero(fpf, rd, rn, ge); break; case NEON_FCMEQ_zero_scalar: fcmp_zero(fpf, rd, rn, eq); break; case NEON_FCMLE_zero_scalar: fcmp_zero(fpf, rd, rn, le); break; case NEON_FCMLT_zero_scalar: fcmp_zero(fpf, rd, rn, lt); break; case NEON_SCVTF_scalar: scvtf(fpf, rd, rn, 0, fpcr_rounding); break; case NEON_UCVTF_scalar: ucvtf(fpf, rd, rn, 0, fpcr_rounding); break; case NEON_FCVTNS_scalar: fcvts(fpf, rd, rn, FPTieEven); break; case NEON_FCVTNU_scalar: fcvtu(fpf, rd, rn, FPTieEven); break; case NEON_FCVTPS_scalar: fcvts(fpf, rd, rn, FPPositiveInfinity); break; case NEON_FCVTPU_scalar: fcvtu(fpf, rd, rn, FPPositiveInfinity); break; case NEON_FCVTMS_scalar: fcvts(fpf, rd, rn, FPNegativeInfinity); break; case NEON_FCVTMU_scalar: fcvtu(fpf, rd, rn, FPNegativeInfinity); break; case NEON_FCVTZS_scalar: fcvts(fpf, rd, rn, FPZero); break; case NEON_FCVTZU_scalar: fcvtu(fpf, rd, rn, FPZero); break; case NEON_FCVTAS_scalar: fcvts(fpf, rd, rn, FPTieAway); break; case NEON_FCVTAU_scalar: fcvtu(fpf, rd, rn, FPTieAway); break; case NEON_FCVTXN_scalar: // Unlike all of the other FP instructions above, fcvtxn encodes dest // size S as size<0>=1. There's only one case, so we ignore the form.
VIXL_ASSERT(instr->Bit(22) == 1);
fcvtxn(kFormatS, rd, rn); break; default: switch (instr->Mask(NEONScalar2RegMiscMask)) { case NEON_SQXTN_scalar: sqxtn(vf, rd, rn); break; case NEON_UQXTN_scalar: uqxtn(vf, rd, rn); break; case NEON_SQXTUN_scalar: sqxtun(vf, rd, rn); break; default:
VIXL_UNIMPLEMENTED();
}
}
}
}
int rm_reg = instr->Rm(); int index = (instr->NEONH() << 1) | instr->NEONL(); if (instr->NEONSize() == 1) {
rm_reg &= 0xf;
index = (index << 1) | instr->NEONM();
}
switch (instr->Mask(NEONScalarByIndexedElementMask)) { case NEON_SQDMULL_byelement_scalar: Op = &Simulator::sqdmull; break; case NEON_SQDMLAL_byelement_scalar: Op = &Simulator::sqdmlal; break; case NEON_SQDMLSL_byelement_scalar: Op = &Simulator::sqdmlsl; break; case NEON_SQDMULH_byelement_scalar:
Op = &Simulator::sqdmulh;
vf = vf_r; break; case NEON_SQRDMULH_byelement_scalar:
Op = &Simulator::sqrdmulh;
vf = vf_r; break; default:
vf = nfd.GetVectorFormat(nfd.FPScalarFormatMap());
index = instr->NEONH(); if ((instr->FPType() & 1) == 0) {
index = (index << 1) | instr->NEONL();
} switch (instr->Mask(NEONScalarByIndexedElementFPMask)) { case NEON_FMUL_byelement_scalar: Op = &Simulator::fmul; break; case NEON_FMLA_byelement_scalar: Op = &Simulator::fmla; break; case NEON_FMLS_byelement_scalar: Op = &Simulator::fmls; break; case NEON_FMULX_byelement_scalar: Op = &Simulator::fmulx; break; default: VIXL_UNIMPLEMENTED();
}
}
// We don't support a one-shot LOG_DISASM.
VIXL_ASSERT((parameters & LOG_DISASM) == 0); // Print the requested information. if (parameters & LOG_SYSREGS) PrintSystemRegisters(); if (parameters & LOG_REGS) PrintRegisters(); if (parameters & LOG_VREGS) PrintVRegisters();
// We need to call the host printf function with a set of arguments defined by // arg_pattern_list. Because we don't know the types and sizes of the // arguments, this is very difficult to do in a robust and portable way. To // work around the problem, we pick apart the format string, and print one // format placeholder at a time.
// Allocate space for the format string. We take a copy, so we can modify it. // Leave enough space for one extra character per expected argument (plus the // '\0' termination). constchar * format_base = reg<constchar *>(0);
VIXL_ASSERT(format_base != NULL);
size_t length = strlen(format_base) + 1; char * const format = (char *)js_calloc(length + arg_count);
// A list of chunks, each with exactly one format placeholder. constchar * chunks[kPrintfMaxArgCount];
// Copy the format string and search for format placeholders.
uint32_t placeholder_count = 0; char * format_scratch = format; for (size_t i = 0; i < length; i++) { if (format_base[i] != '%') {
*format_scratch++ = format_base[i];
} else { if (format_base[i + 1] == '%') { // Ignore explicit "%%" sequences.
*format_scratch++ = format_base[i];
i++; // Chunks after the first are passed as format strings to printf, so we // need to escape '%' characters in those chunks. if (placeholder_count > 0) *format_scratch++ = format_base[i];
} else {
VIXL_CHECK(placeholder_count < arg_count); // Insert '\0' before placeholders, and store their locations.
*format_scratch++ = '\0';
chunks[placeholder_count++] = format_scratch;
*format_scratch++ = format_base[i];
}
}
}
VIXL_CHECK(placeholder_count == arg_count);
// Finally, call printf with each chunk, passing the appropriate register // argument. Normally, printf returns the number of bytes transmitted, so we // can emulate a single printf call by adding the result from each chunk. If // any call returns a negative (error) value, though, just return that value.
printf("%s", clr_printf);
// Because '\0' is inserted before each placeholder, the first string in // 'format' contains no format placeholders and should be printed literally. int result = printf("%s", format); int pcs_r = 1; // Start at x1. x0 holds the format string. int pcs_f = 0; // Start at d0. if (result >= 0) { for (uint32_t i = 0; i < placeholder_count; i++) { int part_result = -1;
if (part_result < 0) { // Handle error values.
result = part_result; break;
}
result += part_result;
}
}
printf("%s", clr_normal);
// Printf returns its result in x0 (just like the C library's printf).
set_xreg(0, result);
// The printf parameters are inlined in the code, so skip them.
set_pc(instr->InstructionAtOffset(kPrintfLength));
// Set LR as if we'd just called a native printf function.
set_lr(pc());
js_free(format);
}
} // namespace vixl
#endif// JS_SIMULATOR_ARM64
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.105Bemerkung:
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.