Fixing livelock caused by lf stuff (or at least making it harder to hit).
This commit is contained in:
parent
6fd0fa9103
commit
262712d4e9
|
@ -172,6 +172,10 @@ typedef struct alignas(64) PPCContext_s {
|
||||||
|
|
||||||
// Reserve address for load acquire/store release. Shared.
|
// Reserve address for load acquire/store release. Shared.
|
||||||
uint64_t* reserve_address;
|
uint64_t* reserve_address;
|
||||||
|
uint64_t* reserve_value;
|
||||||
|
|
||||||
|
// Used to shuttle data into externs. Contents volatile.
|
||||||
|
uint64_t scratch;
|
||||||
|
|
||||||
// Runtime-specific data pointer. Used on callbacks to get access to the
|
// Runtime-specific data pointer. Used on callbacks to get access to the
|
||||||
// current runtime and its data.
|
// current runtime and its data.
|
||||||
|
|
|
@ -607,22 +607,38 @@ XEEMITTER(mtspr, 0x7C0003A6, XFX)(PPCHIRBuilder& f, InstrData& i) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(benvanik): MSR is used for toggling interrupts, and it'd be nice to
|
// MSR is used for toggling interrupts (among other things).
|
||||||
// obey that setting. It's usually guarding atomic stores.
|
// We track it here for taking a global processor lock, as lots of lockfree
|
||||||
|
// code requires it. Sequences of mtmsr/lwar/stcw/mtmsr come up a lot, and
|
||||||
|
// without the lock here threads can livelock.
|
||||||
|
|
||||||
XEEMITTER(mfmsr, 0x7C0000A6, X)(PPCHIRBuilder& f, InstrData& i) {
|
XEEMITTER(mfmsr, 0x7C0000A6, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||||
f.Nop();
|
f.StoreGPR(i.X.RT, f.LoadMSR());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(mtmsr, 0x7C000124, X)(PPCHIRBuilder& f, InstrData& i) {
|
XEEMITTER(mtmsr, 0x7C000124, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||||
f.Nop();
|
if (i.X.RA & 0x01) {
|
||||||
return 0;
|
// L = 1
|
||||||
|
f.StoreMSR(f.ZeroExtend(f.LoadGPR(i.X.RT), INT64_TYPE));
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
// L = 0
|
||||||
|
XEINSTRNOTIMPLEMENTED();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(mtmsrd, 0x7C000164, X)(PPCHIRBuilder& f, InstrData& i) {
|
XEEMITTER(mtmsrd, 0x7C000164, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||||
f.Nop();
|
if (i.X.RA & 0x01) {
|
||||||
return 0;
|
// L = 1
|
||||||
|
f.StoreMSR(f.LoadGPR(i.X.RT));
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
// L = 0
|
||||||
|
XEINSTRNOTIMPLEMENTED();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterEmitCategoryControl() {
|
void RegisterEmitCategoryControl() {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <alloy/frontend/ppc/ppc_disasm.h>
|
#include <alloy/frontend/ppc/ppc_disasm.h>
|
||||||
#include <alloy/frontend/ppc/ppc_emit.h>
|
#include <alloy/frontend/ppc/ppc_emit.h>
|
||||||
#include <alloy/frontend/ppc/ppc_translator.h>
|
#include <alloy/frontend/ppc/ppc_translator.h>
|
||||||
|
#include <alloy/runtime/runtime.h>
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace frontend {
|
namespace frontend {
|
||||||
|
@ -58,12 +59,37 @@ PPCFrontend::~PPCFrontend() {
|
||||||
translator_pool_.Reset();
|
translator_pool_.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckGlobalLock(PPCContext* ppc_state, void* arg0, void* arg1) {
|
||||||
|
ppc_state->scratch = 0x8000;
|
||||||
|
}
|
||||||
|
void HandleGlobalLock(PPCContext* ppc_state, void* arg0, void* arg1) {
|
||||||
|
std::mutex* global_lock = reinterpret_cast<std::mutex*>(arg0);
|
||||||
|
volatile bool* global_lock_taken = reinterpret_cast<bool*>(arg1);
|
||||||
|
uint64_t value = ppc_state->scratch;
|
||||||
|
if (value == 0x8000) {
|
||||||
|
global_lock->unlock();
|
||||||
|
*global_lock_taken = false;
|
||||||
|
} else if (value == ppc_state->r[13]) {
|
||||||
|
global_lock->lock();
|
||||||
|
*global_lock_taken = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int PPCFrontend::Initialize() {
|
int PPCFrontend::Initialize() {
|
||||||
int result = Frontend::Initialize();
|
int result = Frontend::Initialize();
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* arg0 = reinterpret_cast<void*>(&builtins_.global_lock);
|
||||||
|
void* arg1 = reinterpret_cast<void*>(&builtins_.global_lock_taken);
|
||||||
|
builtins_.check_global_lock = runtime_->DefineBuiltin(
|
||||||
|
"CheckGlobalLock", (FunctionInfo::ExternHandler)CheckGlobalLock, arg0,
|
||||||
|
arg1);
|
||||||
|
builtins_.handle_global_lock = runtime_->DefineBuiltin(
|
||||||
|
"HandleGlobalLock", (FunctionInfo::ExternHandler)HandleGlobalLock, arg0,
|
||||||
|
arg1);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#ifndef ALLOY_FRONTEND_PPC_PPC_FRONTEND_H_
|
#ifndef ALLOY_FRONTEND_PPC_PPC_FRONTEND_H_
|
||||||
#define ALLOY_FRONTEND_PPC_PPC_FRONTEND_H_
|
#define ALLOY_FRONTEND_PPC_PPC_FRONTEND_H_
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <alloy/frontend/frontend.h>
|
#include <alloy/frontend/frontend.h>
|
||||||
#include <alloy/type_pool.h>
|
#include <alloy/type_pool.h>
|
||||||
|
|
||||||
|
@ -19,6 +21,13 @@ namespace ppc {
|
||||||
|
|
||||||
class PPCTranslator;
|
class PPCTranslator;
|
||||||
|
|
||||||
|
struct PPCBuiltins {
|
||||||
|
std::mutex global_lock;
|
||||||
|
bool global_lock_taken;
|
||||||
|
runtime::FunctionInfo* check_global_lock;
|
||||||
|
runtime::FunctionInfo* handle_global_lock;
|
||||||
|
};
|
||||||
|
|
||||||
class PPCFrontend : public Frontend {
|
class PPCFrontend : public Frontend {
|
||||||
public:
|
public:
|
||||||
PPCFrontend(runtime::Runtime* runtime);
|
PPCFrontend(runtime::Runtime* runtime);
|
||||||
|
@ -26,6 +35,8 @@ class PPCFrontend : public Frontend {
|
||||||
|
|
||||||
int Initialize() override;
|
int Initialize() override;
|
||||||
|
|
||||||
|
PPCBuiltins* builtins() { return &builtins_; }
|
||||||
|
|
||||||
int DeclareFunction(runtime::FunctionInfo* symbol_info) override;
|
int DeclareFunction(runtime::FunctionInfo* symbol_info) override;
|
||||||
int DefineFunction(runtime::FunctionInfo* symbol_info,
|
int DefineFunction(runtime::FunctionInfo* symbol_info,
|
||||||
uint32_t debug_info_flags, uint32_t trace_flags,
|
uint32_t debug_info_flags, uint32_t trace_flags,
|
||||||
|
@ -33,6 +44,7 @@ class PPCFrontend : public Frontend {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TypePool<PPCTranslator, PPCFrontend*> translator_pool_;
|
TypePool<PPCTranslator, PPCFrontend*> translator_pool_;
|
||||||
|
PPCBuiltins builtins_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ppc
|
} // namespace ppc
|
||||||
|
|
|
@ -308,6 +308,20 @@ void PPCHIRBuilder::UpdateCR6(Value* src_value) {
|
||||||
// TOOD(benvanik): trace CR.
|
// TOOD(benvanik): trace CR.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value* PPCHIRBuilder::LoadMSR() {
|
||||||
|
// bit 48 = EE; interrupt enabled
|
||||||
|
// bit 62 = RI; recoverable interrupt
|
||||||
|
// return 8000h if unlocked, else 0
|
||||||
|
CallExtern(frontend_->builtins()->check_global_lock);
|
||||||
|
return LoadContext(offsetof(PPCContext, scratch), INT64_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPCHIRBuilder::StoreMSR(Value* value) {
|
||||||
|
// if & 0x8000 == 0, lock, else unlock
|
||||||
|
StoreContext(offsetof(PPCContext, scratch), ZeroExtend(value, INT64_TYPE));
|
||||||
|
CallExtern(frontend_->builtins()->handle_global_lock);
|
||||||
|
}
|
||||||
|
|
||||||
Value* PPCHIRBuilder::LoadFPSCR() {
|
Value* PPCHIRBuilder::LoadFPSCR() {
|
||||||
return LoadContext(offsetof(PPCContext, fpscr), INT64_TYPE);
|
return LoadContext(offsetof(PPCContext, fpscr), INT64_TYPE);
|
||||||
}
|
}
|
||||||
|
@ -397,7 +411,12 @@ Value* PPCHIRBuilder::LoadAcquire(Value* address, TypeName type,
|
||||||
uint32_t load_flags) {
|
uint32_t load_flags) {
|
||||||
AtomicExchange(LoadContext(offsetof(PPCContext, reserve_address), INT64_TYPE),
|
AtomicExchange(LoadContext(offsetof(PPCContext, reserve_address), INT64_TYPE),
|
||||||
Truncate(address, INT32_TYPE));
|
Truncate(address, INT32_TYPE));
|
||||||
return Load(address, type, load_flags);
|
Value* value = Load(address, type, load_flags);
|
||||||
|
// Save the value so that we can compare it later in StoreRelease.
|
||||||
|
AtomicExchange(
|
||||||
|
LoadContext(offsetof(PPCContext, reserve_value), INT64_TYPE),
|
||||||
|
value);
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* PPCHIRBuilder::StoreRelease(Value* address, Value* value,
|
Value* PPCHIRBuilder::StoreRelease(Value* address, Value* value,
|
||||||
|
@ -405,7 +424,13 @@ Value* PPCHIRBuilder::StoreRelease(Value* address, Value* value,
|
||||||
Value* old_address = AtomicExchange(
|
Value* old_address = AtomicExchange(
|
||||||
LoadContext(offsetof(PPCContext, reserve_address), INT64_TYPE),
|
LoadContext(offsetof(PPCContext, reserve_address), INT64_TYPE),
|
||||||
LoadZero(INT32_TYPE));
|
LoadZero(INT32_TYPE));
|
||||||
Value* eq = CompareEQ(Truncate(address, INT32_TYPE), old_address);
|
// HACK: ensure the reservation addresses match AND the value hasn't changed.
|
||||||
|
Value* old_value = AtomicExchange(
|
||||||
|
LoadContext(offsetof(PPCContext, reserve_value), INT64_TYPE),
|
||||||
|
LoadZero(value->type));
|
||||||
|
Value* current_value = Load(address, value->type);
|
||||||
|
Value* eq = And(CompareEQ(Truncate(address, INT32_TYPE), old_address),
|
||||||
|
CompareEQ(current_value, old_value));
|
||||||
StoreContext(offsetof(PPCContext, cr0.cr0_eq), eq);
|
StoreContext(offsetof(PPCContext, cr0.cr0_eq), eq);
|
||||||
StoreContext(offsetof(PPCContext, cr0.cr0_lt), LoadZero(INT8_TYPE));
|
StoreContext(offsetof(PPCContext, cr0.cr0_lt), LoadZero(INT8_TYPE));
|
||||||
StoreContext(offsetof(PPCContext, cr0.cr0_gt), LoadZero(INT8_TYPE));
|
StoreContext(offsetof(PPCContext, cr0.cr0_gt), LoadZero(INT8_TYPE));
|
||||||
|
|
|
@ -57,6 +57,8 @@ class PPCHIRBuilder : public hir::HIRBuilder {
|
||||||
void UpdateCR(uint32_t n, Value* lhs, bool is_signed = true);
|
void UpdateCR(uint32_t n, Value* lhs, bool is_signed = true);
|
||||||
void UpdateCR(uint32_t n, Value* lhs, Value* rhs, bool is_signed = true);
|
void UpdateCR(uint32_t n, Value* lhs, Value* rhs, bool is_signed = true);
|
||||||
void UpdateCR6(Value* src_value);
|
void UpdateCR6(Value* src_value);
|
||||||
|
Value* LoadMSR();
|
||||||
|
void StoreMSR(Value* value);
|
||||||
Value* LoadFPSCR();
|
Value* LoadFPSCR();
|
||||||
void StoreFPSCR(Value* value);
|
void StoreFPSCR(Value* value);
|
||||||
Value* LoadXER();
|
Value* LoadXER();
|
||||||
|
|
|
@ -52,6 +52,7 @@ class ThreadState : public alloy::runtime::ThreadState {
|
||||||
|
|
||||||
// Stash pointers to common structures that callbacks may need.
|
// Stash pointers to common structures that callbacks may need.
|
||||||
context_->reserve_address = memory_->reserve_address();
|
context_->reserve_address = memory_->reserve_address();
|
||||||
|
context_->reserve_value = memory_->reserve_value();
|
||||||
context_->membase = memory_->membase();
|
context_->membase = memory_->membase();
|
||||||
context_->runtime = runtime;
|
context_->runtime = runtime;
|
||||||
context_->thread_state = this;
|
context_->thread_state = this;
|
||||||
|
|
|
@ -13,7 +13,11 @@
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
|
|
||||||
Memory::Memory() : membase_(nullptr), reserve_address_(0), trace_base_(0) {
|
Memory::Memory()
|
||||||
|
: membase_(nullptr),
|
||||||
|
reserve_address_(0),
|
||||||
|
reserve_value_(0),
|
||||||
|
trace_base_(0) {
|
||||||
system_page_size_ = poly::page_size();
|
system_page_size_ = poly::page_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +68,7 @@ uint64_t Memory::SearchAligned(uint64_t start, uint64_t end,
|
||||||
SimpleMemory::SimpleMemory(size_t capacity) : memory_(capacity) {
|
SimpleMemory::SimpleMemory(size_t capacity) : memory_(capacity) {
|
||||||
membase_ = reinterpret_cast<uint8_t*>(memory_.data());
|
membase_ = reinterpret_cast<uint8_t*>(memory_.data());
|
||||||
reserve_address_ = capacity - 8;
|
reserve_address_ = capacity - 8;
|
||||||
|
reserve_value_ = capacity - 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleMemory::~SimpleMemory() = default;
|
SimpleMemory::~SimpleMemory() = default;
|
||||||
|
|
|
@ -25,6 +25,7 @@ class Memory {
|
||||||
return membase_ + guest_address;
|
return membase_ + guest_address;
|
||||||
};
|
};
|
||||||
inline uint64_t* reserve_address() { return &reserve_address_; }
|
inline uint64_t* reserve_address() { return &reserve_address_; }
|
||||||
|
inline uint64_t* reserve_value() { return &reserve_value_; }
|
||||||
|
|
||||||
// TODO(benvanik): remove with GPU refactor.
|
// TODO(benvanik): remove with GPU refactor.
|
||||||
virtual uint64_t page_table() const = 0;
|
virtual uint64_t page_table() const = 0;
|
||||||
|
@ -56,6 +57,7 @@ class Memory {
|
||||||
size_t system_page_size_;
|
size_t system_page_size_;
|
||||||
uint8_t* membase_;
|
uint8_t* membase_;
|
||||||
uint64_t reserve_address_;
|
uint64_t reserve_address_;
|
||||||
|
uint64_t reserve_value_;
|
||||||
uint64_t trace_base_;
|
uint64_t trace_base_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,11 +28,25 @@ namespace runtime {
|
||||||
using alloy::backend::Backend;
|
using alloy::backend::Backend;
|
||||||
using alloy::frontend::Frontend;
|
using alloy::frontend::Frontend;
|
||||||
|
|
||||||
|
class BuiltinModule : public Module {
|
||||||
|
public:
|
||||||
|
BuiltinModule(Runtime* runtime) : Module(runtime), name_("builtin") {}
|
||||||
|
const std::string& name() const override { return name_; }
|
||||||
|
bool ContainsAddress(uint64_t address) override {
|
||||||
|
return (address & 0x1FFFFFFF0) == 0x100000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string name_;
|
||||||
|
};
|
||||||
|
|
||||||
Runtime::Runtime(Memory* memory, uint32_t debug_info_flags,
|
Runtime::Runtime(Memory* memory, uint32_t debug_info_flags,
|
||||||
uint32_t trace_flags)
|
uint32_t trace_flags)
|
||||||
: memory_(memory),
|
: memory_(memory),
|
||||||
debug_info_flags_(debug_info_flags),
|
debug_info_flags_(debug_info_flags),
|
||||||
trace_flags_(trace_flags) {}
|
trace_flags_(trace_flags),
|
||||||
|
builtin_module_(nullptr),
|
||||||
|
next_builtin_address_(0x100000000ull) {}
|
||||||
|
|
||||||
Runtime::~Runtime() {
|
Runtime::~Runtime() {
|
||||||
{
|
{
|
||||||
|
@ -53,6 +67,10 @@ int Runtime::Initialize(std::unique_ptr<Frontend> frontend,
|
||||||
// Create debugger first. Other types hook up to it.
|
// Create debugger first. Other types hook up to it.
|
||||||
debugger_.reset(new Debugger(this));
|
debugger_.reset(new Debugger(this));
|
||||||
|
|
||||||
|
std::unique_ptr<Module> builtin_module(new BuiltinModule(this));
|
||||||
|
builtin_module_ = builtin_module.get();
|
||||||
|
modules_.push_back(std::move(builtin_module));
|
||||||
|
|
||||||
if (frontend_ || backend_) {
|
if (frontend_ || backend_) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -127,6 +145,22 @@ std::vector<Module*> Runtime::GetModules() {
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FunctionInfo* Runtime::DefineBuiltin(const std::string& name,
|
||||||
|
FunctionInfo::ExternHandler handler,
|
||||||
|
void* arg0, void* arg1) {
|
||||||
|
uint64_t address = next_builtin_address_;
|
||||||
|
next_builtin_address_ += 4;
|
||||||
|
|
||||||
|
FunctionInfo* fn_info;
|
||||||
|
builtin_module_->DeclareFunction(address, &fn_info);
|
||||||
|
fn_info->set_end_address(address + 4);
|
||||||
|
fn_info->set_name(name);
|
||||||
|
fn_info->SetupExtern(handler, arg0, arg1);
|
||||||
|
fn_info->set_status(runtime::SymbolInfo::STATUS_DECLARED);
|
||||||
|
|
||||||
|
return fn_info;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Function*> Runtime::FindFunctionsWithAddress(uint64_t address) {
|
std::vector<Function*> Runtime::FindFunctionsWithAddress(uint64_t address) {
|
||||||
return entry_table_.FindWithAddress(address);
|
return entry_table_.FindWithAddress(address);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,11 @@ class Runtime {
|
||||||
Module* GetModule(const std::string& name) { return GetModule(name.c_str()); }
|
Module* GetModule(const std::string& name) { return GetModule(name.c_str()); }
|
||||||
std::vector<Module*> GetModules();
|
std::vector<Module*> GetModules();
|
||||||
|
|
||||||
|
Module* builtin_module() const { return builtin_module_; }
|
||||||
|
FunctionInfo* DefineBuiltin(const std::string& name,
|
||||||
|
FunctionInfo::ExternHandler handler, void* arg0,
|
||||||
|
void* arg1);
|
||||||
|
|
||||||
std::vector<Function*> FindFunctionsWithAddress(uint64_t address);
|
std::vector<Function*> FindFunctionsWithAddress(uint64_t address);
|
||||||
|
|
||||||
int LookupFunctionInfo(uint64_t address, FunctionInfo** out_symbol_info);
|
int LookupFunctionInfo(uint64_t address, FunctionInfo** out_symbol_info);
|
||||||
|
@ -71,6 +76,8 @@ class Runtime {
|
||||||
EntryTable entry_table_;
|
EntryTable entry_table_;
|
||||||
std::mutex modules_lock_;
|
std::mutex modules_lock_;
|
||||||
std::vector<std::unique_ptr<Module>> modules_;
|
std::vector<std::unique_ptr<Module>> modules_;
|
||||||
|
Module* builtin_module_;
|
||||||
|
uint64_t next_builtin_address_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace runtime
|
} // namespace runtime
|
||||||
|
|
|
@ -47,6 +47,7 @@ class ThreadState : public alloy::runtime::ThreadState {
|
||||||
|
|
||||||
// Stash pointers to common structures that callbacks may need.
|
// Stash pointers to common structures that callbacks may need.
|
||||||
context_->reserve_address = memory_->reserve_address();
|
context_->reserve_address = memory_->reserve_address();
|
||||||
|
context_->reserve_value = memory_->reserve_value();
|
||||||
context_->membase = memory_->membase();
|
context_->membase = memory_->membase();
|
||||||
context_->runtime = runtime;
|
context_->runtime = runtime;
|
||||||
context_->thread_state = this;
|
context_->thread_state = this;
|
||||||
|
|
|
@ -33,6 +33,7 @@ XenonThreadState::XenonThreadState(XenonRuntime* runtime, uint32_t thread_id,
|
||||||
|
|
||||||
// Stash pointers to common structures that callbacks may need.
|
// Stash pointers to common structures that callbacks may need.
|
||||||
context_->reserve_address = memory_->reserve_address();
|
context_->reserve_address = memory_->reserve_address();
|
||||||
|
context_->reserve_value = memory_->reserve_value();
|
||||||
context_->membase = memory_->membase();
|
context_->membase = memory_->membase();
|
||||||
context_->runtime = runtime;
|
context_->runtime = runtime;
|
||||||
context_->thread_state = this;
|
context_->thread_state = this;
|
||||||
|
|
Loading…
Reference in New Issue