Chromium Code Reviews| Index: src/arm/simulator-arm.cc |
| diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc |
| index afe31db95063ec4c214049dbdb52bda4717ecbb7..f6e3c8467da82ee5dcc0b56a4117150e038104e8 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 |
| @@ -769,9 +773,10 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { |
| last_debugger_input_ = NULL; |
| } |
| - |
| -Simulator::~Simulator() { free(stack_); } |
| - |
| +Simulator::~Simulator() { |
| + global_monitor_.Pointer()->RemoveNode(&global_monitor_node_); |
| + 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 |
| @@ -1134,8 +1139,7 @@ int Simulator::ReadW(int32_t addr, Instruction* instr) { |
| } |
| } |
| - |
| -void Simulator::WriteW(int32_t addr, int value, Instruction* instr) { |
| +void Simulator::WriteW_Internal(int32_t addr, int value, Instruction* instr) { |
| if (FLAG_enable_unaligned_accesses || (addr & 3) == 0) { |
| intptr_t* ptr = reinterpret_cast<intptr_t*>(addr); |
| *ptr = value; |
| @@ -1174,8 +1178,8 @@ int16_t Simulator::ReadH(int32_t addr, Instruction* instr) { |
| } |
| } |
| - |
| -void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) { |
| +void Simulator::WriteH_Internal(int32_t addr, uint16_t value, |
| + Instruction* instr) { |
| if (FLAG_enable_unaligned_accesses || (addr & 1) == 0) { |
| uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); |
| *ptr = value; |
| @@ -1188,8 +1192,8 @@ void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) { |
| } |
| } |
| - |
| -void Simulator::WriteH(int32_t addr, int16_t value, Instruction* instr) { |
| +void Simulator::WriteH_Internal(int32_t addr, int16_t value, |
| + Instruction* instr) { |
| if (FLAG_enable_unaligned_accesses || (addr & 1) == 0) { |
| int16_t* ptr = reinterpret_cast<int16_t*>(addr); |
| *ptr = value; |
| @@ -1213,14 +1217,12 @@ int8_t Simulator::ReadB(int32_t addr) { |
| return *ptr; |
| } |
| - |
| -void Simulator::WriteB(int32_t addr, uint8_t value) { |
| +void Simulator::WriteB_Internal(int32_t addr, uint8_t value) { |
| uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); |
| *ptr = value; |
| } |
| - |
| -void Simulator::WriteB(int32_t addr, int8_t value) { |
| +void Simulator::WriteB_Internal(int32_t addr, int8_t value) { |
| int8_t* ptr = reinterpret_cast<int8_t*>(addr); |
| *ptr = value; |
| } |
| @@ -2197,7 +2199,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 |
| @@ -4310,6 +4377,262 @@ uintptr_t Simulator::PopAddress() { |
| return address; |
| } |
| +void Simulator::WriteB(int32_t addr, uint8_t value) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + local_monitor_.Store(addr, TransactionSize::Byte); |
| + global_monitor_.Pointer()->Store_Locked(addr, &global_monitor_node_); |
| + WriteB_Internal(addr, value); |
| +} |
| + |
| +void Simulator::WriteB(int32_t addr, int8_t value) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + local_monitor_.Store(addr, TransactionSize::Byte); |
| + global_monitor_.Pointer()->Store_Locked(addr, &global_monitor_node_); |
| + WriteB_Internal(addr, value); |
| +} |
| + |
| +void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + local_monitor_.Store(addr, TransactionSize::HalfWord); |
| + global_monitor_.Pointer()->Store_Locked(addr, &global_monitor_node_); |
| + WriteH_Internal(addr, value, instr); |
| +} |
| + |
| +void Simulator::WriteH(int32_t addr, int16_t value, Instruction* instr) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + local_monitor_.Store(addr, TransactionSize::HalfWord); |
| + global_monitor_.Pointer()->Store_Locked(addr, &global_monitor_node_); |
| + WriteH_Internal(addr, value, instr); |
| +} |
| + |
| +void Simulator::WriteW(int32_t addr, int value, Instruction* instr) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + local_monitor_.Store(addr, TransactionSize::Word); |
| + global_monitor_.Pointer()->Store_Locked(addr, &global_monitor_node_); |
| + WriteW_Internal(addr, value, instr); |
| +} |
| + |
| +uint8_t Simulator::ReadExBU(int32_t addr) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + local_monitor_.LoadExcl(addr, TransactionSize::Byte); |
| + global_monitor_.Pointer()->LoadExcl_Locked(addr, &global_monitor_node_); |
| + return ReadBU(addr); |
| +} |
| + |
| +int Simulator::WriteExB(int32_t addr, uint8_t value) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + if (local_monitor_.CheckStoreExcl(addr, TransactionSize::Byte) && |
| + global_monitor_.Pointer()->CheckStoreExcl_Locked(addr, |
| + &global_monitor_node_)) { |
| + WriteB_Internal(addr, value); |
| + return 0; |
| + } else { |
| + return 1; |
| + } |
| +} |
| + |
| +uint16_t Simulator::ReadExHU(int32_t addr, Instruction* instr) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + local_monitor_.LoadExcl(addr, TransactionSize::HalfWord); |
| + global_monitor_.Pointer()->LoadExcl_Locked(addr, &global_monitor_node_); |
| + return ReadHU(addr, instr); |
| +} |
| + |
| +int Simulator::WriteExH(int32_t addr, uint16_t value, Instruction* instr) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + if (local_monitor_.CheckStoreExcl(addr, TransactionSize::HalfWord) && |
| + global_monitor_.Pointer()->CheckStoreExcl_Locked(addr, |
| + &global_monitor_node_)) { |
| + WriteH_Internal(addr, value, instr); |
| + return 0; |
| + } else { |
| + return 1; |
| + } |
| +} |
| + |
| +int Simulator::ReadExW(int32_t addr, Instruction* instr) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + local_monitor_.LoadExcl(addr, TransactionSize::Word); |
| + global_monitor_.Pointer()->LoadExcl_Locked(addr, &global_monitor_node_); |
| + return ReadW(addr, instr); |
| +} |
| + |
| +int Simulator::WriteExW(int32_t addr, int value, Instruction* instr) { |
| + base::LockGuard<base::Mutex> lock_guard(&global_monitor_.Pointer()->mutex); |
| + if (local_monitor_.CheckStoreExcl(addr, TransactionSize::Word) && |
| + global_monitor_.Pointer()->CheckStoreExcl_Locked(addr, |
| + &global_monitor_node_)) { |
| + WriteW_Internal(addr, value, instr); |
| + return 0; |
| + } else { |
| + return 1; |
| + } |
| +} |
| + |
| +Simulator::LocalMonitor::LocalMonitor() |
| + : access_state_(MonitorAccess::Open), |
| + tagged_addr_(0), |
| + size_(TransactionSize::None) {} |
| + |
| +void Simulator::LocalMonitor::LoadExcl(int32_t addr, TransactionSize size) { |
| + access_state_ = MonitorAccess::Exclusive; |
| + tagged_addr_ = addr & kTaggedAddrMask; |
| + size_ = size; |
| +} |
| + |
| +void Simulator::LocalMonitor::Store(int32_t addr, TransactionSize size) { |
| + 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. |
|
jbramley
2016/07/20 16:09:24
That's true, but also note that software should av
binji
2016/07/29 21:46:29
Done.
|
| + if ((addr & kTaggedAddrMask) == tagged_addr_) { |
| + access_state_ = MonitorAccess::Open; |
| + tagged_addr_ = 0; |
| + size_ = TransactionSize::None; |
| + } |
| + } |
| +} |
| + |
| +bool Simulator::LocalMonitor::CheckStoreExcl(int32_t addr, |
| + TransactionSize size) { |
| + if (access_state_ == MonitorAccess::Exclusive) { |
| + if ((addr & kTaggedAddrMask) == tagged_addr_ && size_ == size) { |
|
jbramley
2016/07/20 16:09:24
I'd drop the mask here. The ARM ARM allows a proce
binji
2016/07/29 21:46:29
Done.
|
| + access_state_ = MonitorAccess::Open; |
| + tagged_addr_ = 0; |
| + size_ = TransactionSize::None; |
| + return true; |
|
jbramley
2016/07/20 16:09:24
This should fail randomly from time to time. The A
binji
2016/07/29 21:46:29
Done, though I'm not sure this is the best way to
jbramley
2016/11/10 11:19:01
Some randomness would be useful but this is probab
|
| + } 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. |
| + access_state_ = MonitorAccess::Open; |
| + tagged_addr_ = 0; |
| + size_ = TransactionSize::None; |
| + return false; |
| + } |
| + } else { |
| + DCHECK(access_state_ == MonitorAccess::Open); |
| + return false; |
| + } |
| +} |
| + |
| +Simulator::GlobalMonitor::Node::Node() |
| + : access_state_(MonitorAccess::Open), |
| + tagged_addr_(0), |
| + next_(nullptr), |
| + prev_(nullptr) {} |
| + |
| +void Simulator::GlobalMonitor::Node::LoadExcl_Locked(int32_t addr) { |
| + access_state_ = MonitorAccess::Exclusive; |
| + tagged_addr_ = addr & kTaggedAddrMask; |
| +} |
| + |
| +void Simulator::GlobalMonitor::Node::Store_Locked( |
| + int32_t addr, bool is_requesting_processor) { |
| + if (access_state_ == MonitorAccess::Exclusive) { |
| + if ((addr & kTaggedAddrMask) == tagged_addr_) { |
| + // 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. |
| + access_state_ = MonitorAccess::Open; |
| + tagged_addr_ = 0; |
| + } |
| + } |
| +} |
| + |
| +bool Simulator::GlobalMonitor::Node::CheckStoreExcl_Locked( |
| + int32_t addr, bool is_requesting_processor) { |
| + if (access_state_ == MonitorAccess::Exclusive) { |
| + if ((addr & kTaggedAddrMask) == tagged_addr_) { |
| + if (is_requesting_processor) { |
| + // 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. |
| + access_state_ = MonitorAccess::Open; |
| + tagged_addr_ = 0; |
| + return true; |
| + } else { |
| + access_state_ = MonitorAccess::Open; |
| + tagged_addr_ = 0; |
| + return false; |
| + } |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +Simulator::GlobalMonitor::GlobalMonitor() : head_(nullptr) {} |
| + |
| +void Simulator::GlobalMonitor::LoadExcl_Locked(int32_t addr, Node* node) { |
| + node->LoadExcl_Locked(addr); |
| + PrependNode_Locked(node); |
| +} |
| + |
| +void Simulator::GlobalMonitor::Store_Locked(int32_t addr, Node* node) { |
| + // Notify each processor of the store operation. |
| + for (Node* iter = head_; iter; iter = iter->next_) { |
| + bool is_requesting_processor = iter == node; |
| + iter->Store_Locked(addr, is_requesting_processor); |
| + } |
| +} |
| + |
| +bool Simulator::GlobalMonitor::CheckStoreExcl_Locked(int32_t addr, Node* node) { |
| + DCHECK(IsNodeInLinkedList_Locked(node)); |
| + if (node->CheckStoreExcl_Locked(addr, true)) { |
| + // Notify the other processors that this StoreExcl succeeded. |
| + for (Node* iter = head_; iter; iter = iter->next_) { |
| + if (iter != node) { |
| + iter->CheckStoreExcl_Locked(addr, false); |
| + } |
| + } |
| + return true; |
| + } else { |
| + return false; |
| + } |
| +} |
| + |
| +bool Simulator::GlobalMonitor::IsNodeInLinkedList_Locked(Node* node) const { |
| + return head_ == node || node->next_ || node->prev_; |
| +} |
| + |
| +void Simulator::GlobalMonitor::PrependNode_Locked(Node* node) { |
| + if (IsNodeInLinkedList_Locked(node)) { |
| + return; |
| + } |
| + |
| + if (head_) { |
| + head_->prev_ = node; |
| + } |
| + node->prev_ = nullptr; |
| + node->next_ = head_; |
| + head_ = node; |
| +} |
| + |
| +void Simulator::GlobalMonitor::RemoveNode(Node* node) { |
| + base::LockGuard<base::Mutex> lock_guard(&mutex); |
| + if (!IsNodeInLinkedList_Locked(node)) { |
| + return; |
| + } |
| + |
| + if (node->prev_) { |
| + node->prev_->next_ = node->next_; |
| + } else { |
| + head_ = node->next_; |
| + } |
| + if (node->next_) { |
| + node->next_->prev_ = node->prev_; |
| + } |
| + node->prev_ = nullptr; |
| + node->next_ = nullptr; |
| +} |
| + |
| } // namespace internal |
| } // namespace v8 |