Commit ee8bb5f0 authored by Predrag Rudic's avatar Predrag Rudic Committed by Commit Bot

MIPS64: Correct implementation of LLD/SCD instructions in simulator.

This implementation uses mutex to implement those instructions.
I will add 32-bit version LL/SC after review.

Change-Id: I7b0e2b42527bc21060a84eb5e27099e129f2858f
Reviewed-on: https://chromium-review.googlesource.com/c/1354462Reviewed-by: 's avatarSreten Kovacevic <skovacevic@wavecomp.com>
Commit-Queue: Predrag Rudic <prudic@wavecomp.com>
Cr-Commit-Position: refs/heads/master@{#58048}
parent feb65761
......@@ -26,6 +26,10 @@
namespace v8 {
namespace internal {
// static
base::LazyInstance<Simulator::GlobalMonitor>::type Simulator::global_monitor_ =
LAZY_INSTANCE_INITIALIZER;
// Utils functions.
bool HaveSameSign(int32_t a, int32_t b) {
return ((a ^ b) >= 0);
......@@ -915,9 +919,10 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) {
last_debugger_input_ = nullptr;
}
Simulator::~Simulator() { free(stack_); }
Simulator::~Simulator() {
global_monitor_.Pointer()->RemoveLinkedAddress(&global_monitor_thread_);
free(stack_);
}
// Get the active Simulator for the current thread.
Simulator* Simulator::current(Isolate* isolate) {
......@@ -1966,6 +1971,7 @@ int Simulator::ReadW(int32_t addr, Instruction* instr, TraceType t) {
dbg.Debug();
}
if ((addr & kPointerAlignmentMask) == 0 || IsMipsArchVariant(kMips32r6)) {
local_monitor_.NotifyLoad();
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
switch (t) {
case WORD:
......@@ -1996,6 +2002,9 @@ void Simulator::WriteW(int32_t addr, int value, Instruction* instr) {
dbg.Debug();
}
if ((addr & kPointerAlignmentMask) == 0 || IsMipsArchVariant(kMips32r6)) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
TraceMemWr(addr, value, WORD);
*ptr = value;
......@@ -2008,8 +2017,40 @@ void Simulator::WriteW(int32_t addr, int value, Instruction* instr) {
dbg.Debug();
}
void Simulator::WriteConditionalW(int32_t addr, int32_t value,
Instruction* instr, int32_t rt_reg) {
if (addr >= 0 && addr < 0x400) {
// This has to be a nullptr-dereference, drop into debugger.
PrintF("Memory write to bad address: 0x%08x, pc=0x%08" PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MipsDebugger dbg(this);
dbg.Debug();
}
if ((addr & kPointerAlignmentMask) == 0 || IsMipsArchVariant(kMips32r6)) {
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
if (local_monitor_.NotifyStoreConditional(addr, TransactionSize::Word) &&
global_monitor_.Pointer()->NotifyStoreConditional_Locked(
addr, &global_monitor_thread_)) {
local_monitor_.NotifyStore();
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
TraceMemWr(addr, value, WORD);
int* ptr = reinterpret_cast<int*>(addr);
*ptr = value;
set_register(rt_reg, 1);
} else {
set_register(rt_reg, 0);
}
return;
}
PrintF("Unaligned write at 0x%08x, pc=0x%08" V8PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MipsDebugger dbg(this);
dbg.Debug();
}
double Simulator::ReadD(int32_t addr, Instruction* instr) {
if ((addr & kDoubleAlignmentMask) == 0 || IsMipsArchVariant(kMips32r6)) {
local_monitor_.NotifyLoad();
double* ptr = reinterpret_cast<double*>(addr);
return *ptr;
}
......@@ -2023,6 +2064,9 @@ double Simulator::ReadD(int32_t addr, Instruction* instr) {
void Simulator::WriteD(int32_t addr, double value, Instruction* instr) {
if ((addr & kDoubleAlignmentMask) == 0 || IsMipsArchVariant(kMips32r6)) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
double* ptr = reinterpret_cast<double*>(addr);
*ptr = value;
return;
......@@ -2036,6 +2080,7 @@ void Simulator::WriteD(int32_t addr, double value, Instruction* instr) {
uint16_t Simulator::ReadHU(int32_t addr, Instruction* instr) {
if ((addr & 1) == 0 || IsMipsArchVariant(kMips32r6)) {
local_monitor_.NotifyLoad();
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
TraceMemRd(addr, static_cast<int32_t>(*ptr));
return *ptr;
......@@ -2050,6 +2095,7 @@ uint16_t Simulator::ReadHU(int32_t addr, Instruction* instr) {
int16_t Simulator::ReadH(int32_t addr, Instruction* instr) {
if ((addr & 1) == 0 || IsMipsArchVariant(kMips32r6)) {
local_monitor_.NotifyLoad();
int16_t* ptr = reinterpret_cast<int16_t*>(addr);
TraceMemRd(addr, static_cast<int32_t>(*ptr));
return *ptr;
......@@ -2064,6 +2110,9 @@ int16_t Simulator::ReadH(int32_t addr, Instruction* instr) {
void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) {
if ((addr & 1) == 0 || IsMipsArchVariant(kMips32r6)) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
TraceMemWr(addr, value, HALF);
*ptr = value;
......@@ -2078,6 +2127,9 @@ void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) {
void Simulator::WriteH(int32_t addr, int16_t value, Instruction* instr) {
if ((addr & 1) == 0 || IsMipsArchVariant(kMips32r6)) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
int16_t* ptr = reinterpret_cast<int16_t*>(addr);
TraceMemWr(addr, value, HALF);
*ptr = value;
......@@ -2091,6 +2143,7 @@ void Simulator::WriteH(int32_t addr, int16_t value, Instruction* instr) {
uint32_t Simulator::ReadBU(int32_t addr) {
local_monitor_.NotifyLoad();
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
TraceMemRd(addr, static_cast<int32_t>(*ptr));
return *ptr & 0xFF;
......@@ -2098,6 +2151,7 @@ uint32_t Simulator::ReadBU(int32_t addr) {
int32_t Simulator::ReadB(int32_t addr) {
local_monitor_.NotifyLoad();
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
TraceMemRd(addr, static_cast<int32_t>(*ptr));
return *ptr;
......@@ -2105,6 +2159,9 @@ int32_t Simulator::ReadB(int32_t addr) {
void Simulator::WriteB(int32_t addr, uint8_t value) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
TraceMemWr(addr, value, BYTE);
*ptr = value;
......@@ -2112,6 +2169,9 @@ void Simulator::WriteB(int32_t addr, uint8_t value) {
void Simulator::WriteB(int32_t addr, int8_t value) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
TraceMemWr(addr, value, BYTE);
*ptr = value;
......@@ -2121,6 +2181,7 @@ template <typename T>
T Simulator::ReadMem(int32_t addr, Instruction* instr) {
int alignment_mask = (1 << sizeof(T)) - 1;
if ((addr & alignment_mask) == 0 || IsMipsArchVariant(kMips32r6)) {
local_monitor_.NotifyLoad();
T* ptr = reinterpret_cast<T*>(addr);
TraceMemRd(addr, *ptr);
return *ptr;
......@@ -2133,6 +2194,9 @@ T Simulator::ReadMem(int32_t addr, Instruction* instr) {
template <typename T>
void Simulator::WriteMem(int32_t addr, T value, Instruction* instr) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
int alignment_mask = (1 << sizeof(T)) - 1;
if ((addr & alignment_mask) == 0 || IsMipsArchVariant(kMips32r6)) {
T* ptr = reinterpret_cast<T*>(addr);
......@@ -6726,16 +6790,19 @@ void Simulator::DecodeTypeImmediate() {
break;
}
case LL: {
// LL/SC sequence cannot be simulated properly
DCHECK(!IsMipsArchVariant(kMips32r6));
set_register(rt_reg, ReadW(rs + se_imm16, instr_.instr()));
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
addr = rs + se_imm16;
set_register(rt_reg, ReadW(addr, instr_.instr()));
local_monitor_.NotifyLoadLinked(addr, TransactionSize::Word);
global_monitor_.Pointer()->NotifyLoadLinked_Locked(
addr, &global_monitor_thread_);
break;
}
case SC: {
// LL/SC sequence cannot be simulated properly
DCHECK(!IsMipsArchVariant(kMips32r6));
WriteW(rs + se_imm16, rt, instr_.instr());
set_register(rt_reg, 1);
addr = rs + se_imm16;
WriteConditionalW(addr, rt, instr_.instr(), rt_reg);
break;
}
case LWC1:
......@@ -6805,24 +6872,25 @@ void Simulator::DecodeTypeImmediate() {
case SPECIAL3: {
switch (instr_.FunctionFieldRaw()) {
case LL_R6: {
// LL/SC sequence cannot be simulated properly
DCHECK(IsMipsArchVariant(kMips32r6));
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
int32_t base = get_register(instr_.BaseValue());
int32_t offset9 = instr_.Imm9Value();
addr = base + offset9;
DCHECK_EQ(addr & kPointerAlignmentMask, 0);
set_register(rt_reg, ReadW(base + offset9, instr_.instr()));
local_monitor_.NotifyLoadLinked(addr, TransactionSize::Word);
global_monitor_.Pointer()->NotifyLoadLinked_Locked(
addr, &global_monitor_thread_);
break;
}
case SC_R6: {
// LL/SC sequence cannot be simulated properly
DCHECK(IsMipsArchVariant(kMips32r6));
int32_t base = get_register(instr_.BaseValue());
int32_t offset9 = instr_.Imm9Value();
int32_t bit6 = instr_.Bit(6);
WriteW(base + offset9, rt, instr_.instr());
// Only SC (and not SCX) instruction modifies rt_reg
if (bit6 == 0) {
set_register(rt_reg, 1);
}
addr = base + offset9;
DCHECK_EQ(addr & kPointerAlignmentMask, 0);
WriteConditionalW(addr, rt, instr_.instr(), rt_reg);
break;
}
default:
......@@ -7112,6 +7180,179 @@ 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() {
if (access_state_ == MonitorAccess::RMW) {
// A non linked load could clear the local monitor. As a result, it's
// most strict to unconditionally clear the local monitor on load.
Clear();
}
}
void Simulator::LocalMonitor::NotifyLoadLinked(uintptr_t addr,
TransactionSize size) {
access_state_ = MonitorAccess::RMW;
tagged_addr_ = addr;
size_ = size;
}
void Simulator::LocalMonitor::NotifyStore() {
if (access_state_ == MonitorAccess::RMW) {
// A non exclusive store could clear the local monitor. As a result, it's
// most strict to unconditionally clear the local monitor on store.
Clear();
}
}
bool Simulator::LocalMonitor::NotifyStoreConditional(uintptr_t addr,
TransactionSize size) {
if (access_state_ == MonitorAccess::RMW) {
if (addr == tagged_addr_ && size_ == size) {
Clear();
return true;
} else {
return false;
}
} else {
DCHECK(access_state_ == MonitorAccess::Open);
return false;
}
}
Simulator::GlobalMonitor::LinkedAddress::LinkedAddress()
: access_state_(MonitorAccess::Open),
tagged_addr_(0),
next_(nullptr),
prev_(nullptr),
failure_counter_(0) {}
void Simulator::GlobalMonitor::LinkedAddress::Clear_Locked() {
access_state_ = MonitorAccess::Open;
tagged_addr_ = 0;
}
void Simulator::GlobalMonitor::LinkedAddress::NotifyLoadLinked_Locked(
uintptr_t addr) {
access_state_ = MonitorAccess::RMW;
tagged_addr_ = addr;
}
void Simulator::GlobalMonitor::LinkedAddress::NotifyStore_Locked() {
if (access_state_ == MonitorAccess::RMW) {
// A non exclusive store could clear the global monitor. As a result, it's
// most strict to unconditionally clear global monitors on store.
Clear_Locked();
}
}
bool Simulator::GlobalMonitor::LinkedAddress::NotifyStoreConditional_Locked(
uintptr_t addr, bool is_requesting_processor) {
if (access_state_ == MonitorAccess::RMW) {
if (is_requesting_processor) {
if (addr == tagged_addr_) {
Clear_Locked();
// Introduce occasional sc/scd 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 thread 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::NotifyLoadLinked_Locked(
uintptr_t addr, LinkedAddress* linked_address) {
linked_address->NotifyLoadLinked_Locked(addr);
PrependProcessor_Locked(linked_address);
}
void Simulator::GlobalMonitor::NotifyStore_Locked(
LinkedAddress* linked_address) {
// Notify each thread of the store operation.
for (LinkedAddress* iter = head_; iter; iter = iter->next_) {
iter->NotifyStore_Locked();
}
}
bool Simulator::GlobalMonitor::NotifyStoreConditional_Locked(
uintptr_t addr, LinkedAddress* linked_address) {
DCHECK(IsProcessorInLinkedList_Locked(linked_address));
if (linked_address->NotifyStoreConditional_Locked(addr, true)) {
// Notify the other processors that this StoreConditional succeeded.
for (LinkedAddress* iter = head_; iter; iter = iter->next_) {
if (iter != linked_address) {
iter->NotifyStoreConditional_Locked(addr, false);
}
}
return true;
} else {
return false;
}
}
bool Simulator::GlobalMonitor::IsProcessorInLinkedList_Locked(
LinkedAddress* linked_address) const {
return head_ == linked_address || linked_address->next_ ||
linked_address->prev_;
}
void Simulator::GlobalMonitor::PrependProcessor_Locked(
LinkedAddress* linked_address) {
if (IsProcessorInLinkedList_Locked(linked_address)) {
return;
}
if (head_) {
head_->prev_ = linked_address;
}
linked_address->prev_ = nullptr;
linked_address->next_ = head_;
head_ = linked_address;
}
void Simulator::GlobalMonitor::RemoveLinkedAddress(
LinkedAddress* linked_address) {
base::MutexGuard lock_guard(&mutex);
if (!IsProcessorInLinkedList_Locked(linked_address)) {
return;
}
if (linked_address->prev_) {
linked_address->prev_->next_ = linked_address->next_;
} else {
head_ = linked_address->next_;
}
if (linked_address->next_) {
linked_address->next_->prev_ = linked_address->prev_;
}
linked_address->prev_ = nullptr;
linked_address->next_ = nullptr;
}
#undef UNSUPPORTED
......
......@@ -314,6 +314,8 @@ class Simulator : public SimulatorBase {
inline int ReadW(int32_t addr, Instruction* instr, TraceType t = WORD);
inline void WriteW(int32_t addr, int value, Instruction* instr);
void WriteConditionalW(int32_t addr, int32_t value, Instruction* instr,
int32_t rt_reg);
inline double ReadD(int32_t addr, Instruction* instr);
inline void WriteD(int32_t addr, double value, Instruction* instr);
......@@ -554,6 +556,94 @@ class Simulator : public SimulatorBase {
char* desc;
};
StopCountAndDesc watched_stops_[kMaxStopCode + 1];
// Synchronization primitives.
enum class MonitorAccess {
Open,
RMW,
};
enum class TransactionSize {
None = 0,
Word = 4,
};
// The least-significant bits of the address are ignored. The number of bits
// is implementation-defined, between 3 and minimum page size.
static const uintptr_t kExclusiveTaggedAddrMask = ~((1 << 3) - 1);
class LocalMonitor {
public:
LocalMonitor();
// These functions manage the state machine for the local monitor, but do
// not actually perform loads and stores. NotifyStoreConditional only
// returns true if the store conditional is allowed; the global monitor will
// still have to be checked to see whether the memory should be updated.
void NotifyLoad();
void NotifyLoadLinked(uintptr_t addr, TransactionSize size);
void NotifyStore();
bool NotifyStoreConditional(uintptr_t addr, TransactionSize size);
private:
void Clear();
MonitorAccess access_state_;
uintptr_t tagged_addr_;
TransactionSize size_;
};
class GlobalMonitor {
public:
GlobalMonitor();
class LinkedAddress {
public:
LinkedAddress();
private:
friend class GlobalMonitor;
// These functions manage the state machine for the global monitor, but do
// not actually perform loads and stores.
void Clear_Locked();
void NotifyLoadLinked_Locked(uintptr_t addr);
void NotifyStore_Locked();
bool NotifyStoreConditional_Locked(uintptr_t addr,
bool is_requesting_thread);
MonitorAccess access_state_;
uintptr_t tagged_addr_;
LinkedAddress* next_;
LinkedAddress* prev_;
// A scd can fail due to background cache evictions. Rather than
// simulating this, we'll just occasionally introduce cases where an
// store conditional fails. This will happen once after every
// kMaxFailureCounter exclusive stores.
static const int kMaxFailureCounter = 5;
int failure_counter_;
};
// Exposed so it can be accessed by Simulator::{Read,Write}Ex*.
base::Mutex mutex;
void NotifyLoadLinked_Locked(uintptr_t addr, LinkedAddress* linked_address);
void NotifyStore_Locked(LinkedAddress* linked_address);
bool NotifyStoreConditional_Locked(uintptr_t addr,
LinkedAddress* linked_address);
// Called when the simulator is destroyed.
void RemoveLinkedAddress(LinkedAddress* linked_address);
private:
bool IsProcessorInLinkedList_Locked(LinkedAddress* linked_address) const;
void PrependProcessor_Locked(LinkedAddress* linked_address);
LinkedAddress* head_;
};
LocalMonitor local_monitor_;
GlobalMonitor::LinkedAddress global_monitor_thread_;
static base::LazyInstance<GlobalMonitor>::type global_monitor_;
};
} // namespace internal
......
......@@ -25,6 +25,10 @@
namespace v8 {
namespace internal {
// static
base::LazyInstance<Simulator::GlobalMonitor>::type Simulator::global_monitor_ =
LAZY_INSTANCE_INITIALIZER;
// Util functions.
inline bool HaveSameSign(int64_t a, int64_t b) { return ((a ^ b) >= 0); }
......@@ -848,9 +852,10 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) {
last_debugger_input_ = nullptr;
}
Simulator::~Simulator() { free(stack_); }
Simulator::~Simulator() {
global_monitor_.Pointer()->RemoveLinkedAddress(&global_monitor_thread_);
free(stack_);
}
// Get the active Simulator for the current thread.
Simulator* Simulator::current(Isolate* isolate) {
......@@ -1871,6 +1876,7 @@ int32_t Simulator::ReadW(int64_t addr, Instruction* instr, TraceType t) {
DieOrDebug();
}
if ((addr & 0x3) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyLoad();
int32_t* ptr = reinterpret_cast<int32_t*>(addr);
TraceMemRd(addr, static_cast<int64_t>(*ptr), t);
return *ptr;
......@@ -1891,6 +1897,7 @@ uint32_t Simulator::ReadWU(int64_t addr, Instruction* instr) {
DieOrDebug();
}
if ((addr & 0x3) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyLoad();
uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
TraceMemRd(addr, static_cast<int64_t>(*ptr), WORD);
return *ptr;
......@@ -1911,6 +1918,9 @@ void Simulator::WriteW(int64_t addr, int32_t value, Instruction* instr) {
DieOrDebug();
}
if ((addr & 0x3) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
TraceMemWr(addr, value, WORD);
int* ptr = reinterpret_cast<int*>(addr);
*ptr = value;
......@@ -1921,6 +1931,35 @@ void Simulator::WriteW(int64_t addr, int32_t value, Instruction* instr) {
DieOrDebug();
}
void Simulator::WriteConditionalW(int64_t addr, int32_t value,
Instruction* instr, int32_t rt_reg) {
if (addr >= 0 && addr < 0x400) {
// This has to be a nullptr-dereference, drop into debugger.
PrintF("Memory write to bad address: 0x%08" PRIx64 " , pc=0x%08" PRIxPTR
" \n",
addr, reinterpret_cast<intptr_t>(instr));
DieOrDebug();
}
if ((addr & 0x3) == 0 || kArchVariant == kMips64r6) {
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
if (local_monitor_.NotifyStoreConditional(addr, TransactionSize::Word) &&
global_monitor_.Pointer()->NotifyStoreConditional_Locked(
addr, &global_monitor_thread_)) {
local_monitor_.NotifyStore();
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
TraceMemWr(addr, value, WORD);
int* ptr = reinterpret_cast<int*>(addr);
*ptr = value;
set_register(rt_reg, 1);
} else {
set_register(rt_reg, 0);
}
return;
}
PrintF("Unaligned write at 0x%08" PRIx64 " , pc=0x%08" V8PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
DieOrDebug();
}
int64_t Simulator::Read2W(int64_t addr, Instruction* instr) {
if (addr >=0 && addr < 0x400) {
......@@ -1931,6 +1970,7 @@ int64_t Simulator::Read2W(int64_t addr, Instruction* instr) {
DieOrDebug();
}
if ((addr & kPointerAlignmentMask) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyLoad();
int64_t* ptr = reinterpret_cast<int64_t*>(addr);
TraceMemRd(addr, *ptr);
return *ptr;
......@@ -1951,6 +1991,9 @@ void Simulator::Write2W(int64_t addr, int64_t value, Instruction* instr) {
DieOrDebug();
}
if ((addr & kPointerAlignmentMask) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
TraceMemWr(addr, value, DWORD);
int64_t* ptr = reinterpret_cast<int64_t*>(addr);
*ptr = value;
......@@ -1961,9 +2004,40 @@ void Simulator::Write2W(int64_t addr, int64_t value, Instruction* instr) {
DieOrDebug();
}
void Simulator::WriteConditional2W(int64_t addr, int64_t value,
Instruction* instr, int32_t rt_reg) {
if (addr >= 0 && addr < 0x400) {
// This has to be a nullptr-dereference, drop into debugger.
PrintF("Memory write to bad address: 0x%08" PRIx64 " , pc=0x%08" PRIxPTR
"\n",
addr, reinterpret_cast<intptr_t>(instr));
DieOrDebug();
}
if ((addr & kPointerAlignmentMask) == 0 || kArchVariant == kMips64r6) {
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
if (local_monitor_.NotifyStoreConditional(addr,
TransactionSize::DoubleWord) &&
global_monitor_.Pointer()->NotifyStoreConditional_Locked(
addr, &global_monitor_thread_)) {
local_monitor_.NotifyStore();
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
TraceMemWr(addr, value, DWORD);
int64_t* ptr = reinterpret_cast<int64_t*>(addr);
*ptr = value;
set_register(rt_reg, 1);
} else {
set_register(rt_reg, 0);
}
return;
}
PrintF("Unaligned write at 0x%08" PRIx64 " , pc=0x%08" V8PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
DieOrDebug();
}
double Simulator::ReadD(int64_t addr, Instruction* instr) {
if ((addr & kDoubleAlignmentMask) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyLoad();
double* ptr = reinterpret_cast<double*>(addr);
return *ptr;
}
......@@ -1976,6 +2050,9 @@ double Simulator::ReadD(int64_t addr, Instruction* instr) {
void Simulator::WriteD(int64_t addr, double value, Instruction* instr) {
if ((addr & kDoubleAlignmentMask) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
double* ptr = reinterpret_cast<double*>(addr);
*ptr = value;
return;
......@@ -1989,6 +2066,7 @@ void Simulator::WriteD(int64_t addr, double value, Instruction* instr) {
uint16_t Simulator::ReadHU(int64_t addr, Instruction* instr) {
if ((addr & 1) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyLoad();
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
TraceMemRd(addr, static_cast<int64_t>(*ptr));
return *ptr;
......@@ -2003,6 +2081,7 @@ uint16_t Simulator::ReadHU(int64_t addr, Instruction* instr) {
int16_t Simulator::ReadH(int64_t addr, Instruction* instr) {
if ((addr & 1) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyLoad();
int16_t* ptr = reinterpret_cast<int16_t*>(addr);
TraceMemRd(addr, static_cast<int64_t>(*ptr));
return *ptr;
......@@ -2017,6 +2096,9 @@ int16_t Simulator::ReadH(int64_t addr, Instruction* instr) {
void Simulator::WriteH(int64_t addr, uint16_t value, Instruction* instr) {
if ((addr & 1) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
TraceMemWr(addr, value, HALF);
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
*ptr = value;
......@@ -2031,6 +2113,9 @@ void Simulator::WriteH(int64_t addr, uint16_t value, Instruction* instr) {
void Simulator::WriteH(int64_t addr, int16_t value, Instruction* instr) {
if ((addr & 1) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
TraceMemWr(addr, value, HALF);
int16_t* ptr = reinterpret_cast<int16_t*>(addr);
*ptr = value;
......@@ -2044,6 +2129,7 @@ void Simulator::WriteH(int64_t addr, int16_t value, Instruction* instr) {
uint32_t Simulator::ReadBU(int64_t addr) {
local_monitor_.NotifyLoad();
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
TraceMemRd(addr, static_cast<int64_t>(*ptr));
return *ptr & 0xFF;
......@@ -2051,6 +2137,7 @@ uint32_t Simulator::ReadBU(int64_t addr) {
int32_t Simulator::ReadB(int64_t addr) {
local_monitor_.NotifyLoad();
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
TraceMemRd(addr, static_cast<int64_t>(*ptr));
return *ptr;
......@@ -2058,6 +2145,9 @@ int32_t Simulator::ReadB(int64_t addr) {
void Simulator::WriteB(int64_t addr, uint8_t value) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
TraceMemWr(addr, value, BYTE);
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
*ptr = value;
......@@ -2065,6 +2155,9 @@ void Simulator::WriteB(int64_t addr, uint8_t value) {
void Simulator::WriteB(int64_t addr, int8_t value) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
TraceMemWr(addr, value, BYTE);
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
*ptr = value;
......@@ -2074,6 +2167,7 @@ template <typename T>
T Simulator::ReadMem(int64_t addr, Instruction* instr) {
int alignment_mask = (1 << sizeof(T)) - 1;
if ((addr & alignment_mask) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyLoad();
T* ptr = reinterpret_cast<T*>(addr);
TraceMemRd(addr, *ptr);
return *ptr;
......@@ -2089,6 +2183,9 @@ template <typename T>
void Simulator::WriteMem(int64_t addr, T value, Instruction* instr) {
int alignment_mask = (1 << sizeof(T)) - 1;
if ((addr & alignment_mask) == 0 || kArchVariant == kMips64r6) {
local_monitor_.NotifyStore();
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
global_monitor_.Pointer()->NotifyStore_Locked(&global_monitor_thread_);
T* ptr = reinterpret_cast<T*>(addr);
*ptr = value;
TraceMemWr(addr, value);
......@@ -6912,6 +7009,7 @@ void Simulator::DecodeTypeImmediate() {
set_register(rt_reg, ReadH(rs + se_imm16, instr_.instr()));
break;
case LWL: {
local_monitor_.NotifyLoad();
// al_offset is offset of the effective address within an aligned word.
uint8_t al_offset = (rs + se_imm16) & kInt32AlignmentMask;
uint8_t byte_shift = kInt32AlignmentMask - al_offset;
......@@ -7025,29 +7123,35 @@ void Simulator::DecodeTypeImmediate() {
break;
}
case LL: {
// LL/SC sequence cannot be simulated properly
DCHECK_EQ(kArchVariant, kMips64r2);
set_register(rt_reg, ReadW(rs + se_imm16, instr_.instr()));
DCHECK(kArchVariant != kMips64r6);
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
addr = rs + se_imm16;
set_register(rt_reg, ReadW(addr, instr_.instr()));
local_monitor_.NotifyLoadLinked(addr, TransactionSize::Word);
global_monitor_.Pointer()->NotifyLoadLinked_Locked(
addr, &global_monitor_thread_);
break;
}
case SC: {
// LL/SC sequence cannot be simulated properly
DCHECK_EQ(kArchVariant, kMips64r2);
WriteW(rs + se_imm16, static_cast<int32_t>(rt), instr_.instr());
set_register(rt_reg, 1);
DCHECK(kArchVariant != kMips64r6);
addr = rs + se_imm16;
WriteConditionalW(addr, static_cast<int32_t>(rt), instr_.instr(), rt_reg);
break;
}
case LLD: {
// LL/SC sequence cannot be simulated properly
DCHECK_EQ(kArchVariant, kMips64r2);
set_register(rt_reg, Read2W(rs + se_imm16, instr_.instr()));
DCHECK(kArchVariant != kMips64r6);
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
addr = rs + se_imm16;
set_register(rt_reg, Read2W(addr, instr_.instr()));
local_monitor_.NotifyLoadLinked(addr, TransactionSize::DoubleWord);
global_monitor_.Pointer()->NotifyLoadLinked_Locked(
addr, &global_monitor_thread_);
break;
}
case SCD: {
// LL/SC sequence cannot be simulated properly
DCHECK_EQ(kArchVariant, kMips64r2);
Write2W(rs + se_imm16, rt, instr_.instr());
set_register(rt_reg, 1);
DCHECK(kArchVariant != kMips64r6);
addr = rs + se_imm16;
WriteConditional2W(addr, rt, instr_.instr(), rt_reg);
break;
}
case LWC1:
......@@ -7101,8 +7205,7 @@ void Simulator::DecodeTypeImmediate() {
imm19 <<= (kOpcodeBits + kRsBits + 2);
imm19 >>= (kOpcodeBits + kRsBits + 2);
addr = current_pc + (imm19 << 2);
uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
alu_out = *ptr;
alu_out = ReadWU(addr, instr_.instr());
break;
}
case LWPC: {
......@@ -7110,8 +7213,7 @@ void Simulator::DecodeTypeImmediate() {
imm19 <<= (kOpcodeBits + kRsBits + 2);
imm19 >>= (kOpcodeBits + kRsBits + 2);
addr = current_pc + (imm19 << 2);
int32_t* ptr = reinterpret_cast<int32_t*>(addr);
alu_out = *ptr;
alu_out = ReadW(addr, instr_.instr());
break;
}
case ADDIUPC: {
......@@ -7136,37 +7238,48 @@ void Simulator::DecodeTypeImmediate() {
case SPECIAL3: {
switch (instr_.FunctionFieldRaw()) {
case LL_R6: {
// LL/SC sequence cannot be simulated properly
DCHECK_EQ(kArchVariant, kMips64r6);
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
int64_t base = get_register(instr_.BaseValue());
int32_t offset9 = instr_.Imm9Value();
set_register(rt_reg, ReadW(base + offset9, instr_.instr()));
addr = base + offset9;
DCHECK_EQ(addr & 0x3, 0);
set_register(rt_reg, ReadW(addr, instr_.instr()));
local_monitor_.NotifyLoadLinked(addr, TransactionSize::Word);
global_monitor_.Pointer()->NotifyLoadLinked_Locked(
addr, &global_monitor_thread_);
break;
}
case LLD_R6: {
// LL/SC sequence cannot be simulated properly
DCHECK_EQ(kArchVariant, kMips64r6);
base::MutexGuard lock_guard(&global_monitor_.Pointer()->mutex);
int64_t base = get_register(instr_.BaseValue());
int32_t offset9 = instr_.Imm9Value();
set_register(rt_reg, Read2W(base + offset9, instr_.instr()));
addr = base + offset9;
DCHECK_EQ(addr & kPointerAlignmentMask, 0);
set_register(rt_reg, Read2W(addr, instr_.instr()));
local_monitor_.NotifyLoadLinked(addr, TransactionSize::DoubleWord);
global_monitor_.Pointer()->NotifyLoadLinked_Locked(
addr, &global_monitor_thread_);
break;
}
case SC_R6: {
// LL/SC sequence cannot be simulated properly
DCHECK_EQ(kArchVariant, kMips64r6);
int64_t base = get_register(instr_.BaseValue());
int32_t offset9 = instr_.Imm9Value();
WriteW(base + offset9, static_cast<int32_t>(rt), instr_.instr());
set_register(rt_reg, 1);
addr = base + offset9;
DCHECK_EQ(addr & 0x3, 0);
WriteConditionalW(addr, static_cast<int32_t>(rt), instr_.instr(),
rt_reg);
break;
}
case SCD_R6: {
// LL/SC sequence cannot be simulated properly
DCHECK_EQ(kArchVariant, kMips64r6);
int64_t base = get_register(instr_.BaseValue());
int32_t offset9 = instr_.Imm9Value();
Write2W(base + offset9, rt, instr_.instr());
set_register(rt_reg, 1);
addr = base + offset9;
DCHECK_EQ(addr & kPointerAlignmentMask, 0);
WriteConditional2W(addr, rt, instr_.instr(), rt_reg);
break;
}
default:
......@@ -7401,7 +7514,7 @@ intptr_t Simulator::CallImpl(Address entry, int argument_count,
if (reg_arg_count > 0) set_register(a0, arguments[0]);
if (reg_arg_count > 1) set_register(a1, arguments[1]);
if (reg_arg_count > 2) set_register(a2, arguments[2]);
if (reg_arg_count > 2) set_register(a3, arguments[3]);
if (reg_arg_count > 3) set_register(a3, arguments[3]);
// Up to eight arguments passed in registers in N64 ABI.
// TODO(plind): N64 ABI calls these regs a4 - a7. Clarify this.
......@@ -7474,6 +7587,179 @@ 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() {
if (access_state_ == MonitorAccess::RMW) {
// A non linked load could clear the local monitor. As a result, it's
// most strict to unconditionally clear the local monitor on load.
Clear();
}
}
void Simulator::LocalMonitor::NotifyLoadLinked(uintptr_t addr,
TransactionSize size) {
access_state_ = MonitorAccess::RMW;
tagged_addr_ = addr;
size_ = size;
}
void Simulator::LocalMonitor::NotifyStore() {
if (access_state_ == MonitorAccess::RMW) {
// A non exclusive store could clear the local monitor. As a result, it's
// most strict to unconditionally clear the local monitor on store.
Clear();
}
}
bool Simulator::LocalMonitor::NotifyStoreConditional(uintptr_t addr,
TransactionSize size) {
if (access_state_ == MonitorAccess::RMW) {
if (addr == tagged_addr_ && size_ == size) {
Clear();
return true;
} else {
return false;
}
} else {
DCHECK(access_state_ == MonitorAccess::Open);
return false;
}
}
Simulator::GlobalMonitor::LinkedAddress::LinkedAddress()
: access_state_(MonitorAccess::Open),
tagged_addr_(0),
next_(nullptr),
prev_(nullptr),
failure_counter_(0) {}
void Simulator::GlobalMonitor::LinkedAddress::Clear_Locked() {
access_state_ = MonitorAccess::Open;
tagged_addr_ = 0;
}
void Simulator::GlobalMonitor::LinkedAddress::NotifyLoadLinked_Locked(
uintptr_t addr) {
access_state_ = MonitorAccess::RMW;
tagged_addr_ = addr;
}
void Simulator::GlobalMonitor::LinkedAddress::NotifyStore_Locked() {
if (access_state_ == MonitorAccess::RMW) {
// A non exclusive store could clear the global monitor. As a result, it's
// most strict to unconditionally clear global monitors on store.
Clear_Locked();
}
}
bool Simulator::GlobalMonitor::LinkedAddress::NotifyStoreConditional_Locked(
uintptr_t addr, bool is_requesting_thread) {
if (access_state_ == MonitorAccess::RMW) {
if (is_requesting_thread) {
if (addr == tagged_addr_) {
Clear_Locked();
// Introduce occasional sc/scd 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 thread 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::NotifyLoadLinked_Locked(
uintptr_t addr, LinkedAddress* linked_address) {
linked_address->NotifyLoadLinked_Locked(addr);
PrependProcessor_Locked(linked_address);
}
void Simulator::GlobalMonitor::NotifyStore_Locked(
LinkedAddress* linked_address) {
// Notify each thread of the store operation.
for (LinkedAddress* iter = head_; iter; iter = iter->next_) {
iter->NotifyStore_Locked();
}
}
bool Simulator::GlobalMonitor::NotifyStoreConditional_Locked(
uintptr_t addr, LinkedAddress* linked_address) {
DCHECK(IsProcessorInLinkedList_Locked(linked_address));
if (linked_address->NotifyStoreConditional_Locked(addr, true)) {
// Notify the other processors that this StoreConditional succeeded.
for (LinkedAddress* iter = head_; iter; iter = iter->next_) {
if (iter != linked_address) {
iter->NotifyStoreConditional_Locked(addr, false);
}
}
return true;
} else {
return false;
}
}
bool Simulator::GlobalMonitor::IsProcessorInLinkedList_Locked(
LinkedAddress* linked_address) const {
return head_ == linked_address || linked_address->next_ ||
linked_address->prev_;
}
void Simulator::GlobalMonitor::PrependProcessor_Locked(
LinkedAddress* linked_address) {
if (IsProcessorInLinkedList_Locked(linked_address)) {
return;
}
if (head_) {
head_->prev_ = linked_address;
}
linked_address->prev_ = nullptr;
linked_address->next_ = head_;
head_ = linked_address;
}
void Simulator::GlobalMonitor::RemoveLinkedAddress(
LinkedAddress* linked_address) {
base::MutexGuard lock_guard(&mutex);
if (!IsProcessorInLinkedList_Locked(linked_address)) {
return;
}
if (linked_address->prev_) {
linked_address->prev_->next_ = linked_address->next_;
} else {
head_ = linked_address->next_;
}
if (linked_address->next_) {
linked_address->next_->prev_ = linked_address->prev_;
}
linked_address->prev_ = nullptr;
linked_address->next_ = nullptr;
}
#undef UNSUPPORTED
} // namespace internal
......
......@@ -326,8 +326,12 @@ class Simulator : public SimulatorBase {
inline uint32_t ReadWU(int64_t addr, Instruction* instr);
inline int32_t ReadW(int64_t addr, Instruction* instr, TraceType t = WORD);
inline void WriteW(int64_t addr, int32_t value, Instruction* instr);
void WriteConditionalW(int64_t addr, int32_t value, Instruction* instr,
int32_t rt_reg);
inline int64_t Read2W(int64_t addr, Instruction* instr);
inline void Write2W(int64_t addr, int64_t value, Instruction* instr);
inline void WriteConditional2W(int64_t addr, int64_t value,
Instruction* instr, int32_t rt_reg);
inline double ReadD(int64_t addr, Instruction* instr);
inline void WriteD(int64_t addr, double value, Instruction* instr);
......@@ -575,6 +579,95 @@ class Simulator : public SimulatorBase {
char* desc;
};
StopCountAndDesc watched_stops_[kMaxStopCode + 1];
// Synchronization primitives.
enum class MonitorAccess {
Open,
RMW,
};
enum class TransactionSize {
None = 0,
Word = 4,
DoubleWord = 8,
};
// The least-significant bits of the address are ignored. The number of bits
// is implementation-defined, between 3 and minimum page size.
static const uintptr_t kExclusiveTaggedAddrMask = ~((1 << 3) - 1);
class LocalMonitor {
public:
LocalMonitor();
// These functions manage the state machine for the local monitor, but do
// not actually perform loads and stores. NotifyStoreConditional only
// returns true if the store conditional is allowed; the global monitor will
// still have to be checked to see whether the memory should be updated.
void NotifyLoad();
void NotifyLoadLinked(uintptr_t addr, TransactionSize size);
void NotifyStore();
bool NotifyStoreConditional(uintptr_t addr, TransactionSize size);
private:
void Clear();
MonitorAccess access_state_;
uintptr_t tagged_addr_;
TransactionSize size_;
};
class GlobalMonitor {
public:
GlobalMonitor();
class LinkedAddress {
public:
LinkedAddress();
private:
friend class GlobalMonitor;
// These functions manage the state machine for the global monitor, but do
// not actually perform loads and stores.
void Clear_Locked();
void NotifyLoadLinked_Locked(uintptr_t addr);
void NotifyStore_Locked();
bool NotifyStoreConditional_Locked(uintptr_t addr,
bool is_requesting_thread);
MonitorAccess access_state_;
uintptr_t tagged_addr_;
LinkedAddress* next_;
LinkedAddress* prev_;
// A scd can fail due to background cache evictions. Rather than
// simulating this, we'll just occasionally introduce cases where an
// store conditional fails. This will happen once after every
// kMaxFailureCounter exclusive stores.
static const int kMaxFailureCounter = 5;
int failure_counter_;
};
// Exposed so it can be accessed by Simulator::{Read,Write}Ex*.
base::Mutex mutex;
void NotifyLoadLinked_Locked(uintptr_t addr, LinkedAddress* linked_address);
void NotifyStore_Locked(LinkedAddress* linked_address);
bool NotifyStoreConditional_Locked(uintptr_t addr,
LinkedAddress* linked_address);
// Called when the simulator is destroyed.
void RemoveLinkedAddress(LinkedAddress* linked_address);
private:
bool IsProcessorInLinkedList_Locked(LinkedAddress* linked_address) const;
void PrependProcessor_Locked(LinkedAddress* linked_address);
LinkedAddress* head_;
};
LocalMonitor local_monitor_;
GlobalMonitor::LinkedAddress global_monitor_thread_;
static base::LazyInstance<GlobalMonitor>::type global_monitor_;
};
} // namespace internal
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment