Commit 66ae5f7d authored by binji's avatar binji Committed by Commit bot

Implement ldrex/strex instructions in ARM simulator

This CL implements ldrex, ldrexb, ldrexh, strex, strexb, and strexh in the
Simulator. These instructions provide "exclusive" access, which provides mutual
exclusion for concurrent threads of execution.

The ARM specification gives some leeway to implementors, but essentially
describes each processor as having Local Monitor and Global Monitor. The Local
Monitor is used to check the exclusivity state without having to synchronize
with other processors. The Global Monitor is shared between processors. We
model both to make it easier to match behavior with the spec.

When running with multiple OS threads, each thread has its own isolate, and
each isolate has its own Simulator. The Local Monitor is stored directly on the
Simulator, and the Global Monitor is stored as a lazy singleton. The Global
Monitor maintains a linked-list of all Simulators.

All loads/stores (even non-exclusive) are guarded by the Global Monitor's mutex.

BUG=v8:4614

Review-Url: https://codereview.chromium.org/2006183004
Cr-Commit-Position: refs/heads/master@{#42481}
parent 9662547c
This diff is collapsed.
......@@ -14,6 +14,8 @@
#define V8_ARM_SIMULATOR_ARM_H_
#include "src/allocation.h"
#include "src/base/lazy-instance.h"
#include "src/base/platform/mutex.h"
#if !defined(USE_SIMULATOR)
// Running without a simulator on a native arm platform.
......@@ -302,19 +304,27 @@ class Simulator {
void PrintStopInfo(uint32_t code);
// Read and write memory.
// The *Ex functions are exclusive access. The writes return the strex status:
// 0 if the write succeeds, and 1 if the write fails.
inline uint8_t ReadBU(int32_t addr);
inline int8_t ReadB(int32_t addr);
uint8_t ReadExBU(int32_t addr);
inline void WriteB(int32_t addr, uint8_t value);
inline void WriteB(int32_t addr, int8_t value);
int WriteExB(int32_t addr, uint8_t value);
inline uint16_t ReadHU(int32_t addr, Instruction* instr);
inline int16_t ReadH(int32_t addr, Instruction* instr);
uint16_t ReadExHU(int32_t addr, Instruction* instr);
// Note: Overloaded on the sign of the value.
inline void WriteH(int32_t addr, uint16_t value, Instruction* instr);
inline void WriteH(int32_t addr, int16_t value, Instruction* instr);
int WriteExH(int32_t addr, uint16_t value, Instruction* instr);
inline int ReadW(int32_t addr, Instruction* instr);
int ReadExW(int32_t addr, Instruction* instr);
inline void WriteW(int32_t addr, int value, Instruction* instr);
int WriteExW(int32_t addr, int value, Instruction* instr);
int32_t* ReadDW(int32_t addr);
void WriteDW(int32_t addr, int32_t value1, int32_t value2);
......@@ -437,6 +447,94 @@ class Simulator {
char* desc;
};
StopCountAndDesc watched_stops_[kNumOfWatchedStops];
// Syncronization primitives. See ARM DDI 0406C.b, A2.9.
enum class MonitorAccess {
Open,
Exclusive,
};
enum class TransactionSize {
None = 0,
Byte = 1,
HalfWord = 2,
Word = 4,
};
// The least-significant bits of the address are ignored. The number of bits
// is implementation-defined, between 3 and 11. See ARM DDI 0406C.b, A3.4.3.
static const int32_t kExclusiveTaggedAddrMask = ~((1 << 11) - 1);
class LocalMonitor {
public:
LocalMonitor();
// These functions manage the state machine for the local monitor, but do
// not actually perform loads and stores. NotifyStoreExcl only returns
// true if the exclusive store is allowed; the global monitor will still
// have to be checked to see whether the memory should be updated.
void NotifyLoad(int32_t addr);
void NotifyLoadExcl(int32_t addr, TransactionSize size);
void NotifyStore(int32_t addr);
bool NotifyStoreExcl(int32_t addr, TransactionSize size);
private:
void Clear();
MonitorAccess access_state_;
int32_t tagged_addr_;
TransactionSize size_;
};
class GlobalMonitor {
public:
GlobalMonitor();
class Processor {
public:
Processor();
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 NotifyLoadExcl_Locked(int32_t addr);
void NotifyStore_Locked(int32_t addr, bool is_requesting_processor);
bool NotifyStoreExcl_Locked(int32_t addr, bool is_requesting_processor);
MonitorAccess access_state_;
int32_t tagged_addr_;
Processor* next_;
Processor* prev_;
// A strex can fail due to background cache evictions. Rather than
// simulating this, we'll just occasionally introduce cases where an
// exclusive store 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 NotifyLoadExcl_Locked(int32_t addr, Processor* processor);
void NotifyStore_Locked(int32_t addr, Processor* processor);
bool NotifyStoreExcl_Locked(int32_t addr, Processor* processor);
// Called when the simulator is destroyed.
void RemoveProcessor(Processor* processor);
private:
bool IsProcessorInLinkedList_Locked(Processor* processor) const;
void PrependProcessor_Locked(Processor* processor);
Processor* head_;
};
LocalMonitor local_monitor_;
GlobalMonitor::Processor global_monitor_processor_;
static base::LazyInstance<GlobalMonitor>::type global_monitor_;
};
......
......@@ -207,6 +207,7 @@ v8_executable("cctest") {
"test-disasm-arm.cc",
"test-macro-assembler-arm.cc",
"test-run-wasm-relocation-arm.cc",
"test-simulator-arm.cc",
"wasm/test-run-wasm-simd.cc",
]
} else if (v8_current_cpu == "arm64") {
......
......@@ -248,6 +248,7 @@
'test-disasm-arm.cc',
'test-macro-assembler-arm.cc',
'test-run-wasm-relocation-arm.cc',
'test-simulator-arm.cc',
'wasm/test-run-wasm-simd-lowering.cc'
],
'cctest_sources_arm64': [ ### gcmole(arch:arm64) ###
......
This diff is collapsed.
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