Chromium Code Reviews| 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 |