Index: src/ppc/simulator-ppc.cc |
diff --git a/src/ppc/simulator-ppc.cc b/src/ppc/simulator-ppc.cc |
index c82f9464eea9333ec3e7acc8b3428df29d403404..071b7e4199f7699547d9c65745c13eddf872327c 100644 |
--- a/src/ppc/simulator-ppc.cc |
+++ b/src/ppc/simulator-ppc.cc |
@@ -25,6 +25,10 @@ namespace internal { |
const auto GetRegConfig = RegisterConfiguration::Crankshaft; |
+// static |
+base::LazyInstance<Simulator::GlobalMonitor>::type Simulator::global_monitor_ = |
+ LAZY_INSTANCE_INITIALIZER; |
+ |
// This macro provides a platform independent use of sscanf. The reason for |
// SScanF not being implemented in a platform independent way through |
// ::v8::internal::OS in the same way as SNPrintF is that the |
@@ -781,9 +785,10 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { |
last_debugger_input_ = NULL; |
} |
- |
-Simulator::~Simulator() { free(stack_); } |
- |
+Simulator::~Simulator() { |
+ global_monitor_.Pointer()->RemoveProcessor(&global_monitor_processor_); |
+ free(stack_); |
+} |
// When the generated code calls an external reference we need to catch that in |
// the simulator. The external reference will be a function compiled for the |
@@ -987,44 +992,105 @@ void Simulator::TrashCallerSaveRegisters() { |
uint32_t Simulator::ReadWU(intptr_t addr, Instruction* instr) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyLoad(addr); |
uint32_t* ptr = reinterpret_cast<uint32_t*>(addr); |
return *ptr; |
} |
+uint32_t Simulator::ReadExWU(intptr_t addr, Instruction* instr) { |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyLoadExcl(addr, TransactionSize::Word); |
+ global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, |
+ &global_monitor_processor_); |
+ uint32_t* ptr = reinterpret_cast<uint32_t*>(addr); |
+ return *ptr; |
+} |
int32_t Simulator::ReadW(intptr_t addr, Instruction* instr) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyLoad(addr); |
int32_t* ptr = reinterpret_cast<int32_t*>(addr); |
return *ptr; |
} |
void Simulator::WriteW(intptr_t addr, uint32_t value, Instruction* instr) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyStore(addr); |
+ global_monitor_.Pointer()->NotifyStore_Locked(addr, |
+ &global_monitor_processor_); |
uint32_t* ptr = reinterpret_cast<uint32_t*>(addr); |
*ptr = value; |
return; |
} |
+int Simulator::WriteExW(intptr_t addr, uint32_t value, Instruction* instr) { |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::Word) && |
+ global_monitor_.Pointer()->NotifyStoreExcl_Locked( |
+ addr, &global_monitor_processor_)) { |
+ uint32_t* ptr = reinterpret_cast<uint32_t*>(addr); |
+ *ptr = value; |
+ return 0; |
+ } else { |
+ return 1; |
+ } |
+} |
void Simulator::WriteW(intptr_t addr, int32_t value, Instruction* instr) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyStore(addr); |
+ global_monitor_.Pointer()->NotifyStore_Locked(addr, |
+ &global_monitor_processor_); |
int32_t* ptr = reinterpret_cast<int32_t*>(addr); |
*ptr = value; |
return; |
} |
- |
uint16_t Simulator::ReadHU(intptr_t addr, Instruction* instr) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyLoad(addr); |
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); |
return *ptr; |
} |
+uint16_t Simulator::ReadExHU(intptr_t addr, Instruction* instr) { |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyLoadExcl(addr, TransactionSize::HalfWord); |
+ global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, |
+ &global_monitor_processor_); |
+ uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); |
+ return *ptr; |
+} |
int16_t Simulator::ReadH(intptr_t addr, Instruction* instr) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyLoad(addr); |
int16_t* ptr = reinterpret_cast<int16_t*>(addr); |
return *ptr; |
} |
void Simulator::WriteH(intptr_t addr, uint16_t value, Instruction* instr) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyStore(addr); |
+ global_monitor_.Pointer()->NotifyStore_Locked(addr, |
+ &global_monitor_processor_); |
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); |
*ptr = value; |
return; |
@@ -1032,43 +1098,111 @@ void Simulator::WriteH(intptr_t addr, uint16_t value, Instruction* instr) { |
void Simulator::WriteH(intptr_t addr, int16_t value, Instruction* instr) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyStore(addr); |
+ global_monitor_.Pointer()->NotifyStore_Locked(addr, |
+ &global_monitor_processor_); |
int16_t* ptr = reinterpret_cast<int16_t*>(addr); |
*ptr = value; |
return; |
} |
+int Simulator::WriteExH(intptr_t addr, uint16_t value, Instruction* instr) { |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::HalfWord) && |
+ global_monitor_.Pointer()->NotifyStoreExcl_Locked( |
+ addr, &global_monitor_processor_)) { |
+ uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); |
+ *ptr = value; |
+ return 0; |
+ } else { |
+ return 1; |
+ } |
+} |
uint8_t Simulator::ReadBU(intptr_t addr) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyLoad(addr); |
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); |
return *ptr; |
} |
int8_t Simulator::ReadB(intptr_t addr) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyLoad(addr); |
int8_t* ptr = reinterpret_cast<int8_t*>(addr); |
return *ptr; |
} |
+uint8_t Simulator::ReadExBU(intptr_t addr) { |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyLoadExcl(addr, TransactionSize::Byte); |
+ global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, |
+ &global_monitor_processor_); |
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); |
+ return *ptr; |
+} |
void Simulator::WriteB(intptr_t addr, uint8_t value) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyStore(addr); |
+ global_monitor_.Pointer()->NotifyStore_Locked(addr, |
+ &global_monitor_processor_); |
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); |
*ptr = value; |
} |
void Simulator::WriteB(intptr_t addr, int8_t value) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyStore(addr); |
+ global_monitor_.Pointer()->NotifyStore_Locked(addr, |
+ &global_monitor_processor_); |
int8_t* ptr = reinterpret_cast<int8_t*>(addr); |
*ptr = value; |
} |
+int Simulator::WriteExB(intptr_t addr, uint8_t value) { |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::Byte) && |
+ global_monitor_.Pointer()->NotifyStoreExcl_Locked( |
+ addr, &global_monitor_processor_)) { |
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); |
+ *ptr = value; |
+ return 0; |
+ } else { |
+ return 1; |
+ } |
+} |
intptr_t* Simulator::ReadDW(intptr_t addr) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyLoad(addr); |
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr); |
return ptr; |
} |
void Simulator::WriteDW(intptr_t addr, int64_t value) { |
+ // All supported PPC targets allow unaligned accesses, so we don't need to |
+ // check the alignment here. |
+ base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
+ local_monitor_.NotifyStore(addr); |
+ global_monitor_.Pointer()->NotifyStore_Locked(addr, |
+ &global_monitor_processor_); |
int64_t* ptr = reinterpret_cast<int64_t*>(addr); |
*ptr = value; |
return; |
@@ -2262,6 +2396,36 @@ void Simulator::ExecuteGeneric(Instruction* instr) { |
} |
break; |
} |
+ case STBCX: { |
+ int rs = instr->RSValue(); |
+ int ra = instr->RAValue(); |
+ int rb = instr->RBValue(); |
+ intptr_t ra_val = ra == 0 ? 0 : get_register(ra); |
+ int8_t rs_val = get_register(rs); |
+ intptr_t rb_val = get_register(rb); |
+ SetCR0(WriteExB(ra_val + rb_val, rs_val)); |
+ break; |
+ } |
+ case STHCX: { |
+ int rs = instr->RSValue(); |
+ int ra = instr->RAValue(); |
+ int rb = instr->RBValue(); |
+ intptr_t ra_val = ra == 0 ? 0 : get_register(ra); |
+ int16_t rs_val = get_register(rs); |
+ intptr_t rb_val = get_register(rb); |
+ SetCR0(WriteExH(ra_val + rb_val, rs_val, instr)); |
+ break; |
+ } |
+ case STWCX: { |
+ int rs = instr->RSValue(); |
+ int ra = instr->RAValue(); |
+ int rb = instr->RBValue(); |
+ intptr_t ra_val = ra == 0 ? 0 : get_register(ra); |
+ int32_t rs_val = get_register(rs); |
+ intptr_t rb_val = get_register(rb); |
+ SetCR0(WriteExW(ra_val + rb_val, rs_val, instr)); |
+ break; |
+ } |
case TW: { |
// used for call redirection in simulation mode |
SoftwareInterrupt(instr); |
@@ -2993,18 +3157,40 @@ void Simulator::ExecuteGeneric(Instruction* instr) { |
} |
break; |
} |
- case LHAX: |
- case LHAUX: { |
+ case LHAX: { |
int rt = instr->RTValue(); |
int ra = instr->RAValue(); |
int rb = instr->RBValue(); |
intptr_t ra_val = ra == 0 ? 0 : get_register(ra); |
intptr_t rb_val = get_register(rb); |
set_register(rt, ReadH(ra_val + rb_val, instr)); |
- if (opcode == LHAUX) { |
- DCHECK(ra != 0 && ra != rt); |
- set_register(ra, ra_val + rb_val); |
- } |
+ break; |
+ } |
+ case LBARX: { |
+ int rt = instr->RTValue(); |
+ int ra = instr->RAValue(); |
+ int rb = instr->RBValue(); |
+ intptr_t ra_val = ra == 0 ? 0 : get_register(ra); |
+ intptr_t rb_val = get_register(rb); |
+ set_register(rt, ReadExBU(ra_val + rb_val) & 0xFF); |
+ break; |
+ } |
+ case LHARX: { |
+ int rt = instr->RTValue(); |
+ int ra = instr->RAValue(); |
+ int rb = instr->RBValue(); |
+ intptr_t ra_val = ra == 0 ? 0 : get_register(ra); |
+ intptr_t rb_val = get_register(rb); |
+ set_register(rt, ReadExHU(ra_val + rb_val, instr)); |
+ break; |
+ } |
+ case LWARX: { |
+ int rt = instr->RTValue(); |
+ int ra = instr->RAValue(); |
+ int rb = instr->RBValue(); |
+ intptr_t ra_val = ra == 0 ? 0 : get_register(ra); |
+ intptr_t rb_val = get_register(rb); |
+ set_register(rt, ReadExWU(ra_val + rb_val, instr)); |
break; |
} |
case DCBF: { |
@@ -4073,6 +4259,169 @@ uintptr_t Simulator::PopAddress() { |
set_register(sp, current_sp + sizeof(uintptr_t)); |
return address; |
} |
+ |
+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(int32_t addr) { |
+ if (access_state_ == MonitorAccess::Exclusive) { |
+ // A load could cause a cache eviction which will affect the monitor. As a |
+ // result, it's most strict to unconditionally clear the local monitor on |
+ // load. |
+ Clear(); |
+ } |
+} |
+ |
+void Simulator::LocalMonitor::NotifyLoadExcl(int32_t addr, |
+ TransactionSize size) { |
+ access_state_ = MonitorAccess::Exclusive; |
+ tagged_addr_ = addr; |
+ size_ = size; |
+} |
+ |
+void Simulator::LocalMonitor::NotifyStore(int32_t addr) { |
+ if (access_state_ == MonitorAccess::Exclusive) { |
+ // A store could cause a cache eviction which will affect the |
+ // monitor. As a result, it's most strict to unconditionally clear the |
+ // local monitor on store. |
+ Clear(); |
+ } |
+} |
+ |
+bool Simulator::LocalMonitor::NotifyStoreExcl(int32_t addr, |
+ TransactionSize size) { |
+ if (access_state_ == MonitorAccess::Exclusive) { |
+ if (addr == tagged_addr_ && size_ == size) { |
+ Clear(); |
+ return true; |
+ } else { |
+ 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) {} |
+ |
+void Simulator::GlobalMonitor::Processor::Clear_Locked() { |
+ access_state_ = MonitorAccess::Open; |
+ tagged_addr_ = 0; |
+} |
+void Simulator::GlobalMonitor::Processor::NotifyLoadExcl_Locked(int32_t addr) { |
+ access_state_ = MonitorAccess::Exclusive; |
+ tagged_addr_ = addr; |
+} |
+ |
+void Simulator::GlobalMonitor::Processor::NotifyStore_Locked( |
+ int32_t addr, bool is_requesting_processor) { |
+ if (access_state_ == MonitorAccess::Exclusive) { |
+ // It is possible that a store caused a cache eviction, |
+ // which can affect the montior, so conservatively, |
+ // we always clear the monitor. |
+ Clear_Locked(); |
+ } |
+} |
+ |
+bool Simulator::GlobalMonitor::Processor::NotifyStoreExcl_Locked( |
+ int32_t addr, bool is_requesting_processor) { |
+ if (access_state_ == MonitorAccess::Exclusive) { |
+ if (is_requesting_processor) { |
+ if (addr == tagged_addr_) { |
+ Clear_Locked(); |
+ return true; |
+ } |
+ } else if (addr == tagged_addr_) { |
+ Clear_Locked(); |
+ return false; |
+ } |
+ } |
+ return false; |
+} |
+ |
+Simulator::GlobalMonitor::GlobalMonitor() : head_(nullptr) {} |
+ |
+void Simulator::GlobalMonitor::NotifyLoadExcl_Locked(int32_t addr, |
+ Processor* processor) { |
+ processor->NotifyLoadExcl_Locked(addr); |
+ PrependProcessor_Locked(processor); |
+} |
+ |
+void Simulator::GlobalMonitor::NotifyStore_Locked(int32_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(int32_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; |
+} |
+ |
} // namespace internal |
} // namespace v8 |