// 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;
--> --------------------
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.