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

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;