Chromium Code Reviews| Index: src/arm64/simulator-arm64.cc |
| diff --git a/src/arm64/simulator-arm64.cc b/src/arm64/simulator-arm64.cc |
| index 83b4cf7ee8954f0838a8389eb3a5c971558eb5a2..a5ced87c420d6fe7e95c6bf4f42b018b7078e844 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,82 @@ 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)); |
| + bool is_acquire_release = instr->LoadStoreXAcquireRelease(); |
| + bool is_exclusive = !instr->LoadStoreXNotExclusive(); |
|
jbramley
2017/02/24 15:25:41
These helpers return integers. Usually we would te
aseemgarg
2017/02/24 22:38:49
Done.
|
| + bool is_load = instr->LoadStoreXLoad(); |
| + bool is_pair = instr->LoadStoreXPair(); |
| + DCHECK(is_acquire_release); |
| + DCHECK(is_exclusive); // non exclusive unimplemented |
| + DCHECK(!is_pair); // pair unimplemented |
|
jbramley
2017/02/24 15:25:41
Capital letters, full stops.
aseemgarg
2017/02/24 22:38:49
Done.
|
| + unsigned access_size = 1 << instr->LoadStoreXSizeLog2(); |
| + uintptr_t address = LoadStoreAddress(rn, 0, AddrMode::Offset); |
| + DCHECK(address % access_size == 0); |
| + if (is_load) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
|
jbramley
2017/02/24 15:25:41
You lock the mutex in both paths, so perhaps facto
aseemgarg
2017/02/24 22:38:49
Done.
|
| + 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 { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + 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 +3980,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 |