Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(89)

Unified Diff: src/arm/simulator-arm.cc

Issue 2006183004: Implement ldrex and strex in ARM simulator (Closed) Base URL: http://chromium.googlesource.com/v8/v8.git@master
Patch Set: Lock on reads Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/arm/simulator-arm.h ('k') | test/cctest/BUILD.gn » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/arm/simulator-arm.cc
diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc
index 39e7a8e837536443266c43075e3daaa5f2733c47..e22a8b95eb3e6c77a55d48ffbc6b613d414ab6d8 100644
--- a/src/arm/simulator-arm.cc
+++ b/src/arm/simulator-arm.cc
@@ -22,6 +22,10 @@
namespace v8 {
namespace internal {
+// 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
@@ -710,9 +714,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
@@ -1040,78 +1045,166 @@ void Simulator::TrashCallerSaveRegisters() {
int Simulator::ReadW(int32_t addr, Instruction* instr) {
// All supported ARM 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;
}
+int Simulator::ReadExW(int32_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_);
+ intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
+ return *ptr;
+}
void Simulator::WriteW(int32_t addr, int value, Instruction* instr) {
// All supported ARM 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_);
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
*ptr = value;
}
+int Simulator::WriteExW(int32_t addr, int 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_)) {
+ intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
+ *ptr = value;
+ return 0;
+ } else {
+ return 1;
+ }
+}
uint16_t Simulator::ReadHU(int32_t addr, Instruction* instr) {
// All supported ARM 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;
}
-
int16_t Simulator::ReadH(int32_t addr, Instruction* instr) {
// All supported ARM 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;
}
+uint16_t Simulator::ReadExHU(int32_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;
+}
void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) {
// All supported ARM 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;
}
-
void Simulator::WriteH(int32_t addr, int16_t value, Instruction* instr) {
// All supported ARM 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;
}
+int Simulator::WriteExH(int32_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(int32_t addr) {
+ 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(int32_t addr) {
+ 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(int32_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(int32_t addr, uint8_t value) {
+ 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(int32_t addr, int8_t value) {
+ 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(int32_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;
+ }
+}
int32_t* Simulator::ReadDW(int32_t addr) {
// All supported ARM 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;
}
@@ -1120,6 +1213,10 @@ int32_t* Simulator::ReadDW(int32_t addr) {
void Simulator::WriteDW(int32_t addr, int32_t value1, int32_t value2) {
// All supported ARM 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++ = value1;
*ptr = value2;
@@ -2073,7 +2170,72 @@ void Simulator::DecodeType01(Instruction* instr) {
}
}
} else {
- UNIMPLEMENTED(); // Not used by V8.
+ if (instr->Bits(24, 23) == 3) {
+ if (instr->Bit(20) == 1) {
+ // ldrex
+ int rt = instr->RtValue();
+ int rn = instr->RnValue();
+ int32_t addr = get_register(rn);
+ switch (instr->Bits(22, 21)) {
+ case 0: {
+ // Format(instr, "ldrex'cond 'rt, ['rn]");
+ int value = ReadExW(addr, instr);
+ set_register(rt, value);
+ break;
+ }
+ case 2: {
+ // Format(instr, "ldrexb'cond 'rt, ['rn]");
+ uint8_t value = ReadExBU(addr);
+ set_register(rt, value);
+ break;
+ }
+ case 3: {
+ // Format(instr, "ldrexh'cond 'rt, ['rn]");
+ uint16_t value = ReadExHU(addr, instr);
+ set_register(rt, value);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ } else {
+ // The instruction is documented as strex rd, rt, [rn], but the
+ // "rt" register is using the rm bits.
+ int rd = instr->RdValue();
+ int rt = instr->RmValue();
+ int rn = instr->RnValue();
+ int32_t addr = get_register(rn);
+ switch (instr->Bits(22, 21)) {
+ case 0: {
+ // Format(instr, "strex'cond 'rd, 'rm, ['rn]");
+ int value = get_register(rt);
+ int status = WriteExW(addr, value, instr);
+ set_register(rd, status);
+ break;
+ }
+ case 2: {
+ // Format(instr, "strexb'cond 'rd, 'rm, ['rn]");
+ uint8_t value = get_register(rt);
+ int status = WriteExB(addr, value);
+ set_register(rd, status);
+ break;
+ }
+ case 3: {
+ // Format(instr, "strexh'cond 'rd, 'rm, ['rn]");
+ uint16_t value = get_register(rt);
+ int status = WriteExH(addr, value, instr);
+ set_register(rd, status);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ } else {
+ UNIMPLEMENTED(); // Not used by V8.
+ }
}
} else {
// extra load/store instructions
@@ -5345,6 +5507,207 @@ uintptr_t Simulator::PopAddress() {
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) {
+ // It is implementation-defined whether a non-exclusive store to an address
+ // covered by the local monitor during exclusive access transitions to open
+ // or exclusive access. See ARM DDI 0406C.b, A3.4.1.
+ //
+ // However, 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) {
+ // It is allowed for a processor to require that the address matches
+ // exactly (A3.4.5), 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. Behavior is unpredictable if
+ // the transaction size of the exclusive store differs from that of the
+ // exclusive load. See ARM DDI 0406C.b, A3.4.5.
+ 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(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 implementation-defined whether a non-exclusive store by the
+ // requesting processor to an address covered by the global monitor
+ // during exclusive access transitions to open or exclusive access.
+ //
+ // For any other processor, the access state always transitions to open
+ // access.
+ //
+ // See ARM DDI 0406C.b, A3.4.2.
+ //
+ // However, similar to the local monitor, 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) {
+ // It is allowed for a processor to require that the address matches
+ // exactly (A3.4.5), so this comparison does not mask addr.
+ if (addr == tagged_addr_) {
+ // The access state for the requesting processor after a successful
+ // exclusive store is implementation-defined, but according to the ARM
+ // DDI, this has no effect on the subsequent operation of the global
+ // monitor.
+ Clear_Locked();
+ // Introduce occasional strex 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(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
« no previous file with comments | « src/arm/simulator-arm.h ('k') | test/cctest/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698