Fixing livelock caused by lf stuff (or at least making it harder to hit).
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user