From 262712d4e9d6f8d657765ac9a7de6d8e94735d8a Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Sun, 26 Oct 2014 19:05:30 -0700 Subject: [PATCH] Fixing livelock caused by lf stuff (or at least making it harder to hit). --- src/alloy/frontend/ppc/ppc_context.h | 4 +++ src/alloy/frontend/ppc/ppc_emit_control.cc | 30 ++++++++++++---- src/alloy/frontend/ppc/ppc_frontend.cc | 26 ++++++++++++++ src/alloy/frontend/ppc/ppc_frontend.h | 12 +++++++ src/alloy/frontend/ppc/ppc_hir_builder.cc | 29 +++++++++++++-- src/alloy/frontend/ppc/ppc_hir_builder.h | 2 ++ src/alloy/frontend/ppc/test/alloy-ppc-test.cc | 1 + src/alloy/memory.cc | 7 +++- src/alloy/memory.h | 2 ++ src/alloy/runtime/runtime.cc | 36 ++++++++++++++++++- src/alloy/runtime/runtime.h | 7 ++++ src/alloy/test/util.h | 1 + src/xenia/cpu/xenon_thread_state.cc | 1 + 13 files changed, 147 insertions(+), 11 deletions(-) diff --git a/src/alloy/frontend/ppc/ppc_context.h b/src/alloy/frontend/ppc/ppc_context.h index 44d0ec080..8eb69ea6a 100644 --- a/src/alloy/frontend/ppc/ppc_context.h +++ b/src/alloy/frontend/ppc/ppc_context.h @@ -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. diff --git a/src/alloy/frontend/ppc/ppc_emit_control.cc b/src/alloy/frontend/ppc/ppc_emit_control.cc index fccddfc7b..7de4a02a4 100644 --- a/src/alloy/frontend/ppc/ppc_emit_control.cc +++ b/src/alloy/frontend/ppc/ppc_emit_control.cc @@ -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() { diff --git a/src/alloy/frontend/ppc/ppc_frontend.cc b/src/alloy/frontend/ppc/ppc_frontend.cc index ce14ddfe3..bc959a0d6 100644 --- a/src/alloy/frontend/ppc/ppc_frontend.cc +++ b/src/alloy/frontend/ppc/ppc_frontend.cc @@ -13,6 +13,7 @@ #include #include #include +#include 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(arg0); + volatile bool* global_lock_taken = reinterpret_cast(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(&builtins_.global_lock); + void* arg1 = reinterpret_cast(&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; } diff --git a/src/alloy/frontend/ppc/ppc_frontend.h b/src/alloy/frontend/ppc/ppc_frontend.h index 9b524fc00..425e0318d 100644 --- a/src/alloy/frontend/ppc/ppc_frontend.h +++ b/src/alloy/frontend/ppc/ppc_frontend.h @@ -10,6 +10,8 @@ #ifndef ALLOY_FRONTEND_PPC_PPC_FRONTEND_H_ #define ALLOY_FRONTEND_PPC_PPC_FRONTEND_H_ +#include + #include #include @@ -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 translator_pool_; + PPCBuiltins builtins_; }; } // namespace ppc diff --git a/src/alloy/frontend/ppc/ppc_hir_builder.cc b/src/alloy/frontend/ppc/ppc_hir_builder.cc index aeb0684e8..4168d92a9 100644 --- a/src/alloy/frontend/ppc/ppc_hir_builder.cc +++ b/src/alloy/frontend/ppc/ppc_hir_builder.cc @@ -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)); diff --git a/src/alloy/frontend/ppc/ppc_hir_builder.h b/src/alloy/frontend/ppc/ppc_hir_builder.h index ffe1a1a7b..55a3bd9ca 100644 --- a/src/alloy/frontend/ppc/ppc_hir_builder.h +++ b/src/alloy/frontend/ppc/ppc_hir_builder.h @@ -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(); diff --git a/src/alloy/frontend/ppc/test/alloy-ppc-test.cc b/src/alloy/frontend/ppc/test/alloy-ppc-test.cc index 9b5cb985a..a19e5c850 100644 --- a/src/alloy/frontend/ppc/test/alloy-ppc-test.cc +++ b/src/alloy/frontend/ppc/test/alloy-ppc-test.cc @@ -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; diff --git a/src/alloy/memory.cc b/src/alloy/memory.cc index 4154e9e43..c699173e0 100644 --- a/src/alloy/memory.cc +++ b/src/alloy/memory.cc @@ -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(memory_.data()); reserve_address_ = capacity - 8; + reserve_value_ = capacity - 16; } SimpleMemory::~SimpleMemory() = default; diff --git a/src/alloy/memory.h b/src/alloy/memory.h index e0e42d116..3b3b7249e 100644 --- a/src/alloy/memory.h +++ b/src/alloy/memory.h @@ -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_; }; diff --git a/src/alloy/runtime/runtime.cc b/src/alloy/runtime/runtime.cc index 8cf99d4a1..5b8765bbe 100644 --- a/src/alloy/runtime/runtime.cc +++ b/src/alloy/runtime/runtime.cc @@ -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, // Create debugger first. Other types hook up to it. debugger_.reset(new Debugger(this)); + std::unique_ptr 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 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 Runtime::FindFunctionsWithAddress(uint64_t address) { return entry_table_.FindWithAddress(address); } diff --git a/src/alloy/runtime/runtime.h b/src/alloy/runtime/runtime.h index 279edaf7c..0d0e6312b 100644 --- a/src/alloy/runtime/runtime.h +++ b/src/alloy/runtime/runtime.h @@ -45,6 +45,11 @@ class Runtime { Module* GetModule(const std::string& name) { return GetModule(name.c_str()); } std::vector GetModules(); + Module* builtin_module() const { return builtin_module_; } + FunctionInfo* DefineBuiltin(const std::string& name, + FunctionInfo::ExternHandler handler, void* arg0, + void* arg1); + std::vector 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> modules_; + Module* builtin_module_; + uint64_t next_builtin_address_; }; } // namespace runtime diff --git a/src/alloy/test/util.h b/src/alloy/test/util.h index 2541db1d3..22734c0b2 100644 --- a/src/alloy/test/util.h +++ b/src/alloy/test/util.h @@ -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; diff --git a/src/xenia/cpu/xenon_thread_state.cc b/src/xenia/cpu/xenon_thread_state.cc index 0cdf90b38..2c8109f96 100644 --- a/src/xenia/cpu/xenon_thread_state.cc +++ b/src/xenia/cpu/xenon_thread_state.cc @@ -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;