Index: src/arm/simulator-arm.cc |
diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc |
index 1a870c5c05ae56fc2524022a52713b15523a39c9..d7c028ffa2caf742fc9c38bdb922fbd2a1f55b5e 100644 |
--- a/src/arm/simulator-arm.cc |
+++ b/src/arm/simulator-arm.cc |
@@ -22,6 +22,12 @@ |
namespace v8 { |
namespace internal { |
+// static |
+base::LazyMutex Simulator::DataMonitor::mutex_ = |
+ LAZY_STATIC_INSTANCE_INITIALIZER; |
+// static |
+Simulator::DataMonitor* Simulator::DataMonitor::head_; |
+ |
// 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 |
@@ -2203,7 +2209,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 = data_monitor_.ReadExW(this, addr, instr); |
+ set_register(rt, value); |
+ break; |
+ } |
+ case 2: { |
+ // Format(instr, "ldrexb'cond 'rt, ['rn]"); |
+ uint8_t value = data_monitor_.ReadExBU(this, addr); |
+ set_register(rt, value); |
+ break; |
+ } |
+ case 3: { |
+ // Format(instr, "ldrexh'cond 'rt, ['rn]"); |
+ uint16_t value = data_monitor_.ReadExHU(this, 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 = data_monitor_.WriteExW(this, 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 = data_monitor_.WriteExB(this, addr, value); |
+ set_register(rd, status); |
+ break; |
+ } |
+ case 3: { |
+ // Format(instr, "strexh'cond 'rd, 'rm, ['rn]"); |
+ uint16_t value = get_register(rt); |
+ int status = data_monitor_.WriteExH(this, addr, value, instr); |
+ set_register(rd, status); |
+ break; |
+ } |
+ default: |
+ UNREACHABLE(); |
+ break; |
+ } |
+ } |
+ } else { |
+ UNIMPLEMENTED(); // Not used by V8. |
+ } |
} |
} else { |
// extra load/store instructions |
@@ -4316,6 +4387,146 @@ uintptr_t Simulator::PopAddress() { |
return address; |
} |
+ |
+// Syncronization primitives. See ARM DDI 0406C.b, A2.9. |
+uint8_t Simulator::DataMonitor::ReadExBU(Simulator* simulator, int32_t addr) { |
+ base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); |
+ ReadExInternal_Locked(addr); |
+ return simulator->ReadBU(addr); |
+} |
+ |
+ |
+int Simulator::DataMonitor::WriteExB(Simulator* simulator, |
+ int32_t addr, |
+ uint8_t value) { |
+ base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); |
+ if (WriteExInternal_Locked(addr)) { |
+ simulator->WriteB(addr, value); |
jbramley
2016/05/31 16:58:47
Also, _any_ unrelated memory operation should _som
binji
2016/06/01 21:06:40
OK, makes sense.
|
+ return 1; |
Rodolph Perfetta (ARM)
2016/05/25 17:38:29
it is 0 on success and 1 for failure.
binji
2016/06/01 21:06:40
Oops! done.
|
+ } |
+ return 0; |
+} |
+ |
+ |
+uint16_t Simulator::DataMonitor::ReadExHU(Simulator* simulator, |
+ int32_t addr, |
+ Instruction* instr) { |
+ base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); |
+ ReadExInternal_Locked(addr); |
+ return simulator->ReadHU(addr, instr); |
+} |
+ |
+ |
+int Simulator::DataMonitor::WriteExH(Simulator* simulator, |
+ int32_t addr, |
+ uint16_t value, |
+ Instruction* instr) { |
+ base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); |
+ if (WriteExInternal_Locked(addr)) { |
+ simulator->WriteH(addr, value, instr); |
+ return 1; |
Rodolph Perfetta (ARM)
2016/05/25 17:38:29
0
binji
2016/06/01 21:06:40
Done.
|
+ } |
+ return 0; |
+} |
+ |
+ |
+int Simulator::DataMonitor::ReadExW(Simulator* simulator, |
+ int32_t addr, |
+ Instruction* instr) { |
+ base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); |
+ ReadExInternal_Locked(addr); |
+ return simulator->ReadW(addr, instr); |
+} |
+ |
+ |
+int Simulator::DataMonitor::WriteExW(Simulator* simulator, |
+ int32_t addr, |
+ int value, |
+ Instruction* instr) { |
+ base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); |
+ if (WriteExInternal_Locked(addr)) { |
+ simulator->WriteW(addr, value, instr); |
+ return 1; |
Rodolph Perfetta (ARM)
2016/05/25 17:38:29
0
binji
2016/06/01 21:06:40
Done.
|
+ } |
+ return 0; |
+} |
+ |
+ |
+Simulator::DataMonitor::DataMonitor() |
+ : access_state_(Access::Open), |
+ tagged_addr_(0), |
+ next_(nullptr), |
+ prev_(nullptr) {} |
+ |
+ |
+void Simulator::DataMonitor::ReadExInternal_Locked(int32_t addr) { |
+ if (access_state_ == Access::Exclusive) { |
+ // This DataMonitor is already in the linked list; remove it so can be |
+ // added below. |
+ RemoveNode_Locked(); |
+ } |
+ |
+ PrependNode_Locked(); |
+ access_state_ = Access::Exclusive; |
+ tagged_addr_ = addr & kAddrMask; |
+} |
+ |
+ |
+bool Simulator::DataMonitor::WriteExInternal_Locked(int32_t addr) { |
+ if (access_state_ == Access::Exclusive) { |
jbramley
2016/05/31 16:58:47
This should be more strict, and check that the add
binji
2016/06/01 21:06:40
OK, I'll work on this after these simple fixes.
|
+ // This DataMonitor is in the linked list, check if there is another |
+ // thread that has more recent exclusive access to this address. |
+ DataMonitor* node = FirstNodeWithTaggedAddr_Locked(addr); |
+ if (node != this) { |
+ // This thread does not have exclusive access (either it never called |
+ // ldrex or another thread took it away. Either way, this write will |
+ // fail. |
+ return false; |
+ } |
+ |
+ RemoveNode_Locked(); |
+ access_state_ = Access::Open; |
Rodolph Perfetta (ARM)
2016/05/25 17:38:29
You should make sure any other nodes with the same
binji
2016/06/01 21:06:40
OK, I'll work on this after these simple fixes.
|
+ return true; |
+ } else { |
+ // Not marked for exclusive access. |
+ return false; |
+ } |
+} |
+ |
+ |
+void Simulator::DataMonitor::RemoveNode_Locked() { |
+ if (prev_) { |
+ prev_->next_ = next_; |
+ } else { |
+ head_ = next_; |
+ } |
+ if (next_) { |
+ next_->prev_ = prev_; |
+ } |
+ prev_ = nullptr; |
+ next_ = nullptr; |
+} |
+ |
+ |
+void Simulator::DataMonitor::PrependNode_Locked() { |
+ if (head_) { |
+ head_->prev_ = this; |
+ } |
Rodolph Perfetta (ARM)
2016/05/25 17:38:29
shouldn't next_ point to head_?
binji
2016/06/01 21:06:40
Done.
|
+ head_ = this; |
+} |
+ |
+ |
+Simulator::DataMonitor* Simulator::DataMonitor::FirstNodeWithTaggedAddr_Locked( |
+ int32_t addr) { |
+ for (DataMonitor* node = head_; node; node = node->next_) { |
+ if ((addr & kAddrMask) == node->tagged_addr_) { |
+ return node; |
+ } |
+ } |
+ return nullptr; |
+} |
+ |
+ |
} // namespace internal |
} // namespace v8 |