Fixing livelock caused by lf stuff (or at least making it harder to hit).

This commit is contained in:
Ben Vanik 2014-10-26 19:05:30 -07:00
parent 6fd0fa9103
commit 262712d4e9
13 changed files with 147 additions and 11 deletions

View File

@ -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.

View File

@ -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();
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();
if (i.X.RA & 0x01) {
// L = 1
f.StoreMSR(f.LoadGPR(i.X.RT));
return 0;
} else {
// L = 0
XEINSTRNOTIMPLEMENTED();
return 1;
}
}
void RegisterEmitCategoryControl() {

View File

@ -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;
}

View File

@ -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

View File

@ -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));

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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_;
};

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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;