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.
|
||||
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
|
||||
// current runtime and its data.
|
||||
|
|
|
@ -607,22 +607,38 @@ XEEMITTER(mtspr, 0x7C0003A6, XFX)(PPCHIRBuilder& f, InstrData& i) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// TODO(benvanik): MSR is used for toggling interrupts, and it'd be nice to
|
||||
// obey that setting. It's usually guarding atomic stores.
|
||||
// MSR is used for toggling interrupts (among other things).
|
||||
// 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) {
|
||||
f.Nop();
|
||||
f.StoreGPR(i.X.RT, f.LoadMSR());
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mtmsr, 0x7C000124, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
f.Nop();
|
||||
return 0;
|
||||
if (i.X.RA & 0x01) {
|
||||
// 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) {
|
||||
f.Nop();
|
||||
return 0;
|
||||
if (i.X.RA & 0x01) {
|
||||
// L = 1
|
||||
f.StoreMSR(f.LoadGPR(i.X.RT));
|
||||
return 0;
|
||||
} else {
|
||||
// L = 0
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterEmitCategoryControl() {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <alloy/frontend/ppc/ppc_disasm.h>
|
||||
#include <alloy/frontend/ppc/ppc_emit.h>
|
||||
#include <alloy/frontend/ppc/ppc_translator.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
|
||||
namespace alloy {
|
||||
namespace frontend {
|
||||
|
@ -58,12 +59,37 @@ PPCFrontend::~PPCFrontend() {
|
|||
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 result = Frontend::Initialize();
|
||||
if (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;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#ifndef ALLOY_FRONTEND_PPC_PPC_FRONTEND_H_
|
||||
#define ALLOY_FRONTEND_PPC_PPC_FRONTEND_H_
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <alloy/frontend/frontend.h>
|
||||
#include <alloy/type_pool.h>
|
||||
|
||||
|
@ -19,6 +21,13 @@ namespace ppc {
|
|||
|
||||
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 {
|
||||
public:
|
||||
PPCFrontend(runtime::Runtime* runtime);
|
||||
|
@ -26,6 +35,8 @@ class PPCFrontend : public Frontend {
|
|||
|
||||
int Initialize() override;
|
||||
|
||||
PPCBuiltins* builtins() { return &builtins_; }
|
||||
|
||||
int DeclareFunction(runtime::FunctionInfo* symbol_info) override;
|
||||
int DefineFunction(runtime::FunctionInfo* symbol_info,
|
||||
uint32_t debug_info_flags, uint32_t trace_flags,
|
||||
|
@ -33,6 +44,7 @@ class PPCFrontend : public Frontend {
|
|||
|
||||
private:
|
||||
TypePool<PPCTranslator, PPCFrontend*> translator_pool_;
|
||||
PPCBuiltins builtins_;
|
||||
};
|
||||
|
||||
} // namespace ppc
|
||||
|
|
|
@ -308,6 +308,20 @@ void PPCHIRBuilder::UpdateCR6(Value* src_value) {
|
|||
// 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() {
|
||||
return LoadContext(offsetof(PPCContext, fpscr), INT64_TYPE);
|
||||
}
|
||||
|
@ -397,7 +411,12 @@ Value* PPCHIRBuilder::LoadAcquire(Value* address, TypeName type,
|
|||
uint32_t load_flags) {
|
||||
AtomicExchange(LoadContext(offsetof(PPCContext, reserve_address), INT64_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,
|
||||
|
@ -405,7 +424,13 @@ Value* PPCHIRBuilder::StoreRelease(Value* address, Value* value,
|
|||
Value* old_address = AtomicExchange(
|
||||
LoadContext(offsetof(PPCContext, reserve_address), INT64_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_lt), 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, Value* rhs, bool is_signed = true);
|
||||
void UpdateCR6(Value* src_value);
|
||||
Value* LoadMSR();
|
||||
void StoreMSR(Value* value);
|
||||
Value* LoadFPSCR();
|
||||
void StoreFPSCR(Value* value);
|
||||
Value* LoadXER();
|
||||
|
|
|
@ -52,6 +52,7 @@ class ThreadState : public alloy::runtime::ThreadState {
|
|||
|
||||
// Stash pointers to common structures that callbacks may need.
|
||||
context_->reserve_address = memory_->reserve_address();
|
||||
context_->reserve_value = memory_->reserve_value();
|
||||
context_->membase = memory_->membase();
|
||||
context_->runtime = runtime;
|
||||
context_->thread_state = this;
|
||||
|
|
|
@ -13,7 +13,11 @@
|
|||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -64,6 +68,7 @@ uint64_t Memory::SearchAligned(uint64_t start, uint64_t end,
|
|||
SimpleMemory::SimpleMemory(size_t capacity) : memory_(capacity) {
|
||||
membase_ = reinterpret_cast<uint8_t*>(memory_.data());
|
||||
reserve_address_ = capacity - 8;
|
||||
reserve_value_ = capacity - 16;
|
||||
}
|
||||
|
||||
SimpleMemory::~SimpleMemory() = default;
|
||||
|
|
|
@ -25,6 +25,7 @@ class Memory {
|
|||
return membase_ + guest_address;
|
||||
};
|
||||
inline uint64_t* reserve_address() { return &reserve_address_; }
|
||||
inline uint64_t* reserve_value() { return &reserve_value_; }
|
||||
|
||||
// TODO(benvanik): remove with GPU refactor.
|
||||
virtual uint64_t page_table() const = 0;
|
||||
|
@ -56,6 +57,7 @@ class Memory {
|
|||
size_t system_page_size_;
|
||||
uint8_t* membase_;
|
||||
uint64_t reserve_address_;
|
||||
uint64_t reserve_value_;
|
||||
uint64_t trace_base_;
|
||||
};
|
||||
|
||||
|
|
|
@ -28,11 +28,25 @@ namespace runtime {
|
|||
using alloy::backend::Backend;
|
||||
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,
|
||||
uint32_t trace_flags)
|
||||
: memory_(memory),
|
||||
debug_info_flags_(debug_info_flags),
|
||||
trace_flags_(trace_flags) {}
|
||||
trace_flags_(trace_flags),
|
||||
builtin_module_(nullptr),
|
||||
next_builtin_address_(0x100000000ull) {}
|
||||
|
||||
Runtime::~Runtime() {
|
||||
{
|
||||
|
@ -53,6 +67,10 @@ int Runtime::Initialize(std::unique_ptr<Frontend> frontend,
|
|||
// Create debugger first. Other types hook up to it.
|
||||
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_) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -127,6 +145,22 @@ std::vector<Module*> Runtime::GetModules() {
|
|||
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) {
|
||||
return entry_table_.FindWithAddress(address);
|
||||
}
|
||||
|
|
|
@ -45,6 +45,11 @@ class Runtime {
|
|||
Module* GetModule(const std::string& name) { return GetModule(name.c_str()); }
|
||||
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);
|
||||
|
||||
int LookupFunctionInfo(uint64_t address, FunctionInfo** out_symbol_info);
|
||||
|
@ -71,6 +76,8 @@ class Runtime {
|
|||
EntryTable entry_table_;
|
||||
std::mutex modules_lock_;
|
||||
std::vector<std::unique_ptr<Module>> modules_;
|
||||
Module* builtin_module_;
|
||||
uint64_t next_builtin_address_;
|
||||
};
|
||||
|
||||
} // namespace runtime
|
||||
|
|
|
@ -47,6 +47,7 @@ class ThreadState : public alloy::runtime::ThreadState {
|
|||
|
||||
// Stash pointers to common structures that callbacks may need.
|
||||
context_->reserve_address = memory_->reserve_address();
|
||||
context_->reserve_value = memory_->reserve_value();
|
||||
context_->membase = memory_->membase();
|
||||
context_->runtime = runtime;
|
||||
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.
|
||||
context_->reserve_address = memory_->reserve_address();
|
||||
context_->reserve_value = memory_->reserve_value();
|
||||
context_->membase = memory_->membase();
|
||||
context_->runtime = runtime;
|
||||
context_->thread_state = this;
|
||||
|
|
Loading…
Reference in New Issue