зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1337084 - Tolerate multiple threads running JS in JIT simulators, r=jandem.
--HG-- extra : rebase_source : 63625feccd12838cda50fc3650de12c7b147275a
This commit is contained in:
Родитель
8287a9c71c
Коммит
18bc843966
|
@ -226,7 +226,7 @@ class ExecutableAllocator
|
|||
#elif defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
|
||||
static void cacheFlush(void* code, size_t size)
|
||||
{
|
||||
js::jit::Simulator::FlushICache(code, size);
|
||||
js::jit::SimulatorProcess::FlushICache(code, size);
|
||||
}
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
static void cacheFlush(void* code, size_t size)
|
||||
|
|
|
@ -365,28 +365,16 @@ class AutoLockSimulatorCache : public LockGuard<Mutex>
|
|||
using Base = LockGuard<Mutex>;
|
||||
|
||||
public:
|
||||
explicit AutoLockSimulatorCache(Simulator* sim)
|
||||
: Base(sim->cacheLock_)
|
||||
, sim_(sim)
|
||||
{
|
||||
MOZ_ASSERT(sim_->cacheLockHolder_.isNothing());
|
||||
#ifdef DEBUG
|
||||
sim_->cacheLockHolder_ = mozilla::Some(ThisThread::GetId());
|
||||
#endif
|
||||
}
|
||||
|
||||
~AutoLockSimulatorCache() {
|
||||
MOZ_ASSERT(sim_->cacheLockHolder_.isSome());
|
||||
#ifdef DEBUG
|
||||
sim_->cacheLockHolder_.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
Simulator* const sim_;
|
||||
explicit AutoLockSimulatorCache()
|
||||
: Base(SimulatorProcess::singleton_->cacheLock_)
|
||||
{}
|
||||
};
|
||||
|
||||
bool Simulator::ICacheCheckingEnabled = false;
|
||||
mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
|
||||
SimulatorProcess::ICacheCheckingDisableCount(1); // Checking is disabled by default.
|
||||
mozilla::Atomic<bool, mozilla::ReleaseAcquire>
|
||||
SimulatorProcess::cacheInvalidatedBySignalHandler_(false);
|
||||
SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
|
||||
|
||||
int64_t Simulator::StopSimAt = -1L;
|
||||
|
||||
|
@ -402,9 +390,6 @@ Simulator::Create(JSContext* cx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (getenv("ARM_SIM_ICACHE_CHECKS"))
|
||||
Simulator::ICacheCheckingEnabled = true;
|
||||
|
||||
char* stopAtStr = getenv("ARM_SIM_STOP_AT");
|
||||
int64_t stopAt;
|
||||
if (stopAtStr && sscanf(stopAtStr, "%lld", &stopAt) == 1) {
|
||||
|
@ -998,9 +983,9 @@ AllOnOnePage(uintptr_t start, int size)
|
|||
}
|
||||
|
||||
static CachePage*
|
||||
GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
|
||||
GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache, void* page)
|
||||
{
|
||||
Simulator::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
|
||||
SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
|
||||
if (p)
|
||||
return p->value();
|
||||
|
||||
|
@ -1014,7 +999,7 @@ GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
|
|||
|
||||
// Flush from start up to and not including start + size.
|
||||
static void
|
||||
FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
|
||||
FlushOnePageLocked(SimulatorProcess::ICacheMap& i_cache, intptr_t start, int size)
|
||||
{
|
||||
MOZ_ASSERT(size <= CachePage::kPageSize);
|
||||
MOZ_ASSERT(AllOnOnePage(start, size - 1));
|
||||
|
@ -1029,7 +1014,7 @@ FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
|
|||
}
|
||||
|
||||
static void
|
||||
FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
|
||||
FlushICacheLocked(SimulatorProcess::ICacheMap& i_cache, void* start_addr, size_t size)
|
||||
{
|
||||
intptr_t start = reinterpret_cast<intptr_t>(start_addr);
|
||||
int intra_line = (start & CachePage::kLineMask);
|
||||
|
@ -1049,14 +1034,14 @@ FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
|
|||
FlushOnePageLocked(i_cache, start, size);
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* instr)
|
||||
/* static */ void
|
||||
SimulatorProcess::checkICacheLocked(SimInstruction* instr)
|
||||
{
|
||||
intptr_t address = reinterpret_cast<intptr_t>(instr);
|
||||
void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
|
||||
void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
|
||||
int offset = (address & CachePage::kPageMask);
|
||||
CachePage* cache_page = GetCachePageLocked(i_cache, page);
|
||||
CachePage* cache_page = GetCachePageLocked(icache(), page);
|
||||
char* cache_valid_byte = cache_page->validityByte(offset);
|
||||
bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
|
||||
char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);
|
||||
|
@ -1074,7 +1059,7 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
|
|||
// It is safe for the signal to arrive during the !cache_hit path, since it
|
||||
// will be cleared the next time this function is called.
|
||||
if (cacheInvalidatedBySignalHandler_) {
|
||||
i_cache.clear();
|
||||
icache().clear();
|
||||
cacheInvalidatedBySignalHandler_ = false;
|
||||
return;
|
||||
}
|
||||
|
@ -1089,13 +1074,13 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
|
|||
}
|
||||
|
||||
HashNumber
|
||||
Simulator::ICacheHasher::hash(const Lookup& l)
|
||||
SimulatorProcess::ICacheHasher::hash(const Lookup& l)
|
||||
{
|
||||
return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(l)) >> 2;
|
||||
}
|
||||
|
||||
bool
|
||||
Simulator::ICacheHasher::match(const Key& k, const Lookup& l)
|
||||
SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l)
|
||||
{
|
||||
MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0);
|
||||
MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0);
|
||||
|
@ -1109,22 +1094,18 @@ Simulator::setLastDebuggerInput(char* input)
|
|||
lastDebuggerInput_ = input;
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::FlushICache(void* start_addr, size_t size)
|
||||
/* static */ void
|
||||
SimulatorProcess::FlushICache(void* start_addr, size_t size)
|
||||
{
|
||||
JitSpewCont(JitSpew_CacheFlush, "[%p %" PRIxSIZE "]", start_addr, size);
|
||||
if (Simulator::ICacheCheckingEnabled) {
|
||||
Simulator* sim = Simulator::Current();
|
||||
|
||||
AutoLockSimulatorCache als(sim);
|
||||
|
||||
js::jit::FlushICacheLocked(sim->icache(), start_addr, size);
|
||||
if (!ICacheCheckingDisableCount) {
|
||||
AutoLockSimulatorCache als;
|
||||
js::jit::FlushICacheLocked(icache(), start_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
Simulator::Simulator(JSContext* cx)
|
||||
: cx_(cx),
|
||||
cacheLock_(mutexid::SimulatorCacheLock)
|
||||
: cx_(cx)
|
||||
{
|
||||
// Set up simulator support first. Some of this information is needed to
|
||||
// setup the architecture state.
|
||||
|
@ -1142,7 +1123,6 @@ Simulator::Simulator(JSContext* cx)
|
|||
single_stepping_ = false;
|
||||
single_step_callback_ = nullptr;
|
||||
single_step_callback_arg_ = nullptr;
|
||||
cacheInvalidatedBySignalHandler_ = false;
|
||||
skipCalleeSavedRegsCheck = false;
|
||||
|
||||
// Set up architecture state.
|
||||
|
@ -1178,7 +1158,6 @@ Simulator::Simulator(JSContext* cx)
|
|||
|
||||
lastDebuggerInput_ = nullptr;
|
||||
|
||||
redirection_ = nullptr;
|
||||
exclusiveMonitorHeld_ = false;
|
||||
exclusiveMonitor_ = 0;
|
||||
}
|
||||
|
@ -1186,9 +1165,6 @@ Simulator::Simulator(JSContext* cx)
|
|||
bool
|
||||
Simulator::init()
|
||||
{
|
||||
if (!icache_.init())
|
||||
return false;
|
||||
|
||||
// Allocate 2MB for the stack. Note that we will only use 1MB, see below.
|
||||
static const size_t stackSize = 2 * 1024*1024;
|
||||
stack_ = reinterpret_cast<char*>(js_malloc(stackSize));
|
||||
|
@ -1215,19 +1191,21 @@ Simulator::init()
|
|||
// offset from the svc instruction so the simulator knows what to call.
|
||||
class Redirection
|
||||
{
|
||||
friend class Simulator;
|
||||
friend class SimulatorProcess;
|
||||
|
||||
// sim's lock must already be held.
|
||||
Redirection(void* nativeFunction, ABIFunctionType type, Simulator* sim)
|
||||
Redirection(void* nativeFunction, ABIFunctionType type)
|
||||
: nativeFunction_(nativeFunction),
|
||||
swiInstruction_(Assembler::AL | (0xf * (1 << 24)) | kCallRtRedirected),
|
||||
type_(type),
|
||||
next_(nullptr)
|
||||
{
|
||||
next_ = sim->redirection();
|
||||
if (Simulator::ICacheCheckingEnabled)
|
||||
FlushICacheLocked(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize);
|
||||
sim->setRedirection(this);
|
||||
next_ = SimulatorProcess::redirection();
|
||||
if (!SimulatorProcess::ICacheCheckingDisableCount) {
|
||||
FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
|
||||
SimInstruction::kInstrSize);
|
||||
}
|
||||
SimulatorProcess::setRedirection(this);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -1236,11 +1214,9 @@ class Redirection
|
|||
ABIFunctionType type() const { return type_; }
|
||||
|
||||
static Redirection* Get(void* nativeFunction, ABIFunctionType type) {
|
||||
Simulator* sim = Simulator::Current();
|
||||
AutoLockSimulatorCache als;
|
||||
|
||||
AutoLockSimulatorCache als(sim);
|
||||
|
||||
Redirection* current = sim->redirection();
|
||||
Redirection* current = SimulatorProcess::redirection();
|
||||
for (; current != nullptr; current = current->next_) {
|
||||
if (current->nativeFunction_ == nativeFunction) {
|
||||
MOZ_ASSERT(current->type() == type);
|
||||
|
@ -1252,7 +1228,7 @@ class Redirection
|
|||
Redirection* redir = (Redirection*)js_malloc(sizeof(Redirection));
|
||||
if (!redir)
|
||||
oomUnsafe.crash("Simulator redirection");
|
||||
new(redir) Redirection(nativeFunction, type, sim);
|
||||
new(redir) Redirection(nativeFunction, type);
|
||||
return redir;
|
||||
}
|
||||
|
||||
|
@ -1272,6 +1248,15 @@ class Redirection
|
|||
Simulator::~Simulator()
|
||||
{
|
||||
js_free(stack_);
|
||||
}
|
||||
|
||||
SimulatorProcess::SimulatorProcess()
|
||||
: cacheLock_(mutexid::SimulatorCacheLock)
|
||||
, redirection_(nullptr)
|
||||
{}
|
||||
|
||||
SimulatorProcess::~SimulatorProcess()
|
||||
{
|
||||
Redirection* r = redirection_;
|
||||
while (r) {
|
||||
Redirection* next = r->next_;
|
||||
|
@ -1280,6 +1265,15 @@ Simulator::~Simulator()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SimulatorProcess::init()
|
||||
{
|
||||
if (getenv("ARM_SIM_ICACHE_CHECKS"))
|
||||
ICacheCheckingDisableCount = 0;
|
||||
|
||||
return icache_.init();
|
||||
}
|
||||
|
||||
/* static */ void*
|
||||
Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type)
|
||||
{
|
||||
|
@ -4660,9 +4654,9 @@ Simulator::decodeSpecialCondition(SimInstruction* instr)
|
|||
void
|
||||
Simulator::instructionDecode(SimInstruction* instr)
|
||||
{
|
||||
if (Simulator::ICacheCheckingEnabled) {
|
||||
AutoLockSimulatorCache als(this);
|
||||
checkICacheLocked(icache(), instr);
|
||||
if (!SimulatorProcess::ICacheCheckingDisableCount) {
|
||||
AutoLockSimulatorCache als;
|
||||
SimulatorProcess::checkICacheLocked(instr);
|
||||
}
|
||||
|
||||
pc_modified_ = false;
|
||||
|
@ -4931,7 +4925,9 @@ Simulator::call(uint8_t* entry, int argument_count, ...)
|
|||
Simulator*
|
||||
Simulator::Current()
|
||||
{
|
||||
return TlsContext.get()->runtime()->unsafeContextFromAnyThread()->simulator();
|
||||
JSContext* cx = TlsContext.get();
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
return cx->simulator();
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
@ -71,11 +71,9 @@ const uint32_t kVFPRoundingModeMask = 3 << 22;
|
|||
typedef int32_t Instr;
|
||||
class SimInstruction;
|
||||
|
||||
// Per thread simulator state.
|
||||
class Simulator
|
||||
{
|
||||
friend class Redirection;
|
||||
friend class AutoLockSimulatorCache;
|
||||
|
||||
public:
|
||||
friend class ArmDebugger;
|
||||
enum Register {
|
||||
|
@ -347,30 +345,7 @@ class Simulator
|
|||
// Executes one instruction.
|
||||
void instructionDecode(SimInstruction* instr);
|
||||
|
||||
private:
|
||||
// ICache checking.
|
||||
struct ICacheHasher {
|
||||
typedef void* Key;
|
||||
typedef void* Lookup;
|
||||
static HashNumber hash(const Lookup& l);
|
||||
static bool match(const Key& k, const Lookup& l);
|
||||
};
|
||||
|
||||
public:
|
||||
typedef HashMap<void*, CachePage*, ICacheHasher, SystemAllocPolicy> ICacheMap;
|
||||
|
||||
public:
|
||||
static bool ICacheCheckingEnabled;
|
||||
static void FlushICache(void* start, size_t size);
|
||||
|
||||
// Jitcode may be rewritten from a signal handler, but is prevented from
|
||||
// calling FlushICache() because the signal may arrive within the critical
|
||||
// area of an AutoLockSimulatorCache. This flag instructs the Simulator
|
||||
// to remove all cache entries the next time it checks, avoiding false negatives.
|
||||
mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
|
||||
|
||||
void checkICacheLocked(ICacheMap& i_cache, SimInstruction* instr);
|
||||
|
||||
static int64_t StopSimAt;
|
||||
|
||||
// For testing the MoveResolver code, a MoveResolver is set up, and
|
||||
|
@ -472,37 +447,6 @@ class Simulator
|
|||
return icount_;
|
||||
}
|
||||
|
||||
private:
|
||||
// This lock creates a critical section around 'redirection_' and
|
||||
// 'icache_', which are referenced both by the execution engine
|
||||
// and by the off-thread compiler (see Redirection::Get in the cpp file).
|
||||
Mutex cacheLock_;
|
||||
#ifdef DEBUG
|
||||
mozilla::Maybe<Thread::Id> cacheLockHolder_;
|
||||
#endif
|
||||
|
||||
Redirection* redirection_;
|
||||
ICacheMap icache_;
|
||||
|
||||
public:
|
||||
ICacheMap& icache() {
|
||||
// Technically we need the lock to access the innards of the
|
||||
// icache, not to take its address, but the latter condition
|
||||
// serves as a useful complement to the former.
|
||||
MOZ_ASSERT(cacheLockHolder_.isSome());
|
||||
return icache_;
|
||||
}
|
||||
|
||||
Redirection* redirection() const {
|
||||
MOZ_ASSERT(cacheLockHolder_.isSome());
|
||||
return redirection_;
|
||||
}
|
||||
|
||||
void setRedirection(js::jit::Redirection* redirection) {
|
||||
MOZ_ASSERT(cacheLockHolder_.isSome());
|
||||
redirection_ = redirection;
|
||||
}
|
||||
|
||||
private:
|
||||
// Exclusive access monitor
|
||||
void exclusiveMonitorSet(uint64_t value);
|
||||
|
@ -513,7 +457,81 @@ class Simulator
|
|||
uint64_t exclusiveMonitor_;
|
||||
};
|
||||
|
||||
#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \
|
||||
// Process wide simulator state.
|
||||
class SimulatorProcess
|
||||
{
|
||||
friend class Redirection;
|
||||
friend class AutoLockSimulatorCache;
|
||||
|
||||
private:
|
||||
// ICache checking.
|
||||
struct ICacheHasher {
|
||||
typedef void* Key;
|
||||
typedef void* Lookup;
|
||||
static HashNumber hash(const Lookup& l);
|
||||
static bool match(const Key& k, const Lookup& l);
|
||||
};
|
||||
|
||||
public:
|
||||
typedef HashMap<void*, CachePage*, ICacheHasher, SystemAllocPolicy> ICacheMap;
|
||||
|
||||
static mozilla::Atomic<size_t, mozilla::ReleaseAcquire> ICacheCheckingDisableCount;
|
||||
static void FlushICache(void* start, size_t size);
|
||||
|
||||
// Jitcode may be rewritten from a signal handler, but is prevented from
|
||||
// calling FlushICache() because the signal may arrive within the critical
|
||||
// area of an AutoLockSimulatorCache. This flag instructs the Simulator
|
||||
// to remove all cache entries the next time it checks, avoiding false negatives.
|
||||
static mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
|
||||
|
||||
static void checkICacheLocked(SimInstruction* instr);
|
||||
|
||||
static bool initialize() {
|
||||
singleton_ = js_new<SimulatorProcess>();
|
||||
return singleton_ && singleton_->init();
|
||||
}
|
||||
static void destroy() {
|
||||
js_delete(singleton_);
|
||||
singleton_ = nullptr;
|
||||
}
|
||||
|
||||
SimulatorProcess();
|
||||
~SimulatorProcess();
|
||||
|
||||
private:
|
||||
bool init();
|
||||
|
||||
static SimulatorProcess* singleton_;
|
||||
|
||||
// This lock creates a critical section around 'redirection_' and
|
||||
// 'icache_', which are referenced both by the execution engine
|
||||
// and by the off-thread compiler (see Redirection::Get in the cpp file).
|
||||
Mutex cacheLock_;
|
||||
|
||||
Redirection* redirection_;
|
||||
ICacheMap icache_;
|
||||
|
||||
public:
|
||||
static ICacheMap& icache() {
|
||||
// Technically we need the lock to access the innards of the
|
||||
// icache, not to take its address, but the latter condition
|
||||
// serves as a useful complement to the former.
|
||||
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
|
||||
return singleton_->icache_;
|
||||
}
|
||||
|
||||
static Redirection* redirection() {
|
||||
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
|
||||
return singleton_->redirection_;
|
||||
}
|
||||
|
||||
static void setRedirection(js::jit::Redirection* redirection) {
|
||||
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
|
||||
singleton_->redirection_ = redirection;
|
||||
}
|
||||
};
|
||||
|
||||
#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (cx->simulator()->overRecursedWithExtra(extra)) { \
|
||||
js::ReportOverRecursed(cx); \
|
||||
|
|
|
@ -32,11 +32,14 @@
|
|||
#include "threading/LockGuard.h"
|
||||
#include "vm/Runtime.h"
|
||||
|
||||
js::jit::SimulatorProcess* js::jit::SimulatorProcess::singleton_ = nullptr;
|
||||
|
||||
namespace vixl {
|
||||
|
||||
|
||||
using mozilla::DebugOnly;
|
||||
using js::jit::ABIFunctionType;
|
||||
using js::jit::SimulatorProcess;
|
||||
|
||||
Simulator::Simulator(Decoder* decoder, FILE* stream)
|
||||
: stream_(nullptr)
|
||||
|
@ -46,7 +49,6 @@ Simulator::Simulator(Decoder* decoder, FILE* stream)
|
|||
, stack_limit_(nullptr)
|
||||
, decoder_(nullptr)
|
||||
, oom_(false)
|
||||
, lock_(js::mutexid::Arm64SimulatorLock)
|
||||
{
|
||||
this->init(decoder, stream);
|
||||
}
|
||||
|
@ -144,13 +146,13 @@ void Simulator::init(Decoder* decoder, FILE* stream) {
|
|||
// time they are encountered. This warning can be silenced using
|
||||
// SilenceExclusiveAccessWarning().
|
||||
print_exclusive_access_warning_ = true;
|
||||
|
||||
redirection_ = nullptr;
|
||||
}
|
||||
|
||||
|
||||
Simulator* Simulator::Current() {
|
||||
return js::TlsContext.get()->simulator();
|
||||
JSContext* cx = js::TlsContext.get();
|
||||
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
return cx->simulator();
|
||||
}
|
||||
|
||||
|
||||
|
@ -294,8 +296,8 @@ class AutoLockSimulatorCache : public js::LockGuard<js::Mutex>
|
|||
using Base = js::LockGuard<js::Mutex>;
|
||||
|
||||
public:
|
||||
explicit AutoLockSimulatorCache(Simulator* sim)
|
||||
: Base(sim->lock_)
|
||||
explicit AutoLockSimulatorCache()
|
||||
: Base(SimulatorProcess::singleton_->lock_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -311,14 +313,14 @@ class Redirection
|
|||
{
|
||||
friend class Simulator;
|
||||
|
||||
Redirection(void* nativeFunction, ABIFunctionType type, Simulator* sim)
|
||||
Redirection(void* nativeFunction, ABIFunctionType type)
|
||||
: nativeFunction_(nativeFunction),
|
||||
type_(type),
|
||||
next_(nullptr)
|
||||
{
|
||||
next_ = sim->redirection();
|
||||
next_ = SimulatorProcess::redirection();
|
||||
// TODO: Flush ICache?
|
||||
sim->setRedirection(this);
|
||||
SimulatorProcess::setRedirection(this);
|
||||
|
||||
Instruction* instr = (Instruction*)(&svcInstruction_);
|
||||
vixl::Assembler::svc(instr, kCallRtRedirected);
|
||||
|
@ -330,13 +332,12 @@ class Redirection
|
|||
ABIFunctionType type() const { return type_; }
|
||||
|
||||
static Redirection* Get(void* nativeFunction, ABIFunctionType type) {
|
||||
Simulator* sim = Simulator::Current();
|
||||
AutoLockSimulatorCache alsr(sim);
|
||||
AutoLockSimulatorCache alsr;
|
||||
|
||||
// TODO: Store srt_ in the simulator for this assertion.
|
||||
// VIXL_ASSERT_IF(pt->simulator(), pt->simulator()->srt_ == srt);
|
||||
|
||||
Redirection* current = sim->redirection();
|
||||
Redirection* current = SimulatorProcess::redirection();
|
||||
for (; current != nullptr; current = current->next_) {
|
||||
if (current->nativeFunction_ == nativeFunction) {
|
||||
VIXL_ASSERT(current->type() == type);
|
||||
|
@ -348,7 +349,7 @@ class Redirection
|
|||
Redirection* redir = (Redirection*)js_malloc(sizeof(Redirection));
|
||||
if (!redir)
|
||||
oomUnsafe.crash("Simulator redirection");
|
||||
new(redir) Redirection(nativeFunction, type, sim);
|
||||
new(redir) Redirection(nativeFunction, type);
|
||||
return redir;
|
||||
}
|
||||
|
||||
|
@ -366,14 +367,6 @@ class Redirection
|
|||
};
|
||||
|
||||
|
||||
void Simulator::setRedirection(Redirection* redirection) {
|
||||
redirection_ = redirection;
|
||||
}
|
||||
|
||||
|
||||
Redirection* Simulator::redirection() const {
|
||||
return redirection_;
|
||||
}
|
||||
|
||||
|
||||
void* Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type) {
|
||||
|
|
|
@ -704,8 +704,6 @@ class SimExclusiveGlobalMonitor {
|
|||
class Redirection;
|
||||
|
||||
class Simulator : public DecoderVisitor {
|
||||
friend class AutoLockSimulatorCache;
|
||||
|
||||
public:
|
||||
explicit Simulator(Decoder* decoder, FILE* stream = stdout);
|
||||
~Simulator();
|
||||
|
@ -720,8 +718,6 @@ class Simulator : public DecoderVisitor {
|
|||
bool overRecursed(uintptr_t newsp = 0) const;
|
||||
bool overRecursedWithExtra(uint32_t extra) const;
|
||||
int64_t call(uint8_t* entry, int argument_count, ...);
|
||||
void setRedirection(Redirection* redirection);
|
||||
Redirection* redirection() const;
|
||||
static void* RedirectNativeFunction(void* nativeFunction, js::jit::ABIFunctionType type);
|
||||
void setGPR32Result(int32_t result);
|
||||
void setGPR64Result(int64_t result);
|
||||
|
@ -2666,12 +2662,50 @@ class Simulator : public DecoderVisitor {
|
|||
bool oom() const { return oom_; }
|
||||
|
||||
protected:
|
||||
// Moz: Synchronizes access between main thread and compilation threads.
|
||||
js::Mutex lock_;
|
||||
Redirection* redirection_;
|
||||
mozilla::Vector<int64_t, 0, js::SystemAllocPolicy> spStack_;
|
||||
};
|
||||
|
||||
} // namespace vixl
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class SimulatorProcess
|
||||
{
|
||||
public:
|
||||
static SimulatorProcess* singleton_;
|
||||
|
||||
SimulatorProcess()
|
||||
: lock_(mutexid::Arm64SimulatorLock)
|
||||
, redirection_(nullptr)
|
||||
{}
|
||||
|
||||
// Synchronizes access between main thread and compilation threads.
|
||||
js::Mutex lock_;
|
||||
vixl::Redirection* redirection_;
|
||||
|
||||
static void setRedirection(vixl::Redirection* redirection) {
|
||||
MOZ_ASSERT(singleton_->lock_.ownedByCurrentThread());
|
||||
singleton_->redirection_ = redirection;
|
||||
}
|
||||
|
||||
static vixl::Redirection* redirection() {
|
||||
MOZ_ASSERT(singleton_->lock_.ownedByCurrentThread());
|
||||
return singleton_->redirection_;
|
||||
}
|
||||
|
||||
static bool initialize() {
|
||||
singleton_ = js_new<SimulatorProcess>();
|
||||
return !!singleton_;
|
||||
}
|
||||
static void destroy() {
|
||||
js_delete(singleton_);
|
||||
singleton_ = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif // JS_SIMULATOR_ARM64
|
||||
#endif // VIXL_A64_SIMULATOR_A64_H_
|
||||
|
|
|
@ -499,28 +499,16 @@ class AutoLockSimulatorCache : public LockGuard<Mutex>
|
|||
using Base = LockGuard<Mutex>;
|
||||
|
||||
public:
|
||||
explicit AutoLockSimulatorCache(Simulator* sim)
|
||||
: Base(sim->cacheLock_)
|
||||
, sim_(sim)
|
||||
{
|
||||
MOZ_ASSERT(sim_->cacheLockHolder_.isNothing());
|
||||
#ifdef DEBUG
|
||||
sim_->cacheLockHolder_ = mozilla::Some(ThisThread::GetId());
|
||||
#endif
|
||||
}
|
||||
|
||||
~AutoLockSimulatorCache() {
|
||||
MOZ_ASSERT(sim_->cacheLockHolder_.isSome());
|
||||
#ifdef DEBUG
|
||||
sim_->cacheLockHolder_.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
Simulator* const sim_;
|
||||
AutoLockSimulatorCache()
|
||||
: Base(SimulatorProcess::singleton_->cacheLock_)
|
||||
{}
|
||||
};
|
||||
|
||||
bool Simulator::ICacheCheckingEnabled = false;
|
||||
mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
|
||||
SimulatorProcess::ICacheCheckingDisableCount(1); // Checking is disabled by default.
|
||||
mozilla::Atomic<bool, mozilla::ReleaseAcquire>
|
||||
SimulatorProcess::cacheInvalidatedBySignalHandler_(false);
|
||||
SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
|
||||
|
||||
int Simulator::StopSimAt = -1;
|
||||
|
||||
|
@ -536,9 +524,6 @@ Simulator::Create(JSContext* cx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (getenv("MIPS_SIM_ICACHE_CHECKS"))
|
||||
Simulator::ICacheCheckingEnabled = true;
|
||||
|
||||
char* stopAtStr = getenv("MIPS_SIM_STOP_AT");
|
||||
int64_t stopAt;
|
||||
if (stopAtStr && sscanf(stopAtStr, "%lld", &stopAt) == 1) {
|
||||
|
@ -1151,9 +1136,9 @@ Simulator::setLastDebuggerInput(char* input)
|
|||
}
|
||||
|
||||
static CachePage*
|
||||
GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
|
||||
GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache, void* page)
|
||||
{
|
||||
Simulator::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
|
||||
SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
|
||||
if (p)
|
||||
return p->value();
|
||||
|
||||
|
@ -1165,7 +1150,7 @@ GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
|
|||
|
||||
// Flush from start up to and not including start + size.
|
||||
static void
|
||||
FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
|
||||
FlushOnePageLocked(SimulatorProcess::ICacheMap& i_cache, intptr_t start, int size)
|
||||
{
|
||||
MOZ_ASSERT(size <= CachePage::kPageSize);
|
||||
MOZ_ASSERT(AllOnOnePage(start, size - 1));
|
||||
|
@ -1179,7 +1164,7 @@ FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
|
|||
}
|
||||
|
||||
static void
|
||||
FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
|
||||
FlushICacheLocked(SimulatorProcess::ICacheMap& i_cache, void* start_addr, size_t size)
|
||||
{
|
||||
intptr_t start = reinterpret_cast<intptr_t>(start_addr);
|
||||
int intra_line = (start & CachePage::kLineMask);
|
||||
|
@ -1200,14 +1185,14 @@ FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* instr)
|
||||
/* static */ void
|
||||
SimulatorProcess::checkICacheLocked(SimInstruction* instr)
|
||||
{
|
||||
intptr_t address = reinterpret_cast<intptr_t>(instr);
|
||||
void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
|
||||
void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
|
||||
int offset = (address & CachePage::kPageMask);
|
||||
CachePage* cache_page = GetCachePageLocked(i_cache, page);
|
||||
CachePage* cache_page = GetCachePageLocked(icache(), page);
|
||||
char* cache_valid_byte = cache_page->validityByte(offset);
|
||||
bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
|
||||
char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);
|
||||
|
@ -1225,7 +1210,7 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
|
|||
// It is safe for the signal to arrive during the !cache_hit path, since it
|
||||
// will be cleared the next time this function is called.
|
||||
if (cacheInvalidatedBySignalHandler_) {
|
||||
i_cache.clear();
|
||||
icache().clear();
|
||||
cacheInvalidatedBySignalHandler_ = false;
|
||||
return;
|
||||
}
|
||||
|
@ -1240,32 +1225,29 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
|
|||
}
|
||||
|
||||
HashNumber
|
||||
Simulator::ICacheHasher::hash(const Lookup& l)
|
||||
SimulatorProcess::ICacheHasher::hash(const Lookup& l)
|
||||
{
|
||||
return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(l)) >> 2;
|
||||
}
|
||||
|
||||
bool
|
||||
Simulator::ICacheHasher::match(const Key& k, const Lookup& l)
|
||||
SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l)
|
||||
{
|
||||
MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0);
|
||||
MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0);
|
||||
return k == l;
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::FlushICache(void* start_addr, size_t size)
|
||||
/* static */ void
|
||||
SimulatorProcess::FlushICache(void* start_addr, size_t size)
|
||||
{
|
||||
if (Simulator::ICacheCheckingEnabled) {
|
||||
Simulator* sim = Simulator::Current();
|
||||
AutoLockSimulatorCache als(sim);
|
||||
js::jit::FlushICacheLocked(sim->icache(), start_addr, size);
|
||||
if (!ICacheCheckingDisableCount) {
|
||||
AutoLockSimulatorCache als;
|
||||
js::jit::FlushICacheLocked(icache(), start_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
Simulator::Simulator()
|
||||
: cacheLock_(mutexid::SimulatorCacheLock),
|
||||
cacheInvalidatedBySignalHandler_(false)
|
||||
{
|
||||
// Set up simulator support first. Some of this information is needed to
|
||||
// setup the architecture state.
|
||||
|
@ -1301,16 +1283,11 @@ Simulator::Simulator()
|
|||
exceptions[i] = 0;
|
||||
|
||||
lastDebuggerInput_ = nullptr;
|
||||
|
||||
redirection_ = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
Simulator::init()
|
||||
{
|
||||
if (!icache_.init())
|
||||
return false;
|
||||
|
||||
// Allocate 2MB for the stack. Note that we will only use 1MB, see below.
|
||||
static const size_t stackSize = 2 * 1024 * 1024;
|
||||
stack_ = static_cast<char*>(js_malloc(stackSize));
|
||||
|
@ -1338,19 +1315,21 @@ Simulator::init()
|
|||
// offset from the swi instruction so the simulator knows what to call.
|
||||
class Redirection
|
||||
{
|
||||
friend class Simulator;
|
||||
friend class SimulatorProcess;
|
||||
|
||||
// sim's lock must already be held.
|
||||
Redirection(void* nativeFunction, ABIFunctionType type, Simulator* sim)
|
||||
Redirection(void* nativeFunction, ABIFunctionType type)
|
||||
: nativeFunction_(nativeFunction),
|
||||
swiInstruction_(kCallRedirInstr),
|
||||
type_(type),
|
||||
next_(nullptr)
|
||||
{
|
||||
next_ = sim->redirection();
|
||||
if (Simulator::ICacheCheckingEnabled)
|
||||
FlushICacheLocked(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize);
|
||||
sim->setRedirection(this);
|
||||
next_ = SimulatorProcess::redirection();
|
||||
if (!SimulatorProcess::ICacheCheckingDisableCount) {
|
||||
FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
|
||||
SimInstruction::kInstrSize);
|
||||
}
|
||||
SimulatorProcess::setRedirection(this);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -1359,11 +1338,9 @@ class Redirection
|
|||
ABIFunctionType type() const { return type_; }
|
||||
|
||||
static Redirection* Get(void* nativeFunction, ABIFunctionType type) {
|
||||
Simulator* sim = Simulator::Current();
|
||||
AutoLockSimulatorCache als;
|
||||
|
||||
AutoLockSimulatorCache als(sim);
|
||||
|
||||
Redirection* current = sim->redirection();
|
||||
Redirection* current = SimulatorProcess::redirection();
|
||||
for (; current != nullptr; current = current->next_) {
|
||||
if (current->nativeFunction_ == nativeFunction) {
|
||||
MOZ_ASSERT(current->type() == type);
|
||||
|
@ -1377,7 +1354,7 @@ class Redirection
|
|||
__FILE__, __LINE__);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
new(redir) Redirection(nativeFunction, type, sim);
|
||||
new(redir) Redirection(nativeFunction, type);
|
||||
return redir;
|
||||
}
|
||||
|
||||
|
@ -1397,6 +1374,15 @@ class Redirection
|
|||
Simulator::~Simulator()
|
||||
{
|
||||
js_free(stack_);
|
||||
}
|
||||
|
||||
SimulatorProcess::SimulatorProcess()
|
||||
: cacheLock_(mutexid::SimulatorCacheLock)
|
||||
, redirection_(nullptr)
|
||||
{}
|
||||
|
||||
SimulatorProcess::~SimulatorProcess()
|
||||
{
|
||||
Redirection* r = redirection_;
|
||||
while (r) {
|
||||
Redirection* next = r->next_;
|
||||
|
@ -1405,6 +1391,15 @@ Simulator::~Simulator()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SimulatorProcess::init()
|
||||
{
|
||||
if (getenv("MIPS_SIM_ICACHE_CHECKS"))
|
||||
ICacheCheckingDisableCount = 0;
|
||||
|
||||
return icache_.init();
|
||||
}
|
||||
|
||||
/* static */ void*
|
||||
Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type)
|
||||
{
|
||||
|
@ -1416,7 +1411,9 @@ Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type)
|
|||
Simulator*
|
||||
Simulator::Current()
|
||||
{
|
||||
return TlsContext.get()->runtime()->unsafeContextFromAnyThread()->simulator();
|
||||
JSContext* cx = TlsContext.get();
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
return cx->simulator();
|
||||
}
|
||||
|
||||
// Sets the register in the architecture state. It will also deal with updating
|
||||
|
@ -3360,9 +3357,9 @@ Simulator::decodeTypeJump(SimInstruction* instr)
|
|||
void
|
||||
Simulator::instructionDecode(SimInstruction* instr)
|
||||
{
|
||||
if (Simulator::ICacheCheckingEnabled) {
|
||||
AutoLockSimulatorCache als(this);
|
||||
checkICacheLocked(icache(), instr);
|
||||
if (!SimulatorProcess::ICacheCheckingDisableCount) {
|
||||
AutoLockSimulatorCache als;
|
||||
SimulatorProcess::checkICacheLocked(instr);
|
||||
}
|
||||
pc_modified_ = false;
|
||||
|
||||
|
|
|
@ -104,10 +104,9 @@ const uint32_t kMaxStopCode = 127;
|
|||
typedef uint32_t Instr;
|
||||
class SimInstruction;
|
||||
|
||||
// Per thread simulator state.
|
||||
class Simulator {
|
||||
friend class Redirection;
|
||||
friend class MipsDebugger;
|
||||
friend class AutoLockSimulatorCache;
|
||||
public:
|
||||
|
||||
// Registers are declared in order. See "See MIPS Run Linux" chapter 2.
|
||||
|
@ -214,8 +213,6 @@ class Simulator {
|
|||
// Debugger input.
|
||||
void setLastDebuggerInput(char* input);
|
||||
char* lastDebuggerInput() { return lastDebuggerInput_; }
|
||||
// ICache checking.
|
||||
static void FlushICache(void* start, size_t size);
|
||||
|
||||
// Returns true if pc register contains one of the 'SpecialValues' defined
|
||||
// below (bad_ra, end_sim_pc).
|
||||
|
@ -365,6 +362,13 @@ class Simulator {
|
|||
char* desc_;
|
||||
};
|
||||
StopCountAndDesc watchedStops_[kNumOfWatchedStops];
|
||||
};
|
||||
|
||||
// Process wide simulator state.
|
||||
class SimulatorProcess
|
||||
{
|
||||
friend class Redirection;
|
||||
friend class AutoLockSimulatorCache;
|
||||
|
||||
private:
|
||||
// ICache checking.
|
||||
|
@ -378,44 +382,59 @@ class Simulator {
|
|||
public:
|
||||
typedef HashMap<void*, CachePage*, ICacheHasher, SystemAllocPolicy> ICacheMap;
|
||||
|
||||
private:
|
||||
// This lock creates a critical section around 'redirection_' and
|
||||
// 'icache_', which are referenced both by the execution engine
|
||||
// and by the off-thread compiler (see Redirection::Get in the cpp file).
|
||||
Mutex cacheLock_;
|
||||
#ifdef DEBUG
|
||||
mozilla::Maybe<Thread::Id> cacheLockHolder_;
|
||||
#endif
|
||||
static mozilla::Atomic<size_t, mozilla::ReleaseAcquire> ICacheCheckingDisableCount;
|
||||
static void FlushICache(void* start, size_t size);
|
||||
|
||||
Redirection* redirection_;
|
||||
ICacheMap icache_;
|
||||
|
||||
private:
|
||||
// Jitcode may be rewritten from a signal handler, but is prevented from
|
||||
// calling FlushICache() because the signal may arrive within the critical
|
||||
// area of an AutoLockSimulatorCache. This flag instructs the Simulator
|
||||
// to remove all cache entries the next time it checks, avoiding false negatives.
|
||||
mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
|
||||
static mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
|
||||
|
||||
void checkICacheLocked(ICacheMap& i_cache, SimInstruction* instr);
|
||||
static void checkICacheLocked(SimInstruction* instr);
|
||||
|
||||
static bool initialize() {
|
||||
singleton_ = js_new<SimulatorProcess>();
|
||||
return singleton_ && singleton_->init();
|
||||
}
|
||||
static void destroy() {
|
||||
js_delete(singleton_);
|
||||
singleton_ = nullptr;
|
||||
}
|
||||
|
||||
SimulatorProcess();
|
||||
~SimulatorProcess();
|
||||
|
||||
private:
|
||||
bool init();
|
||||
|
||||
static SimulatorProcess* singleton_;
|
||||
|
||||
// This lock creates a critical section around 'redirection_' and
|
||||
// 'icache_', which are referenced both by the execution engine
|
||||
// and by the off-thread compiler (see Redirection::Get in the cpp file).
|
||||
Mutex cacheLock_;
|
||||
|
||||
Redirection* redirection_;
|
||||
ICacheMap icache_;
|
||||
|
||||
public:
|
||||
ICacheMap& icache() {
|
||||
static ICacheMap& icache() {
|
||||
// Technically we need the lock to access the innards of the
|
||||
// icache, not to take its address, but the latter condition
|
||||
// serves as a useful complement to the former.
|
||||
MOZ_ASSERT(cacheLockHolder_.isSome());
|
||||
return icache_;
|
||||
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
|
||||
return singleton_->icache_;
|
||||
}
|
||||
|
||||
Redirection* redirection() const {
|
||||
MOZ_ASSERT(cacheLockHolder_.isSome());
|
||||
return redirection_;
|
||||
static Redirection* redirection() {
|
||||
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
|
||||
return singleton_->redirection_;
|
||||
}
|
||||
|
||||
void setRedirection(js::jit::Redirection* redirection) {
|
||||
MOZ_ASSERT(cacheLockHolder_.isSome());
|
||||
redirection_ = redirection;
|
||||
static void setRedirection(js::jit::Redirection* redirection) {
|
||||
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
|
||||
singleton_->redirection_ = redirection;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -531,28 +531,16 @@ class AutoLockSimulatorCache : public LockGuard<Mutex>
|
|||
using Base = LockGuard<Mutex>;
|
||||
|
||||
public:
|
||||
explicit AutoLockSimulatorCache(Simulator* sim)
|
||||
: Base(sim->cacheLock_)
|
||||
, sim_(sim)
|
||||
{
|
||||
MOZ_ASSERT(sim_->cacheLockHolder_.isNothing());
|
||||
#ifdef DEBUG
|
||||
sim_->cacheLockHolder_ = mozilla::Some(ThisThread::GetId());
|
||||
#endif
|
||||
}
|
||||
|
||||
~AutoLockSimulatorCache() {
|
||||
MOZ_ASSERT(sim_->cacheLockHolder_.isSome());
|
||||
#ifdef DEBUG
|
||||
sim_->cacheLockHolder_.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
Simulator* const sim_;
|
||||
explicit AutoLockSimulatorCache()
|
||||
: Base(SimulatorProcess::singleton_->cacheLock_)
|
||||
{}
|
||||
};
|
||||
|
||||
bool Simulator::ICacheCheckingEnabled = false;
|
||||
mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
|
||||
SimulatorProcess::ICacheCheckingDisableCount(1); // Checking is disabled by default.
|
||||
mozilla::Atomic<bool, mozilla::ReleaseAcquire>
|
||||
SimulatorProcess::cacheInvalidatedBySignalHandler_(false);
|
||||
SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
|
||||
|
||||
int64_t Simulator::StopSimAt = -1;
|
||||
|
||||
|
@ -568,9 +556,6 @@ Simulator::Create(JSContext* cx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (getenv("MIPS_SIM_ICACHE_CHECKS"))
|
||||
Simulator::ICacheCheckingEnabled = true;
|
||||
|
||||
int64_t stopAt;
|
||||
char* stopAtStr = getenv("MIPS_SIM_STOP_AT");
|
||||
if (stopAtStr && sscanf(stopAtStr, "%" PRIi64, &stopAt) == 1) {
|
||||
|
@ -1159,9 +1144,9 @@ Simulator::setLastDebuggerInput(char* input)
|
|||
}
|
||||
|
||||
static CachePage*
|
||||
GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
|
||||
GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache, void* page)
|
||||
{
|
||||
Simulator::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
|
||||
SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
|
||||
if (p)
|
||||
return p->value();
|
||||
|
||||
|
@ -1173,7 +1158,7 @@ GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
|
|||
|
||||
// Flush from start up to and not including start + size.
|
||||
static void
|
||||
FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
|
||||
FlushOnePageLocked(SimulatorProcess::ICacheMap& i_cache, intptr_t start, int size)
|
||||
{
|
||||
MOZ_ASSERT(size <= CachePage::kPageSize);
|
||||
MOZ_ASSERT(AllOnOnePage(start, size - 1));
|
||||
|
@ -1187,7 +1172,7 @@ FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
|
|||
}
|
||||
|
||||
static void
|
||||
FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
|
||||
FlushICacheLocked(SimulatorProcess::ICacheMap& i_cache, void* start_addr, size_t size)
|
||||
{
|
||||
intptr_t start = reinterpret_cast<intptr_t>(start_addr);
|
||||
int intra_line = (start & CachePage::kLineMask);
|
||||
|
@ -1207,14 +1192,14 @@ FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
|
|||
FlushOnePageLocked(i_cache, start, size);
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* instr)
|
||||
/* static */ void
|
||||
SimulatorProcess::checkICacheLocked(SimInstruction* instr)
|
||||
{
|
||||
intptr_t address = reinterpret_cast<intptr_t>(instr);
|
||||
void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
|
||||
void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
|
||||
int offset = (address & CachePage::kPageMask);
|
||||
CachePage* cache_page = GetCachePageLocked(i_cache, page);
|
||||
CachePage* cache_page = GetCachePageLocked(icache(), page);
|
||||
char* cache_valid_byte = cache_page->validityByte(offset);
|
||||
bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
|
||||
char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);
|
||||
|
@ -1232,7 +1217,7 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
|
|||
// It is safe for the signal to arrive during the !cache_hit path, since it
|
||||
// will be cleared the next time this function is called.
|
||||
if (cacheInvalidatedBySignalHandler_) {
|
||||
i_cache.clear();
|
||||
icache().clear();
|
||||
cacheInvalidatedBySignalHandler_ = false;
|
||||
return;
|
||||
}
|
||||
|
@ -1247,32 +1232,29 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
|
|||
}
|
||||
|
||||
HashNumber
|
||||
Simulator::ICacheHasher::hash(const Lookup& l)
|
||||
SimulatorProcess::ICacheHasher::hash(const Lookup& l)
|
||||
{
|
||||
return U32(reinterpret_cast<uintptr_t>(l)) >> 2;
|
||||
}
|
||||
|
||||
bool
|
||||
Simulator::ICacheHasher::match(const Key& k, const Lookup& l)
|
||||
SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l)
|
||||
{
|
||||
MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0);
|
||||
MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0);
|
||||
return k == l;
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::FlushICache(void* start_addr, size_t size)
|
||||
/* static */ void
|
||||
SimulatorProcess::FlushICache(void* start_addr, size_t size)
|
||||
{
|
||||
if (Simulator::ICacheCheckingEnabled) {
|
||||
Simulator* sim = Simulator::Current();
|
||||
AutoLockSimulatorCache als(sim);
|
||||
js::jit::FlushICacheLocked(sim->icache(), start_addr, size);
|
||||
if (!ICacheCheckingDisableCount) {
|
||||
AutoLockSimulatorCache als;
|
||||
js::jit::FlushICacheLocked(icache(), start_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
Simulator::Simulator()
|
||||
: cacheLock_(mutexid::SimulatorCacheLock),
|
||||
cacheInvalidatedBySignalHandler_(false)
|
||||
{
|
||||
// Set up simulator support first. Some of this information is needed to
|
||||
// setup the architecture state.
|
||||
|
@ -1309,16 +1291,11 @@ Simulator::Simulator()
|
|||
exceptions[i] = 0;
|
||||
|
||||
lastDebuggerInput_ = nullptr;
|
||||
|
||||
redirection_ = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
Simulator::init()
|
||||
{
|
||||
if (!icache_.init())
|
||||
return false;
|
||||
|
||||
// Allocate 2MB for the stack. Note that we will only use 1MB, see below.
|
||||
static const size_t stackSize = 2 * 1024 * 1024;
|
||||
stack_ = static_cast<char*>(js_malloc(stackSize));
|
||||
|
@ -1346,19 +1323,21 @@ Simulator::init()
|
|||
// offset from the swi instruction so the simulator knows what to call.
|
||||
class Redirection
|
||||
{
|
||||
friend class Simulator;
|
||||
friend class SimulatorProcess;
|
||||
|
||||
// sim's lock must already be held.
|
||||
Redirection(void* nativeFunction, ABIFunctionType type, Simulator* sim)
|
||||
Redirection(void* nativeFunction, ABIFunctionType type)
|
||||
: nativeFunction_(nativeFunction),
|
||||
swiInstruction_(kCallRedirInstr),
|
||||
type_(type),
|
||||
next_(nullptr)
|
||||
{
|
||||
next_ = sim->redirection();
|
||||
if (Simulator::ICacheCheckingEnabled)
|
||||
FlushICacheLocked(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize);
|
||||
sim->setRedirection(this);
|
||||
next_ = SimulatorProcess::redirection();
|
||||
if (!SimulatorProcess::ICacheCheckingDisableCount) {
|
||||
FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
|
||||
SimInstruction::kInstrSize);
|
||||
}
|
||||
SimulatorProcess::setRedirection(this);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -1367,11 +1346,9 @@ class Redirection
|
|||
ABIFunctionType type() const { return type_; }
|
||||
|
||||
static Redirection* Get(void* nativeFunction, ABIFunctionType type) {
|
||||
Simulator* sim = Simulator::Current();
|
||||
AutoLockSimulatorCache als;
|
||||
|
||||
AutoLockSimulatorCache als(sim);
|
||||
|
||||
Redirection* current = sim->redirection();
|
||||
Redirection* current = SimulatorProcess::redirection();
|
||||
for (; current != nullptr; current = current->next_) {
|
||||
if (current->nativeFunction_ == nativeFunction) {
|
||||
MOZ_ASSERT(current->type() == type);
|
||||
|
@ -1385,7 +1362,7 @@ class Redirection
|
|||
__FILE__, __LINE__);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
new(redir) Redirection(nativeFunction, type, sim);
|
||||
new(redir) Redirection(nativeFunction, type);
|
||||
return redir;
|
||||
}
|
||||
|
||||
|
@ -1405,6 +1382,15 @@ class Redirection
|
|||
Simulator::~Simulator()
|
||||
{
|
||||
js_free(stack_);
|
||||
}
|
||||
|
||||
SimulatorProcess::SimulatorProcess()
|
||||
: cacheLock_(mutexid::SimulatorCacheLock)
|
||||
, redirection_(nullptr)
|
||||
{}
|
||||
|
||||
SimulatorProcess::~SimulatorProcess()
|
||||
{
|
||||
Redirection* r = redirection_;
|
||||
while (r) {
|
||||
Redirection* next = r->next_;
|
||||
|
@ -1413,6 +1399,15 @@ Simulator::~Simulator()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SimulatorProcess::init()
|
||||
{
|
||||
if (getenv("MIPS_SIM_ICACHE_CHECKS"))
|
||||
ICacheCheckingDisableCount = 0;
|
||||
|
||||
return icache_.init();
|
||||
}
|
||||
|
||||
/* static */ void*
|
||||
Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type)
|
||||
{
|
||||
|
@ -1424,7 +1419,9 @@ Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type)
|
|||
Simulator*
|
||||
Simulator::Current()
|
||||
{
|
||||
return TlsContext.get()->runtime()->unsafeContextFromAnyThread()->simulator();
|
||||
JSContext* cx = TlsContext.get();
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
return cx->simulator();
|
||||
}
|
||||
|
||||
// Sets the register in the architecture state. It will also deal with updating
|
||||
|
@ -3654,9 +3651,9 @@ Simulator::decodeTypeJump(SimInstruction* instr)
|
|||
void
|
||||
Simulator::instructionDecode(SimInstruction* instr)
|
||||
{
|
||||
if (Simulator::ICacheCheckingEnabled) {
|
||||
AutoLockSimulatorCache als(this);
|
||||
checkICacheLocked(icache(), instr);
|
||||
if (!SimulatorProcess::ICacheCheckingDisableCount) {
|
||||
AutoLockSimulatorCache als;
|
||||
SimulatorProcess::checkICacheLocked(instr);
|
||||
}
|
||||
pc_modified_ = false;
|
||||
|
||||
|
|
|
@ -110,10 +110,9 @@ const uint32_t kMaxStopCode = 127;
|
|||
typedef uint32_t Instr;
|
||||
class SimInstruction;
|
||||
|
||||
// Per thread simulator state.
|
||||
class Simulator {
|
||||
friend class Redirection;
|
||||
friend class MipsDebugger;
|
||||
friend class AutoLockSimulatorCache;
|
||||
public:
|
||||
|
||||
// Registers are declared in order. See "See MIPS Run Linux" chapter 2.
|
||||
|
@ -222,8 +221,6 @@ class Simulator {
|
|||
// Debugger input.
|
||||
void setLastDebuggerInput(char* input);
|
||||
char* lastDebuggerInput() { return lastDebuggerInput_; }
|
||||
// ICache checking.
|
||||
static void FlushICache(void* start, size_t size);
|
||||
|
||||
// Returns true if pc register contains one of the 'SpecialValues' defined
|
||||
// below (bad_ra, end_sim_pc).
|
||||
|
@ -307,8 +304,6 @@ class Simulator {
|
|||
void branchDelayInstructionDecode(SimInstruction* instr);
|
||||
|
||||
public:
|
||||
static bool ICacheCheckingEnabled;
|
||||
|
||||
static int64_t StopSimAt;
|
||||
|
||||
// Runtime call support.
|
||||
|
@ -381,6 +376,13 @@ class Simulator {
|
|||
char* desc_;
|
||||
};
|
||||
StopCountAndDesc watchedStops_[kNumOfWatchedStops];
|
||||
};
|
||||
|
||||
// Process wide simulator state.
|
||||
class SimulatorProcess
|
||||
{
|
||||
friend class Redirection;
|
||||
friend class AutoLockSimulatorCache;
|
||||
|
||||
private:
|
||||
// ICache checking.
|
||||
|
@ -394,48 +396,63 @@ class Simulator {
|
|||
public:
|
||||
typedef HashMap<void*, CachePage*, ICacheHasher, SystemAllocPolicy> ICacheMap;
|
||||
|
||||
private:
|
||||
// This lock creates a critical section around 'redirection_' and
|
||||
// 'icache_', which are referenced both by the execution engine
|
||||
// and by the off-thread compiler (see Redirection::Get in the cpp file).
|
||||
Mutex cacheLock_;
|
||||
#ifdef DEBUG
|
||||
mozilla::Maybe<Thread::Id> cacheLockHolder_;
|
||||
#endif
|
||||
static mozilla::Atomic<size_t, mozilla::ReleaseAcquire> ICacheCheckingDisableCount;
|
||||
static void FlushICache(void* start, size_t size);
|
||||
|
||||
Redirection* redirection_;
|
||||
ICacheMap icache_;
|
||||
|
||||
private:
|
||||
// Jitcode may be rewritten from a signal handler, but is prevented from
|
||||
// calling FlushICache() because the signal may arrive within the critical
|
||||
// area of an AutoLockSimulatorCache. This flag instructs the Simulator
|
||||
// to remove all cache entries the next time it checks, avoiding false negatives.
|
||||
mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
|
||||
static mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
|
||||
|
||||
void checkICacheLocked(ICacheMap& i_cache, SimInstruction* instr);
|
||||
static void checkICacheLocked(SimInstruction* instr);
|
||||
|
||||
static bool initialize() {
|
||||
singleton_ = js_new<SimulatorProcess>();
|
||||
return singleton_ && singleton_->init();
|
||||
}
|
||||
static void destroy() {
|
||||
js_delete(singleton_);
|
||||
singleton_ = nullptr;
|
||||
}
|
||||
|
||||
SimulatorProcess();
|
||||
~SimulatorProcess();
|
||||
|
||||
private:
|
||||
bool init();
|
||||
|
||||
static SimulatorProcess* singleton_;
|
||||
|
||||
// This lock creates a critical section around 'redirection_' and
|
||||
// 'icache_', which are referenced both by the execution engine
|
||||
// and by the off-thread compiler (see Redirection::Get in the cpp file).
|
||||
Mutex cacheLock_;
|
||||
|
||||
Redirection* redirection_;
|
||||
ICacheMap icache_;
|
||||
|
||||
public:
|
||||
ICacheMap& icache() {
|
||||
static ICacheMap& icache() {
|
||||
// Technically we need the lock to access the innards of the
|
||||
// icache, not to take its address, but the latter condition
|
||||
// serves as a useful complement to the former.
|
||||
MOZ_ASSERT(cacheLockHolder_.isSome());
|
||||
return icache_;
|
||||
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
|
||||
return singleton_->icache_;
|
||||
}
|
||||
|
||||
Redirection* redirection() const {
|
||||
MOZ_ASSERT(cacheLockHolder_.isSome());
|
||||
return redirection_;
|
||||
static Redirection* redirection() {
|
||||
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
|
||||
return singleton_->redirection_;
|
||||
}
|
||||
|
||||
void setRedirection(js::jit::Redirection* redirection) {
|
||||
MOZ_ASSERT(cacheLockHolder_.isSome());
|
||||
redirection_ = redirection;
|
||||
static void setRedirection(js::jit::Redirection* redirection) {
|
||||
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
|
||||
singleton_->redirection_ = redirection;
|
||||
}
|
||||
};
|
||||
|
||||
#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \
|
||||
#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (cx->simulator()->overRecursedWithExtra(extra)) { \
|
||||
js::ReportOverRecursed(cx); \
|
||||
|
|
|
@ -7661,7 +7661,7 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
|
|||
|
||||
#if defined(JS_SIMULATOR_ARM)
|
||||
if (op.getBoolOption("arm-sim-icache-checks"))
|
||||
jit::Simulator::ICacheCheckingEnabled = true;
|
||||
jit::SimulatorProcess::ICacheCheckingDisableCount = 0;
|
||||
|
||||
int32_t stopAt = op.getIntOption("arm-sim-stop-at");
|
||||
if (stopAt >= 0)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "gc/Statistics.h"
|
||||
#include "jit/ExecutableAllocator.h"
|
||||
#include "jit/Ion.h"
|
||||
#include "jit/JitCommon.h"
|
||||
#include "js/Utility.h"
|
||||
#if ENABLE_INTL_API
|
||||
#include "unicode/uclean.h"
|
||||
|
@ -123,6 +124,10 @@ JS::detail::InitWithFailureDiagnostic(bool isDebugBuild)
|
|||
RETURN_IF_FAIL(FutexThread::initialize());
|
||||
RETURN_IF_FAIL(js::gcstats::Statistics::initialize());
|
||||
|
||||
#ifdef JS_SIMULATOR
|
||||
RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize());
|
||||
#endif
|
||||
|
||||
libraryInitState = InitState::Running;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -148,6 +153,10 @@ JS_ShutDown(void)
|
|||
|
||||
js::DestroyHelperThreadsState();
|
||||
|
||||
#ifdef JS_SIMULATOR
|
||||
js::jit::SimulatorProcess::destroy();
|
||||
#endif
|
||||
|
||||
#ifdef JS_TRACE_LOGGING
|
||||
js::DestroyTraceLoggerThreadState();
|
||||
js::DestroyTraceLoggerGraphState();
|
||||
|
|
|
@ -1274,15 +1274,14 @@ JitInterruptHandler(int signum, siginfo_t* info, void* context)
|
|||
if (JSContext* cx = TlsContext.get()) {
|
||||
|
||||
#if defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
|
||||
bool prevICacheCheckingState = Simulator::ICacheCheckingEnabled;
|
||||
Simulator::ICacheCheckingEnabled = false;
|
||||
SimulatorProcess::ICacheCheckingDisableCount++;
|
||||
#endif
|
||||
|
||||
RedirectJitCodeToInterruptCheck(cx, (CONTEXT*)context);
|
||||
|
||||
#if defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
|
||||
Simulator::ICacheCheckingEnabled = prevICacheCheckingState;
|
||||
cx->simulator()->cacheInvalidatedBySignalHandler_ = true;
|
||||
SimulatorProcess::cacheInvalidatedBySignalHandler_ = true;
|
||||
SimulatorProcess::ICacheCheckingDisableCount--;
|
||||
#endif
|
||||
|
||||
cx->finishHandlingJitInterrupt();
|
||||
|
|
Загрузка…
Ссылка в новой задаче