Index: src/arm64/simulator-arm64.cc |
diff --git a/src/arm64/simulator-arm64.cc b/src/arm64/simulator-arm64.cc |
index 83b4cf7ee8954f0838a8389eb3a5c971558eb5a2..3c49b21e52d274373ce07b2c548162aa4d7f5981 100644 |
--- a/src/arm64/simulator-arm64.cc |
+++ b/src/arm64/simulator-arm64.cc |
@@ -55,6 +55,9 @@ TEXT_COLOUR clr_debug_number = FLAG_log_colour ? COLOUR_BOLD(YELLOW) : ""; |
TEXT_COLOUR clr_debug_message = FLAG_log_colour ? COLOUR(YELLOW) : ""; |
TEXT_COLOUR clr_printf = FLAG_log_colour ? COLOUR(GREEN) : ""; |
+// static |
+base::LazyInstance<Simulator::GlobalMonitor>::type Simulator::global_monitor_ = |
+ LAZY_INSTANCE_INITIALIZER; |
// This is basically the same as PrintF, with a guard for FLAG_trace_sim. |
void Simulator::TraceSim(const char* format, ...) { |
@@ -429,6 +432,7 @@ void Simulator::ResetState() { |
Simulator::~Simulator() { |
+ global_monitor_.Pointer()->RemoveProcessor(&global_monitor_processor_); |
delete[] reinterpret_cast<byte*>(stack_); |
if (FLAG_log_instruction_stats) { |
delete instrument_; |
@@ -1628,6 +1632,15 @@ void Simulator::LoadStoreHelper(Instruction* instr, |
uintptr_t address = LoadStoreAddress(addr_reg, offset, addrmode); |
uintptr_t stack = 0; |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ if (instr->IsLoad()) { |
+ local_monitor_.NotifyLoad(address); |
+ } else { |
+ local_monitor_.NotifyStore(address); |
+ global_monitor_.Pointer()->NotifyStore_Locked(address, |
+ &global_monitor_processor_); |
+ } |
+ |
// Handle the writeback for stores before the store. On a CPU the writeback |
// and the store are atomic, but when running on the simulator it is possible |
// to be interrupted in between. The simulator is not thread safe and V8 does |
@@ -1730,6 +1743,19 @@ void Simulator::LoadStorePairHelper(Instruction* instr, |
uintptr_t address2 = address + access_size; |
uintptr_t stack = 0; |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ if (instr->IsLoad()) { |
+ local_monitor_.NotifyLoad(address); |
+ local_monitor_.NotifyLoad(address2); |
+ } else { |
+ local_monitor_.NotifyStore(address); |
+ local_monitor_.NotifyStore(address2); |
+ global_monitor_.Pointer()->NotifyStore_Locked(address, |
+ &global_monitor_processor_); |
+ global_monitor_.Pointer()->NotifyStore_Locked(address2, |
+ &global_monitor_processor_); |
+ } |
+ |
// Handle the writeback for stores before the store. On a CPU the writeback |
// and the store are atomic, but when running on the simulator it is possible |
// to be interrupted in between. The simulator is not thread safe and V8 does |
@@ -1853,6 +1879,9 @@ void Simulator::VisitLoadLiteral(Instruction* instr) { |
uintptr_t address = instr->LiteralAddress(); |
unsigned rt = instr->Rt(); |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyLoad(address); |
+ |
switch (instr->Mask(LoadLiteralMask)) { |
// Use _no_log variants to suppress the register trace (LOG_REGS, |
// LOG_FP_REGS), then print a more detailed log. |
@@ -1906,8 +1935,81 @@ void Simulator::LoadStoreWriteBack(unsigned addr_reg, |
} |
} |
+Simulator::TransactionSize Simulator::get_transaction_size(unsigned size) { |
+ switch (size) { |
+ case 0: |
+ return TransactionSize::None; |
+ case 1: |
+ return TransactionSize::Byte; |
+ case 2: |
+ return TransactionSize::HalfWord; |
+ case 4: |
+ return TransactionSize::Word; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ return TransactionSize::None; |
+} |
+ |
void Simulator::VisitLoadStoreAcquireRelease(Instruction* instr) { |
- // TODO(binji) |
+ unsigned rs = instr->Rs(); |
+ unsigned rt = instr->Rt(); |
+ unsigned rn = instr->Rn(); |
+ LoadStoreAcquireReleaseOp op = static_cast<LoadStoreAcquireReleaseOp>( |
+ instr->Mask(LoadStoreAcquireReleaseMask)); |
+ int32_t is_acquire_release = instr->LoadStoreXAcquireRelease(); |
+ int32_t is_not_exclusive = instr->LoadStoreXNotExclusive(); |
+ int32_t is_load = instr->LoadStoreXLoad(); |
+ int32_t is_pair = instr->LoadStoreXPair(); |
+ DCHECK_NE(is_acquire_release, 0); |
+ DCHECK_EQ(is_not_exclusive, 0); // Non exclusive unimplemented. |
+ DCHECK_EQ(is_pair, 0); // Pair unimplemented. |
+ unsigned access_size = 1 << instr->LoadStoreXSizeLog2(); |
+ uintptr_t address = LoadStoreAddress(rn, 0, AddrMode::Offset); |
+ DCHECK(address % access_size == 0); |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ if (is_load != 0) { |
+ local_monitor_.NotifyLoadExcl(address, get_transaction_size(access_size)); |
+ global_monitor_.Pointer()->NotifyLoadExcl_Locked( |
+ address, &global_monitor_processor_); |
+ switch (op) { |
+ case LDAXR_b: |
+ set_wreg_no_log(rt, MemoryRead<uint8_t>(address)); |
+ break; |
+ case LDAXR_h: |
+ set_wreg_no_log(rt, MemoryRead<uint16_t>(address)); |
+ break; |
+ case LDAXR_w: |
+ set_wreg_no_log(rt, MemoryRead<uint32_t>(address)); |
+ break; |
+ default: |
+ UNIMPLEMENTED(); |
+ } |
+ LogRead(address, access_size, rt); |
+ } else { |
+ if (local_monitor_.NotifyStoreExcl(address, |
+ get_transaction_size(access_size)) && |
+ global_monitor_.Pointer()->NotifyStoreExcl_Locked( |
+ address, &global_monitor_processor_)) { |
+ switch (op) { |
+ case STLXR_b: |
+ MemoryWrite<uint8_t>(address, wreg(rt)); |
+ break; |
+ case STLXR_h: |
+ MemoryWrite<uint16_t>(address, wreg(rt)); |
+ break; |
+ case STLXR_w: |
+ MemoryWrite<uint32_t>(address, wreg(rt)); |
+ break; |
+ default: |
+ UNIMPLEMENTED(); |
+ } |
+ LogWrite(address, access_size, rt); |
+ set_wreg(rs, 0); |
+ } else { |
+ set_wreg(rs, 1); |
+ } |
+ } |
} |
void Simulator::CheckMemoryAccess(uintptr_t address, uintptr_t stack) { |
@@ -3877,6 +3979,186 @@ void Simulator::DoPrintf(Instruction* instr) { |
delete[] format; |
} |
+Simulator::LocalMonitor::LocalMonitor() |
+ : access_state_(MonitorAccess::Open), |
+ tagged_addr_(0), |
+ size_(TransactionSize::None) {} |
+ |
+void Simulator::LocalMonitor::Clear() { |
+ access_state_ = MonitorAccess::Open; |
+ tagged_addr_ = 0; |
+ size_ = TransactionSize::None; |
+} |
+ |
+void Simulator::LocalMonitor::NotifyLoad(uintptr_t addr) { |
+ if (access_state_ == MonitorAccess::Exclusive) { |
+ // A non exclusive load could clear the local monitor. As a result, it's |
+ // most strict to unconditionally clear the local monitor on load. |
+ Clear(); |
+ } |
+} |
+ |
+void Simulator::LocalMonitor::NotifyLoadExcl(uintptr_t addr, |
+ TransactionSize size) { |
+ access_state_ = MonitorAccess::Exclusive; |
+ tagged_addr_ = addr; |
+ size_ = size; |
+} |
+ |
+void Simulator::LocalMonitor::NotifyStore(uintptr_t addr) { |
+ if (access_state_ == MonitorAccess::Exclusive) { |
+ // A non exclusive store could clear the local monitor. As a result, it's |
+ // most strict to unconditionally clear the local monitor on store. |
+ Clear(); |
+ } |
+} |
+ |
+bool Simulator::LocalMonitor::NotifyStoreExcl(uintptr_t addr, |
+ TransactionSize size) { |
+ if (access_state_ == MonitorAccess::Exclusive) { |
+ // It is allowed for a processor to require that the address matches |
+ // exactly (B2.10.1), so this comparison does not mask addr. |
+ if (addr == tagged_addr_ && size_ == size) { |
+ Clear(); |
+ return true; |
+ } else { |
+ // It is implementation-defined whether an exclusive store to a |
+ // non-tagged address will update memory. As a result, it's most strict |
+ // to unconditionally clear the local monitor. |
+ Clear(); |
+ return false; |
+ } |
+ } else { |
+ DCHECK(access_state_ == MonitorAccess::Open); |
+ return false; |
+ } |
+} |
+ |
+Simulator::GlobalMonitor::Processor::Processor() |
+ : access_state_(MonitorAccess::Open), |
+ tagged_addr_(0), |
+ next_(nullptr), |
+ prev_(nullptr), |
+ failure_counter_(0) {} |
+ |
+void Simulator::GlobalMonitor::Processor::Clear_Locked() { |
+ access_state_ = MonitorAccess::Open; |
+ tagged_addr_ = 0; |
+} |
+ |
+void Simulator::GlobalMonitor::Processor::NotifyLoadExcl_Locked( |
+ uintptr_t addr) { |
+ access_state_ = MonitorAccess::Exclusive; |
+ tagged_addr_ = addr; |
+} |
+ |
+void Simulator::GlobalMonitor::Processor::NotifyStore_Locked( |
+ uintptr_t addr, bool is_requesting_processor) { |
+ if (access_state_ == MonitorAccess::Exclusive) { |
+ // A non exclusive store could clear the global monitor. As a result, it's |
+ // most strict to unconditionally clear global monitors on store. |
+ Clear_Locked(); |
+ } |
+} |
+ |
+bool Simulator::GlobalMonitor::Processor::NotifyStoreExcl_Locked( |
+ uintptr_t addr, bool is_requesting_processor) { |
+ if (access_state_ == MonitorAccess::Exclusive) { |
+ if (is_requesting_processor) { |
+ // It is allowed for a processor to require that the address matches |
+ // exactly (B2.10.2), so this comparison does not mask addr. |
+ if (addr == tagged_addr_) { |
+ Clear_Locked(); |
+ // Introduce occasional stxr failures. This is to simulate the |
+ // behavior of hardware, which can randomly fail due to background |
+ // cache evictions. |
+ if (failure_counter_++ >= kMaxFailureCounter) { |
+ failure_counter_ = 0; |
+ return false; |
+ } else { |
+ return true; |
+ } |
+ } |
+ } else if ((addr & kExclusiveTaggedAddrMask) == |
+ (tagged_addr_ & kExclusiveTaggedAddrMask)) { |
+ // Check the masked addresses when responding to a successful lock by |
+ // another processor so the implementation is more conservative (i.e. the |
+ // granularity of locking is as large as possible.) |
+ Clear_Locked(); |
+ return false; |
+ } |
+ } |
+ return false; |
+} |
+ |
+Simulator::GlobalMonitor::GlobalMonitor() : head_(nullptr) {} |
+ |
+void Simulator::GlobalMonitor::NotifyLoadExcl_Locked(uintptr_t addr, |
+ Processor* processor) { |
+ processor->NotifyLoadExcl_Locked(addr); |
+ PrependProcessor_Locked(processor); |
+} |
+ |
+void Simulator::GlobalMonitor::NotifyStore_Locked(uintptr_t addr, |
+ Processor* processor) { |
+ // Notify each processor of the store operation. |
+ for (Processor* iter = head_; iter; iter = iter->next_) { |
+ bool is_requesting_processor = iter == processor; |
+ iter->NotifyStore_Locked(addr, is_requesting_processor); |
+ } |
+} |
+ |
+bool Simulator::GlobalMonitor::NotifyStoreExcl_Locked(uintptr_t addr, |
+ Processor* processor) { |
+ DCHECK(IsProcessorInLinkedList_Locked(processor)); |
+ if (processor->NotifyStoreExcl_Locked(addr, true)) { |
+ // Notify the other processors that this StoreExcl succeeded. |
+ for (Processor* iter = head_; iter; iter = iter->next_) { |
+ if (iter != processor) { |
+ iter->NotifyStoreExcl_Locked(addr, false); |
+ } |
+ } |
+ return true; |
+ } else { |
+ return false; |
+ } |
+} |
+ |
+bool Simulator::GlobalMonitor::IsProcessorInLinkedList_Locked( |
+ Processor* processor) const { |
+ return head_ == processor || processor->next_ || processor->prev_; |
+} |
+ |
+void Simulator::GlobalMonitor::PrependProcessor_Locked(Processor* processor) { |
+ if (IsProcessorInLinkedList_Locked(processor)) { |
+ return; |
+ } |
+ |
+ if (head_) { |
+ head_->prev_ = processor; |
+ } |
+ processor->prev_ = nullptr; |
+ processor->next_ = head_; |
+ head_ = processor; |
+} |
+ |
+void Simulator::GlobalMonitor::RemoveProcessor(Processor* processor) { |
+ base::LockGuard<base::Mutex> lock_guard(&mutex); |
+ if (!IsProcessorInLinkedList_Locked(processor)) { |
+ return; |
+ } |
+ |
+ if (processor->prev_) { |
+ processor->prev_->next_ = processor->next_; |
+ } else { |
+ head_ = processor->next_; |
+ } |
+ if (processor->next_) { |
+ processor->next_->prev_ = processor->prev_; |
+ } |
+ processor->prev_ = nullptr; |
+ processor->next_ = nullptr; |
+} |
#endif // USE_SIMULATOR |