From eaa1a8ee3aa05963cc30c2594345fc8f75e26930 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Wed, 5 Aug 2015 21:50:02 -0700 Subject: [PATCH] Refactoring SymbolInfo/FunctionInfo/Function into Symbol/Function. --- src/xenia/cpu/backend/assembler.h | 8 +- src/xenia/cpu/backend/backend.h | 5 + src/xenia/cpu/backend/code_cache.h | 4 +- src/xenia/cpu/backend/x64/x64_assembler.cc | 23 ++- src/xenia/cpu/backend/x64/x64_assembler.h | 5 +- src/xenia/cpu/backend/x64/x64_backend.cc | 8 +- src/xenia/cpu/backend/x64/x64_backend.h | 3 + src/xenia/cpu/backend/x64/x64_code_cache.cc | 12 +- src/xenia/cpu/backend/x64/x64_code_cache.h | 6 +- .../cpu/backend/x64/x64_code_cache_win.cc | 1 + src/xenia/cpu/backend/x64/x64_emitter.cc | 115 +++++++------- src/xenia/cpu/backend/x64/x64_emitter.h | 13 +- src/xenia/cpu/backend/x64/x64_function.cc | 12 +- src/xenia/cpu/backend/x64/x64_function.h | 13 +- src/xenia/cpu/backend/x64/x64_sequences.cc | 25 +-- .../passes/constant_propagation_pass.cc | 12 +- src/xenia/cpu/debug_info.cc | 1 + src/xenia/cpu/debug_info.h | 9 +- src/xenia/cpu/frontend/ppc_emit_control.cc | 10 +- src/xenia/cpu/frontend/ppc_frontend.cc | 12 +- src/xenia/cpu/frontend/ppc_frontend.h | 10 +- src/xenia/cpu/frontend/ppc_hir_builder.cc | 29 ++-- src/xenia/cpu/frontend/ppc_hir_builder.h | 14 +- src/xenia/cpu/frontend/ppc_scanner.cc | 19 ++- src/xenia/cpu/frontend/ppc_scanner.h | 13 +- src/xenia/cpu/frontend/ppc_translator.cc | 36 +++-- src/xenia/cpu/frontend/ppc_translator.h | 8 +- .../cpu/frontend/testing/ppc_testing_main.cc | 8 +- src/xenia/cpu/function.cc | 124 ++++++++------- src/xenia/cpu/function.h | 91 ++++++++--- src/xenia/cpu/hir/hir_builder.cc | 20 +-- src/xenia/cpu/hir/hir_builder.h | 7 +- src/xenia/cpu/hir/instr.h | 4 +- src/xenia/cpu/instrument.cc | 114 -------------- src/xenia/cpu/instrument.h | 142 ------------------ src/xenia/cpu/module.cc | 123 ++++++++------- src/xenia/cpu/module.h | 40 ++--- src/xenia/cpu/processor.cc | 114 +++++++------- src/xenia/cpu/processor.h | 15 +- src/xenia/cpu/raw_module.cc | 7 + src/xenia/cpu/raw_module.h | 3 + src/xenia/cpu/stack_walker.h | 4 +- src/xenia/cpu/stack_walker_win.cc | 22 +-- src/xenia/cpu/symbol.h | 62 ++++++++ src/xenia/cpu/symbol_info.cc | 55 ------- src/xenia/cpu/symbol_info.h | 122 --------------- src/xenia/cpu/test_module.cc | 29 ++-- src/xenia/cpu/test_module.h | 7 +- src/xenia/cpu/testing/util.h | 3 +- src/xenia/cpu/xex_module.cc | 117 ++++++++------- src/xenia/cpu/xex_module.h | 4 +- src/xenia/debug/debug_server.cc | 8 +- src/xenia/debug/debugger.cc | 68 ++++----- src/xenia/debug/debugger.h | 3 +- 54 files changed, 715 insertions(+), 1027 deletions(-) delete mode 100644 src/xenia/cpu/instrument.cc delete mode 100644 src/xenia/cpu/instrument.h create mode 100644 src/xenia/cpu/symbol.h delete mode 100644 src/xenia/cpu/symbol_info.cc delete mode 100644 src/xenia/cpu/symbol_info.h diff --git a/src/xenia/cpu/backend/assembler.h b/src/xenia/cpu/backend/assembler.h index e9312c0d7..b8854598b 100644 --- a/src/xenia/cpu/backend/assembler.h +++ b/src/xenia/cpu/backend/assembler.h @@ -15,8 +15,7 @@ namespace xe { namespace cpu { class DebugInfo; -class Function; -class FunctionInfo; +class GuestFunction; namespace hir { class HIRBuilder; } // namespace hir @@ -38,10 +37,9 @@ class Assembler { virtual void Reset(); - virtual bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder, + virtual bool Assemble(GuestFunction* function, hir::HIRBuilder* builder, uint32_t debug_info_flags, - std::unique_ptr debug_info, - Function** out_function) = 0; + std::unique_ptr debug_info) = 0; protected: Backend* backend_; diff --git a/src/xenia/cpu/backend/backend.h b/src/xenia/cpu/backend/backend.h index 463a2321b..90d4adf41 100644 --- a/src/xenia/cpu/backend/backend.h +++ b/src/xenia/cpu/backend/backend.h @@ -16,6 +16,8 @@ namespace xe { namespace cpu { +class GuestFunction; +class Module; class Processor; } // namespace cpu } // namespace xe @@ -46,6 +48,9 @@ class Backend { virtual std::unique_ptr CreateAssembler() = 0; + virtual std::unique_ptr CreateGuestFunction( + Module* module, uint32_t address) = 0; + protected: Processor* processor_; MachineInfo machine_info_; diff --git a/src/xenia/cpu/backend/code_cache.h b/src/xenia/cpu/backend/code_cache.h index cc7949ac3..1b98757d8 100644 --- a/src/xenia/cpu/backend/code_cache.h +++ b/src/xenia/cpu/backend/code_cache.h @@ -12,7 +12,7 @@ #include -#include "xenia/cpu/symbol_info.h" +#include "xenia/cpu/function.h" namespace xe { namespace cpu { @@ -29,7 +29,7 @@ class CodeCache { // Finds a function based on the given host PC (that may be within a // function). - virtual FunctionInfo* LookupFunction(uint64_t host_pc) = 0; + virtual GuestFunction* LookupFunction(uint64_t host_pc) = 0; // Finds platform-specific function unwind info for the given host PC. virtual void* LookupUnwindInfo(uint64_t host_pc) = 0; diff --git a/src/xenia/cpu/backend/x64/x64_assembler.cc b/src/xenia/cpu/backend/x64/x64_assembler.cc index 4b83dbafc..abadfa2e3 100644 --- a/src/xenia/cpu/backend/x64/x64_assembler.cc +++ b/src/xenia/cpu/backend/x64/x64_assembler.cc @@ -66,30 +66,26 @@ void X64Assembler::Reset() { Assembler::Reset(); } -bool X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder, +bool X64Assembler::Assemble(GuestFunction* function, HIRBuilder* builder, uint32_t debug_info_flags, - std::unique_ptr debug_info, - Function** out_function) { + std::unique_ptr debug_info) { SCOPE_profile_cpu_f("cpu"); // Reset when we leave. xe::make_reset_scope(this); - // Create now, and populate as we go. - // We may throw it away if we fail. - auto fn = std::make_unique(symbol_info); - // Lower HIR -> x64. void* machine_code = nullptr; size_t code_size = 0; - if (!emitter_->Emit(symbol_info, builder, debug_info_flags, debug_info.get(), - machine_code, code_size, fn->source_map())) { + if (!emitter_->Emit(function, builder, debug_info_flags, debug_info.get(), + machine_code, code_size, function->source_map())) { return false; } // Stash generated machine code. if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmMachineCode) { - DumpMachineCode(machine_code, code_size, fn->source_map(), &string_buffer_); + DumpMachineCode(machine_code, code_size, function->source_map(), + &string_buffer_); debug_info->set_machine_code_disasm(string_buffer_.ToString()); string_buffer_.Reset(); } @@ -102,11 +98,10 @@ bool X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder, } } - fn->set_debug_info(std::move(debug_info)); - fn->Setup(reinterpret_cast(machine_code), code_size); + function->set_debug_info(std::move(debug_info)); + static_cast(function) + ->Setup(reinterpret_cast(machine_code), code_size); - // Pass back ownership. - *out_function = fn.release(); return true; } diff --git a/src/xenia/cpu/backend/x64/x64_assembler.h b/src/xenia/cpu/backend/x64/x64_assembler.h index 82c6f86d7..a97d4a411 100644 --- a/src/xenia/cpu/backend/x64/x64_assembler.h +++ b/src/xenia/cpu/backend/x64/x64_assembler.h @@ -35,10 +35,9 @@ class X64Assembler : public Assembler { void Reset() override; - bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder, + bool Assemble(GuestFunction* function, hir::HIRBuilder* builder, uint32_t debug_info_flags, - std::unique_ptr debug_info, - Function** out_function) override; + std::unique_ptr debug_info) override; private: void DumpMachineCode(void* machine_code, size_t code_size, diff --git a/src/xenia/cpu/backend/x64/x64_backend.cc b/src/xenia/cpu/backend/x64/x64_backend.cc index 99c1bfa19..539e6c0bb 100644 --- a/src/xenia/cpu/backend/x64/x64_backend.cc +++ b/src/xenia/cpu/backend/x64/x64_backend.cc @@ -10,8 +10,9 @@ #include "xenia/cpu/backend/x64/x64_backend.h" #include "xenia/cpu/backend/x64/x64_assembler.h" -#include "xenia/cpu/backend/x64/x64_emitter.h" #include "xenia/cpu/backend/x64/x64_code_cache.h" +#include "xenia/cpu/backend/x64/x64_emitter.h" +#include "xenia/cpu/backend/x64/x64_function.h" #include "xenia/cpu/backend/x64/x64_sequences.h" #include "xenia/cpu/backend/x64/x64_stack_layout.h" #include "xenia/cpu/processor.h" @@ -106,6 +107,11 @@ std::unique_ptr X64Backend::CreateAssembler() { return std::make_unique(this); } +std::unique_ptr X64Backend::CreateGuestFunction( + Module* module, uint32_t address) { + return std::make_unique(module, address); +} + using namespace Xbyak; X64ThunkEmitter::X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator) diff --git a/src/xenia/cpu/backend/x64/x64_backend.h b/src/xenia/cpu/backend/x64/x64_backend.h index 50b691935..e4b613593 100644 --- a/src/xenia/cpu/backend/x64/x64_backend.h +++ b/src/xenia/cpu/backend/x64/x64_backend.h @@ -56,6 +56,9 @@ class X64Backend : public Backend { std::unique_ptr CreateAssembler() override; + std::unique_ptr CreateGuestFunction(Module* module, + uint32_t address) override; + private: std::unique_ptr code_cache_; diff --git a/src/xenia/cpu/backend/x64/x64_code_cache.cc b/src/xenia/cpu/backend/x64/x64_code_cache.cc index 8fd5eda51..e94561dd4 100644 --- a/src/xenia/cpu/backend/x64/x64_code_cache.cc +++ b/src/xenia/cpu/backend/x64/x64_code_cache.cc @@ -17,6 +17,7 @@ #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/base/memory.h" +#include "xenia/cpu/function.h" namespace xe { namespace cpu { @@ -120,7 +121,7 @@ void* X64CodeCache::PlaceHostCode(uint32_t guest_address, void* machine_code, void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code, size_t code_size, size_t stack_size, - FunctionInfo* function_info) { + GuestFunction* function_info) { // Hold a lock while we bump the pointers up. This is important as the // unwind table requires entries AND code to be sorted in order. size_t low_mark; @@ -220,15 +221,15 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) { return uint32_t(uintptr_t(data_address)); } -FunctionInfo* X64CodeCache::LookupFunction(uint64_t host_pc) { +GuestFunction* X64CodeCache::LookupFunction(uint64_t host_pc) { uint32_t key = uint32_t(host_pc - kGeneratedCodeBase); void* fn_entry = std::bsearch( &key, generated_code_map_.data(), generated_code_map_.size() + 1, - sizeof(std::pair), + sizeof(std::pair), [](const void* key_ptr, const void* element_ptr) { auto key = *reinterpret_cast(key_ptr); auto element = - reinterpret_cast*>( + reinterpret_cast*>( element_ptr); if (key < (element->first >> 32)) { return -1; @@ -239,7 +240,8 @@ FunctionInfo* X64CodeCache::LookupFunction(uint64_t host_pc) { } }); if (fn_entry) { - return reinterpret_cast*>(fn_entry) + return reinterpret_cast*>( + fn_entry) ->second; } else { return nullptr; diff --git a/src/xenia/cpu/backend/x64/x64_code_cache.h b/src/xenia/cpu/backend/x64/x64_code_cache.h index 0c2f83659..2c284e621 100644 --- a/src/xenia/cpu/backend/x64/x64_code_cache.h +++ b/src/xenia/cpu/backend/x64/x64_code_cache.h @@ -50,10 +50,10 @@ class X64CodeCache : public CodeCache { size_t code_size, size_t stack_size); void* PlaceGuestCode(uint32_t guest_address, void* machine_code, size_t code_size, size_t stack_size, - FunctionInfo* function_info); + GuestFunction* function_info); uint32_t PlaceData(const void* data, size_t length); - FunctionInfo* LookupFunction(uint64_t host_pc) override; + GuestFunction* LookupFunction(uint64_t host_pc) override; protected: // All executable code falls within 0x80000000 to 0x9FFFFFFF, so we can @@ -111,7 +111,7 @@ class X64CodeCache : public CodeCache { // Sorted map by host PC base offsets to source function info. // This can be used to bsearch on host PC to find the guest function. // The key is [start address | end address]. - std::vector> generated_code_map_; + std::vector> generated_code_map_; }; } // namespace x64 diff --git a/src/xenia/cpu/backend/x64/x64_code_cache_win.cc b/src/xenia/cpu/backend/x64/x64_code_cache_win.cc index a8cb5333d..563525fe1 100644 --- a/src/xenia/cpu/backend/x64/x64_code_cache_win.cc +++ b/src/xenia/cpu/backend/x64/x64_code_cache_win.cc @@ -18,6 +18,7 @@ #include "xenia/base/math.h" #include "xenia/base/memory.h" #include "xenia/base/platform_win.h" +#include "xenia/cpu/function.h" // When enabled, this will use Windows 8 APIs to get unwind info. // TODO(benvanik): figure out why the callback variant doesn't work. diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc index e9b5e1c58..204775221 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_emitter.cc @@ -27,8 +27,9 @@ #include "xenia/cpu/backend/x64/x64_stack_layout.h" #include "xenia/cpu/cpu_flags.h" #include "xenia/cpu/debug_info.h" +#include "xenia/cpu/function.h" #include "xenia/cpu/processor.h" -#include "xenia/cpu/symbol_info.h" +#include "xenia/cpu/symbol.h" #include "xenia/cpu/thread_state.h" #include "xenia/debug/debugger.h" #include "xenia/profiling.h" @@ -87,7 +88,7 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator) X64Emitter::~X64Emitter() = default; -bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder, +bool X64Emitter::Emit(GuestFunction* function, HIRBuilder* builder, uint32_t debug_info_flags, DebugInfo* debug_info, void*& out_code_address, size_t& out_code_size, std::vector& out_source_map) { @@ -96,6 +97,7 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder, // Reset. debug_info_ = debug_info; debug_info_flags_ = debug_info_flags; + trace_data_ = &function->trace_data(); source_map_arena_.Reset(); // Fill the generator with code. @@ -106,7 +108,7 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder, // Copy the final code to the cache and relocate it. out_code_size = getSize(); - out_code_address = Emplace(stack_size, function_info); + out_code_address = Emplace(stack_size, function); // Stash source map. source_map_arena_.CloneContents(out_source_map); @@ -114,16 +116,16 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder, return true; } -void* X64Emitter::Emplace(size_t stack_size, FunctionInfo* function_info) { +void* X64Emitter::Emplace(size_t stack_size, GuestFunction* function) { // To avoid changing xbyak, we do a switcharoo here. // top_ points to the Xbyak buffer, and since we are in AutoGrow mode // it has pending relocations. We copy the top_ to our buffer, swap the // pointer, relocate, then return the original scratch pointer for use. uint8_t* old_address = top_; void* new_address; - if (function_info) { - new_address = code_cache_->PlaceGuestCode(function_info->address(), top_, - size_, stack_size, function_info); + if (function) { + new_address = code_cache_->PlaceGuestCode(function->address(), top_, size_, + stack_size, function); } else { new_address = code_cache_->PlaceHostCode(0, top_, size_, stack_size); } @@ -177,8 +179,8 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) { // Safe now to do some tracing. if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctions) { // We require 32-bit addresses. - assert_true(uint64_t(debug_info_->trace_data().header()) < UINT_MAX); - auto trace_header = debug_info_->trace_data().header(); + assert_true(uint64_t(trace_data_->header()) < UINT_MAX); + auto trace_header = trace_data_->header(); // Call count. lock(); @@ -265,11 +267,10 @@ void X64Emitter::MarkSourceOffset(const Instr* i) { } if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) { - auto trace_data = debug_info_->trace_data(); uint32_t instruction_index = - (entry->source_offset - trace_data.start_address()) / 4; + (entry->source_offset - trace_data_->start_address()) / 4; lock(); - inc(qword[low_address(trace_data.instruction_execute_counts() + + inc(qword[low_address(trace_data_->instruction_execute_counts() + instruction_index * 8)]); } } @@ -341,8 +342,7 @@ extern "C" uint64_t ResolveFunction(void* raw_context, // TODO(benvanik): required? assert_not_zero(target_address); - Function* fn = NULL; - thread_state->processor()->ResolveFunction(target_address, &fn); + auto fn = thread_state->processor()->ResolveFunction(target_address); assert_not_null(fn); auto x64_fn = static_cast(fn); uint64_t addr = reinterpret_cast(x64_fn->machine_code()); @@ -350,11 +350,11 @@ extern "C" uint64_t ResolveFunction(void* raw_context, return addr; } -void X64Emitter::Call(const hir::Instr* instr, FunctionInfo* symbol_info) { - assert_not_null(symbol_info); - auto fn = reinterpret_cast(symbol_info->function()); +void X64Emitter::Call(const hir::Instr* instr, GuestFunction* function) { + assert_not_null(function); + auto fn = static_cast(function); // Resolve address to the function to call and store in rax. - if (fn) { + if (fn->machine_code()) { // TODO(benvanik): is it worth it to do this? It removes the need for // a ResolveFunction call, but makes the table less useful. assert_zero(uint64_t(fn->machine_code()) & 0xFFFFFFFF00000000); @@ -363,7 +363,7 @@ void X64Emitter::Call(const hir::Instr* instr, FunctionInfo* symbol_info) { // Load the pointer to the indirection table maintained in X64CodeCache. // The target dword will either contain the address of the generated code // or a thunk to ResolveAddress. - mov(ebx, symbol_info->address()); + mov(ebx, function->address()); mov(eax, dword[ebx]); } @@ -418,43 +418,50 @@ void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) { } } -uint64_t UndefinedCallExtern(void* raw_context, uint64_t symbol_info_ptr) { - auto symbol_info = reinterpret_cast(symbol_info_ptr); - XELOGW("undefined extern call to %.8X %s", symbol_info->address(), - symbol_info->name().c_str()); +uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) { + auto function = reinterpret_cast(function_ptr); + XELOGW("undefined extern call to %.8X %s", function->address(), + function->name().c_str()); return 0; } -void X64Emitter::CallExtern(const hir::Instr* instr, - const FunctionInfo* symbol_info) { - if (symbol_info->behavior() == FunctionBehavior::kBuiltin && - symbol_info->builtin_handler()) { - // rcx = context - // rdx = target host function - // r8 = arg0 - // r9 = arg1 - mov(rdx, reinterpret_cast(symbol_info->builtin_handler())); - mov(r8, reinterpret_cast(symbol_info->builtin_arg0())); - mov(r9, reinterpret_cast(symbol_info->builtin_arg1())); - auto thunk = backend()->guest_to_host_thunk(); - mov(rax, reinterpret_cast(thunk)); - call(rax); - ReloadECX(); - ReloadEDX(); - // rax = host return - } else if (symbol_info->behavior() == FunctionBehavior::kExtern && - symbol_info->extern_handler()) { - // rcx = context - // rdx = target host function - mov(rdx, reinterpret_cast(symbol_info->extern_handler())); - mov(r8, qword[rcx + offsetof(cpu::frontend::PPCContext, kernel_state)]); - auto thunk = backend()->guest_to_host_thunk(); - mov(rax, reinterpret_cast(thunk)); - call(rax); - ReloadECX(); - ReloadEDX(); - // rax = host return - } else { - CallNative(UndefinedCallExtern, reinterpret_cast(symbol_info)); +void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) { + bool undefined = true; + if (function->behavior() == Function::Behavior::kBuiltin) { + auto builtin_function = static_cast(function); + if (builtin_function->handler()) { + undefined = false; + // rcx = context + // rdx = target host function + // r8 = arg0 + // r9 = arg1 + mov(rdx, reinterpret_cast(builtin_function->handler())); + mov(r8, reinterpret_cast(builtin_function->arg0())); + mov(r9, reinterpret_cast(builtin_function->arg1())); + auto thunk = backend()->guest_to_host_thunk(); + mov(rax, reinterpret_cast(thunk)); + call(rax); + ReloadECX(); + ReloadEDX(); + // rax = host return + } + } else if (function->behavior() == Function::Behavior::kExtern) { + auto extern_function = static_cast(function); + if (extern_function->extern_handler()) { + undefined = false; + // rcx = context + // rdx = target host function + mov(rdx, reinterpret_cast(extern_function->extern_handler())); + mov(r8, qword[rcx + offsetof(cpu::frontend::PPCContext, kernel_state)]); + auto thunk = backend()->guest_to_host_thunk(); + mov(rax, reinterpret_cast(thunk)); + call(rax); + ReloadECX(); + ReloadEDX(); + // rax = host return + } + } + if (undefined) { + CallNative(UndefinedCallExtern, reinterpret_cast(function)); } } diff --git a/src/xenia/cpu/backend/x64/x64_emitter.h b/src/xenia/cpu/backend/x64/x64_emitter.h index 72bd62877..f920c93c1 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.h +++ b/src/xenia/cpu/backend/x64/x64_emitter.h @@ -17,7 +17,6 @@ #include "xenia/cpu/hir/hir_builder.h" #include "xenia/cpu/hir/instr.h" #include "xenia/cpu/hir/value.h" -#include "xenia/cpu/symbol_info.h" #include "xenia/debug/function_trace_data.h" #include "xenia/memory.h" @@ -27,10 +26,7 @@ namespace xe { namespace cpu { -class DebugInfo; -class FunctionInfo; class Processor; -class SymbolInfo; } // namespace cpu } // namespace xe @@ -118,7 +114,7 @@ class X64Emitter : public Xbyak::CodeGenerator { Processor* processor() const { return processor_; } X64Backend* backend() const { return backend_; } - bool Emit(FunctionInfo* function_info, hir::HIRBuilder* builder, + bool Emit(GuestFunction* function, hir::HIRBuilder* builder, uint32_t debug_info_flags, DebugInfo* debug_info, void*& out_code_address, size_t& out_code_size, std::vector& out_source_map); @@ -163,9 +159,9 @@ class X64Emitter : public Xbyak::CodeGenerator { void Trap(uint16_t trap_type = 0); void UnimplementedInstr(const hir::Instr* i); - void Call(const hir::Instr* instr, FunctionInfo* symbol_info); + void Call(const hir::Instr* instr, GuestFunction* function); void CallIndirect(const hir::Instr* instr, const Xbyak::Reg64& reg); - void CallExtern(const hir::Instr* instr, const FunctionInfo* symbol_info); + void CallExtern(const hir::Instr* instr, const Function* function); void CallNative(void* fn); void CallNative(uint64_t (*fn)(void* raw_context)); void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0)); @@ -199,7 +195,7 @@ class X64Emitter : public Xbyak::CodeGenerator { size_t stack_size() const { return stack_size_; } protected: - void* Emplace(size_t stack_size, FunctionInfo* function_info = nullptr); + void* Emplace(size_t stack_size, GuestFunction* function = nullptr); bool Emit(hir::HIRBuilder* builder, size_t& out_stack_size); void EmitGetCurrentThreadId(); void EmitTraceUserCallReturn(); @@ -218,6 +214,7 @@ class X64Emitter : public Xbyak::CodeGenerator { DebugInfo* debug_info_ = nullptr; uint32_t debug_info_flags_ = 0; + debug::FunctionTraceData* trace_data_ = nullptr; Arena source_map_arena_; size_t stack_size_ = 0; diff --git a/src/xenia/cpu/backend/x64/x64_function.cc b/src/xenia/cpu/backend/x64/x64_function.cc index 8ca5a6e34..668726e8f 100644 --- a/src/xenia/cpu/backend/x64/x64_function.cc +++ b/src/xenia/cpu/backend/x64/x64_function.cc @@ -18,8 +18,8 @@ namespace cpu { namespace backend { namespace x64 { -X64Function::X64Function(FunctionInfo* symbol_info) - : Function(symbol_info), machine_code_(nullptr), machine_code_length_(0) {} +X64Function::X64Function(Module* module, uint32_t address) + : GuestFunction(module, address) {} X64Function::~X64Function() { // machine_code_ is freed by code cache. @@ -30,14 +30,6 @@ void X64Function::Setup(uint8_t* machine_code, size_t machine_code_length) { machine_code_length_ = machine_code_length; } -bool X64Function::AddBreakpointImpl(debug::Breakpoint* breakpoint) { - return false; -} - -bool X64Function::RemoveBreakpointImpl(debug::Breakpoint* breakpoint) { - return false; -} - bool X64Function::CallImpl(ThreadState* thread_state, uint32_t return_address) { auto backend = reinterpret_cast(thread_state->processor()->backend()); diff --git a/src/xenia/cpu/backend/x64/x64_function.h b/src/xenia/cpu/backend/x64/x64_function.h index 86a08b18b..1f5e114ed 100644 --- a/src/xenia/cpu/backend/x64/x64_function.h +++ b/src/xenia/cpu/backend/x64/x64_function.h @@ -11,7 +11,6 @@ #define XENIA_BACKEND_X64_X64_FUNCTION_H_ #include "xenia/cpu/function.h" -#include "xenia/cpu/symbol_info.h" #include "xenia/cpu/thread_state.h" namespace xe { @@ -19,10 +18,10 @@ namespace cpu { namespace backend { namespace x64 { -class X64Function : public Function { +class X64Function : public GuestFunction { public: - X64Function(FunctionInfo* symbol_info); - virtual ~X64Function(); + X64Function(Module* module, uint32_t address); + ~X64Function() override; uint8_t* machine_code() const override { return machine_code_; } size_t machine_code_length() const override { return machine_code_length_; } @@ -30,13 +29,11 @@ class X64Function : public Function { void Setup(uint8_t* machine_code, size_t machine_code_length); protected: - bool AddBreakpointImpl(debug::Breakpoint* breakpoint) override; - bool RemoveBreakpointImpl(debug::Breakpoint* breakpoint) override; bool CallImpl(ThreadState* thread_state, uint32_t return_address) override; private: - uint8_t* machine_code_; - size_t machine_code_length_; + uint8_t* machine_code_ = nullptr; + size_t machine_code_length_ = 0; }; } // namespace x64 diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index 465f8b034..42651b1f3 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -154,7 +154,7 @@ struct OffsetOp : Op { }; struct SymbolOp : Op { - FunctionInfo* value; + Function* value; protected: template @@ -162,7 +162,7 @@ struct SymbolOp : Op { template friend struct I; bool Load(const Instr::Op& op) { - this->value = op.symbol_info; + this->value = op.symbol; return true; } }; @@ -856,7 +856,8 @@ EMITTER_OPCODE_TABLE(OPCODE_TRAP_TRUE, TRAP_TRUE_I8, TRAP_TRUE_I16, // ============================================================================ struct CALL : Sequence> { static void Emit(X64Emitter& e, const EmitArgType& i) { - e.Call(i.instr, i.src1.value); + assert_true(i.src1.value->is_guest()); + e.Call(i.instr, static_cast(i.src1.value)); } }; EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL); @@ -867,60 +868,66 @@ EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL); struct CALL_TRUE_I8 : Sequence> { static void Emit(X64Emitter& e, const EmitArgType& i) { + assert_true(i.src2.value->is_guest()); e.test(i.src1, i.src1); Xbyak::Label skip; e.jz(skip); - e.Call(i.instr, i.src2.value); + e.Call(i.instr, static_cast(i.src2.value)); e.L(skip); } }; struct CALL_TRUE_I16 : Sequence> { static void Emit(X64Emitter& e, const EmitArgType& i) { + assert_true(i.src2.value->is_guest()); e.test(i.src1, i.src1); Xbyak::Label skip; e.jz(skip); - e.Call(i.instr, i.src2.value); + e.Call(i.instr, static_cast(i.src2.value)); e.L(skip); } }; struct CALL_TRUE_I32 : Sequence> { static void Emit(X64Emitter& e, const EmitArgType& i) { + assert_true(i.src2.value->is_guest()); e.test(i.src1, i.src1); Xbyak::Label skip; e.jz(skip); - e.Call(i.instr, i.src2.value); + e.Call(i.instr, static_cast(i.src2.value)); e.L(skip); } }; struct CALL_TRUE_I64 : Sequence> { static void Emit(X64Emitter& e, const EmitArgType& i) { + assert_true(i.src2.value->is_guest()); e.test(i.src1, i.src1); Xbyak::Label skip; e.jz(skip); - e.Call(i.instr, i.src2.value); + e.Call(i.instr, static_cast(i.src2.value)); e.L(skip); } }; struct CALL_TRUE_F32 : Sequence> { static void Emit(X64Emitter& e, const EmitArgType& i) { + assert_true(i.src2.value->is_guest()); e.vptest(i.src1, i.src1); Xbyak::Label skip; e.jz(skip); - e.Call(i.instr, i.src2.value); + e.Call(i.instr, static_cast(i.src2.value)); e.L(skip); } }; struct CALL_TRUE_F64 : Sequence> { static void Emit(X64Emitter& e, const EmitArgType& i) { + assert_true(i.src2.value->is_guest()); e.vptest(i.src1, i.src1); Xbyak::Label skip; e.jz(skip); - e.Call(i.instr, i.src2.value); + e.Call(i.instr, static_cast(i.src2.value)); e.L(skip); } }; diff --git a/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc b/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc index 10644b7a4..dfe31aaf4 100644 --- a/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc +++ b/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc @@ -91,9 +91,9 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) { case OPCODE_CALL_TRUE: if (i->src1.value->IsConstant()) { if (i->src1.value->IsConstantTrue()) { - auto symbol_info = i->src2.symbol_info; + auto symbol = i->src2.symbol; i->Replace(&OPCODE_CALL_info, i->flags); - i->src1.symbol_info = symbol_info; + i->src1.symbol = symbol; } else { i->Remove(); } @@ -101,13 +101,13 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) { break; case OPCODE_CALL_INDIRECT: if (i->src1.value->IsConstant()) { - FunctionInfo* symbol_info; - if (!processor_->LookupFunctionInfo( - (uint32_t)i->src1.value->constant.i32, &symbol_info)) { + auto function = processor_->LookupFunction( + uint32_t(i->src1.value->constant.i32)); + if (!function) { break; } i->Replace(&OPCODE_CALL_info, i->flags); - i->src1.symbol_info = symbol_info; + i->src1.symbol = function; } break; case OPCODE_CALL_INDIRECT_TRUE: diff --git a/src/xenia/cpu/debug_info.cc b/src/xenia/cpu/debug_info.cc index d3c71f87a..a685f930e 100644 --- a/src/xenia/cpu/debug_info.cc +++ b/src/xenia/cpu/debug_info.cc @@ -9,6 +9,7 @@ #include "xenia/cpu/debug_info.h" +#include #include namespace xe { diff --git a/src/xenia/cpu/debug_info.h b/src/xenia/cpu/debug_info.h index 2d13f694e..b31a69a25 100644 --- a/src/xenia/cpu/debug_info.h +++ b/src/xenia/cpu/debug_info.h @@ -13,8 +13,6 @@ #include #include -#include "xenia/debug/function_trace_data.h" - namespace xe { namespace cpu { @@ -37,6 +35,9 @@ enum DebugInfoFlags : uint32_t { kDebugInfoAll = 0xFFFFFFFF, }; +// DEPRECATED +// This will be getting removed or refactored to contain only on-demand +// disassembly data. class DebugInfo { public: DebugInfo(); @@ -53,8 +54,6 @@ class DebugInfo { instruction_result_count_ = value; } - debug::FunctionTraceData& trace_data() { return trace_data_; } - const char* source_disasm() const { return source_disasm_; } void set_source_disasm(char* value) { source_disasm_ = value; } const char* raw_hir_disasm() const { return raw_hir_disasm_; } @@ -70,8 +69,6 @@ class DebugInfo { uint32_t address_reference_count_; uint32_t instruction_result_count_; - debug::FunctionTraceData trace_data_; - char* source_disasm_; char* raw_hir_disasm_; char* hir_disasm_; diff --git a/src/xenia/cpu/frontend/ppc_emit_control.cc b/src/xenia/cpu/frontend/ppc_emit_control.cc index 633844987..179ab6800 100644 --- a/src/xenia/cpu/frontend/ppc_emit_control.cc +++ b/src/xenia/cpu/frontend/ppc_emit_control.cc @@ -53,7 +53,7 @@ int InstrEmit_branch(PPCHIRBuilder& f, const char* src, uint64_t cia, // recursion. uint32_t nia_value = nia->AsUint64() & 0xFFFFFFFF; bool is_recursion = false; - if (nia_value == f.symbol_info()->address() && lk) { + if (nia_value == f.function()->address() && lk) { is_recursion = true; } Label* label = is_recursion ? NULL : f.LookupLabel(nia_value); @@ -71,14 +71,14 @@ int InstrEmit_branch(PPCHIRBuilder& f, const char* src, uint64_t cia, } } else { // Call function. - auto symbol_info = f.LookupFunction(nia_value); + auto function = f.LookupFunction(nia_value); if (cond) { if (!expect_true) { cond = f.IsFalse(cond); } - f.CallTrue(cond, symbol_info, call_flags); + f.CallTrue(cond, function, call_flags); } else { - f.Call(symbol_info, call_flags); + f.Call(function, call_flags); } } } else { @@ -419,7 +419,7 @@ XEEMITTER(mcrf, 0x4C000000, XL)(PPCHIRBuilder& f, InstrData& i) { // System linkage (A-24) XEEMITTER(sc, 0x44000002, SC)(PPCHIRBuilder& f, InstrData& i) { - f.CallExtern(f.symbol_info()); + f.CallExtern(f.function()); return 0; } diff --git a/src/xenia/cpu/frontend/ppc_frontend.cc b/src/xenia/cpu/frontend/ppc_frontend.cc index 5dc9148ed..f0e7f1034 100644 --- a/src/xenia/cpu/frontend/ppc_frontend.cc +++ b/src/xenia/cpu/frontend/ppc_frontend.cc @@ -86,7 +86,7 @@ bool PPCFrontend::Initialize() { return true; } -bool PPCFrontend::DeclareFunction(FunctionInfo* symbol_info) { +bool PPCFrontend::DeclareFunction(GuestFunction* function) { // Could scan or something here. // Could also check to see if it's a well-known function type and classify // for later. @@ -95,12 +95,10 @@ bool PPCFrontend::DeclareFunction(FunctionInfo* symbol_info) { return true; } -bool PPCFrontend::DefineFunction(FunctionInfo* symbol_info, - uint32_t debug_info_flags, - Function** out_function) { - PPCTranslator* translator = translator_pool_.Allocate(this); - bool result = - translator->Translate(symbol_info, debug_info_flags, out_function); +bool PPCFrontend::DefineFunction(GuestFunction* function, + uint32_t debug_info_flags) { + auto translator = translator_pool_.Allocate(this); + bool result = translator->Translate(function, debug_info_flags); translator_pool_.Release(translator); return result; } diff --git a/src/xenia/cpu/frontend/ppc_frontend.h b/src/xenia/cpu/frontend/ppc_frontend.h index 05718caa8..0f374457c 100644 --- a/src/xenia/cpu/frontend/ppc_frontend.h +++ b/src/xenia/cpu/frontend/ppc_frontend.h @@ -17,7 +17,6 @@ #include "xenia/base/type_pool.h" #include "xenia/cpu/frontend/context_info.h" #include "xenia/cpu/function.h" -#include "xenia/cpu/symbol_info.h" #include "xenia/memory.h" namespace xe { @@ -35,8 +34,8 @@ class PPCTranslator; struct PPCBuiltins { xe::mutex global_lock; bool global_lock_taken; - FunctionInfo* check_global_lock; - FunctionInfo* handle_global_lock; + Function* check_global_lock; + Function* handle_global_lock; }; class PPCFrontend { @@ -51,9 +50,8 @@ class PPCFrontend { ContextInfo* context_info() const { return context_info_.get(); } PPCBuiltins* builtins() { return &builtins_; } - bool DeclareFunction(FunctionInfo* symbol_info); - bool DefineFunction(FunctionInfo* symbol_info, uint32_t debug_info_flags, - Function** out_function); + bool DeclareFunction(GuestFunction* function); + bool DefineFunction(GuestFunction* function, uint32_t debug_info_flags); private: Processor* processor_; diff --git a/src/xenia/cpu/frontend/ppc_hir_builder.cc b/src/xenia/cpu/frontend/ppc_hir_builder.cc index a53c78b05..ebd2dfa4c 100644 --- a/src/xenia/cpu/frontend/ppc_hir_builder.cc +++ b/src/xenia/cpu/frontend/ppc_hir_builder.cc @@ -40,27 +40,29 @@ PPCHIRBuilder::PPCHIRBuilder(PPCFrontend* frontend) PPCHIRBuilder::~PPCHIRBuilder() = default; void PPCHIRBuilder::Reset() { + function_ = nullptr; start_address_ = 0; + instr_count_ = 0; instr_offset_list_ = NULL; label_list_ = NULL; with_debug_info_ = false; HIRBuilder::Reset(); } -bool PPCHIRBuilder::Emit(FunctionInfo* symbol_info, uint32_t flags) { +bool PPCHIRBuilder::Emit(GuestFunction* function, uint32_t flags) { SCOPE_profile_cpu_f("cpu"); Memory* memory = frontend_->memory(); - symbol_info_ = symbol_info; - start_address_ = symbol_info->address(); - instr_count_ = (symbol_info->end_address() - symbol_info->address()) / 4 + 1; + function_ = function; + start_address_ = function_->address(); + instr_count_ = (function_->end_address() - function_->address()) / 4 + 1; with_debug_info_ = (flags & EMIT_DEBUG_COMMENTS) == EMIT_DEBUG_COMMENTS; if (with_debug_info_) { - CommentFormat("%s fn %.8X-%.8X %s", symbol_info->module()->name().c_str(), - symbol_info->address(), symbol_info->end_address(), - symbol_info->name().c_str()); + CommentFormat("%s fn %.8X-%.8X %s", function_->module()->name().c_str(), + function_->address(), function_->end_address(), + function_->name().c_str()); } // Allocate offset list. @@ -78,8 +80,8 @@ bool PPCHIRBuilder::Emit(FunctionInfo* symbol_info, uint32_t flags) { // Always mark entry with label. label_list_[0] = NewLabel(); - uint32_t start_address = symbol_info->address(); - uint32_t end_address = symbol_info->end_address(); + uint32_t start_address = function_->address(); + uint32_t end_address = function_->end_address(); InstrData i; for (uint32_t address = start_address, offset = 0; address <= end_address; address += 4, offset++) { @@ -165,13 +167,8 @@ void PPCHIRBuilder::AnnotateLabel(uint32_t address, Label* label) { memcpy(label->name, name_buffer, sizeof(name_buffer)); } -FunctionInfo* PPCHIRBuilder::LookupFunction(uint32_t address) { - Processor* processor = frontend_->processor(); - FunctionInfo* symbol_info; - if (!processor->LookupFunctionInfo(address, &symbol_info)) { - return nullptr; - } - return symbol_info; +Function* PPCHIRBuilder::LookupFunction(uint32_t address) { + return frontend_->processor()->LookupFunction(address); } Label* PPCHIRBuilder::LookupLabel(uint32_t address) { diff --git a/src/xenia/cpu/frontend/ppc_hir_builder.h b/src/xenia/cpu/frontend/ppc_hir_builder.h index 0611a4af1..7d1900d92 100644 --- a/src/xenia/cpu/frontend/ppc_hir_builder.h +++ b/src/xenia/cpu/frontend/ppc_hir_builder.h @@ -13,7 +13,6 @@ #include "xenia/base/string_buffer.h" #include "xenia/cpu/hir/hir_builder.h" #include "xenia/cpu/function.h" -#include "xenia/cpu/symbol_info.h" namespace xe { namespace cpu { @@ -28,18 +27,18 @@ class PPCHIRBuilder : public hir::HIRBuilder { public: PPCHIRBuilder(PPCFrontend* frontend); - virtual ~PPCHIRBuilder(); + ~PPCHIRBuilder() override; - virtual void Reset(); + void Reset() override; enum EmitFlags { // Emit comment nodes. EMIT_DEBUG_COMMENTS = 1 << 0, }; - bool Emit(FunctionInfo* symbol_info, uint32_t flags); + bool Emit(GuestFunction* function, uint32_t flags); - FunctionInfo* symbol_info() const { return symbol_info_; } - FunctionInfo* LookupFunction(uint32_t address); + GuestFunction* function() const { return function_; } + Function* LookupFunction(uint32_t address); Label* LookupLabel(uint32_t address); Value* LoadLR(); @@ -83,7 +82,6 @@ class PPCHIRBuilder : public hir::HIRBuilder { private: void AnnotateLabel(uint32_t address, Label* label); - private: PPCFrontend* frontend_; // Reset whenever needed: @@ -91,7 +89,7 @@ class PPCHIRBuilder : public hir::HIRBuilder { // Reset each Emit: bool with_debug_info_; - FunctionInfo* symbol_info_; + GuestFunction* function_; uint64_t start_address_; uint64_t instr_count_; Instr** instr_offset_list_; diff --git a/src/xenia/cpu/frontend/ppc_scanner.cc b/src/xenia/cpu/frontend/ppc_scanner.cc index ad5d699b2..a8e157781 100644 --- a/src/xenia/cpu/frontend/ppc_scanner.cc +++ b/src/xenia/cpu/frontend/ppc_scanner.cc @@ -35,11 +35,10 @@ PPCScanner::~PPCScanner() {} bool PPCScanner::IsRestGprLr(uint32_t address) { auto function = frontend_->processor()->QueryFunction(address); - return function && - function->symbol_info()->behavior() == FunctionBehavior::kEpilogReturn; + return function && function->behavior() == Function::Behavior::kEpilogReturn; } -bool PPCScanner::Scan(FunctionInfo* symbol_info, DebugInfo* debug_info) { +bool PPCScanner::Scan(GuestFunction* function, DebugInfo* debug_info) { // This is a simple basic block analyizer. It walks the start address to the // end address looking for branches. Each span of instructions between // branches is considered a basic block. When the last blr (that has no @@ -49,14 +48,14 @@ bool PPCScanner::Scan(FunctionInfo* symbol_info, DebugInfo* debug_info) { Memory* memory = frontend_->memory(); - LOGPPC("Analyzing function %.8X...", symbol_info->address()); + LOGPPC("Analyzing function %.8X...", function->address()); // For debug info, only if needed. uint32_t address_reference_count = 0; uint32_t instruction_result_count = 0; - uint32_t start_address = static_cast(symbol_info->address()); - uint32_t end_address = static_cast(symbol_info->end_address()); + uint32_t start_address = static_cast(function->address()); + uint32_t end_address = static_cast(function->end_address()); uint32_t address = start_address; uint32_t furthest_target = start_address; size_t blocks_found = 0; @@ -270,7 +269,7 @@ bool PPCScanner::Scan(FunctionInfo* symbol_info, DebugInfo* debug_info) { LOGPPC("Function ran under: %.8X-%.8X ended at %.8X", start_address, end_address, address + 4); } - symbol_info->set_end_address(address); + function->set_end_address(address); // If there's spare bits at the end, split the function. // TODO(benvanik): splitting? @@ -289,13 +288,13 @@ bool PPCScanner::Scan(FunctionInfo* symbol_info, DebugInfo* debug_info) { return true; } -std::vector PPCScanner::FindBlocks(FunctionInfo* symbol_info) { +std::vector PPCScanner::FindBlocks(GuestFunction* function) { Memory* memory = frontend_->memory(); std::map block_map; - uint32_t start_address = symbol_info->address(); - uint32_t end_address = symbol_info->end_address(); + uint32_t start_address = function->address(); + uint32_t end_address = function->end_address(); bool in_block = false; uint32_t block_start = 0; InstrData i; diff --git a/src/xenia/cpu/frontend/ppc_scanner.h b/src/xenia/cpu/frontend/ppc_scanner.h index ad264cdcd..b2c893d46 100644 --- a/src/xenia/cpu/frontend/ppc_scanner.h +++ b/src/xenia/cpu/frontend/ppc_scanner.h @@ -13,7 +13,7 @@ #include #include "xenia/cpu/debug_info.h" -#include "xenia/cpu/symbol_info.h" +#include "xenia/cpu/function.h" namespace xe { namespace cpu { @@ -21,25 +21,24 @@ namespace frontend { class PPCFrontend; -typedef struct BlockInfo_t { +struct BlockInfo { uint32_t start_address; uint32_t end_address; -} BlockInfo; +}; class PPCScanner { public: PPCScanner(PPCFrontend* frontend); ~PPCScanner(); - bool Scan(FunctionInfo* symbol_info, DebugInfo* debug_info); + bool Scan(GuestFunction* function, DebugInfo* debug_info); - std::vector FindBlocks(FunctionInfo* symbol_info); + std::vector FindBlocks(GuestFunction* function); private: bool IsRestGprLr(uint32_t address); - private: - PPCFrontend* frontend_; + PPCFrontend* frontend_ = nullptr; }; } // namespace frontend diff --git a/src/xenia/cpu/frontend/ppc_translator.cc b/src/xenia/cpu/frontend/ppc_translator.cc index 7d02d46c1..03eb5e3b4 100644 --- a/src/xenia/cpu/frontend/ppc_translator.cc +++ b/src/xenia/cpu/frontend/ppc_translator.cc @@ -99,9 +99,8 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) { PPCTranslator::~PPCTranslator() = default; -bool PPCTranslator::Translate(FunctionInfo* symbol_info, - uint32_t debug_info_flags, - Function** out_function) { +bool PPCTranslator::Translate(GuestFunction* function, + uint32_t debug_info_flags) { SCOPE_profile_cpu_f("cpu"); // Reset() all caching when we leave. @@ -137,7 +136,7 @@ bool PPCTranslator::Translate(FunctionInfo* symbol_info, } // Scan the function to find its extents and gather debug data. - if (!scanner_->Scan(symbol_info, debug_info.get())) { + if (!scanner_->Scan(function, debug_info.get())) { return false; } @@ -153,19 +152,19 @@ bool PPCTranslator::Translate(FunctionInfo* symbol_info, if (debug_info_flags & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) { // Additional space for instruction coverage counts. trace_data_size += debug::FunctionTraceData::SizeOfInstructionCounts( - symbol_info->address(), symbol_info->end_address()); + function->address(), function->end_address()); } uint8_t* trace_data = debugger->AllocateFunctionTraceData(trace_data_size); if (trace_data) { - debug_info->trace_data().Reset(trace_data, trace_data_size, - symbol_info->address(), - symbol_info->end_address()); + function->trace_data().Reset(trace_data, trace_data_size, + function->address(), + function->end_address()); } } // Stash source. if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmSource) { - DumpSource(symbol_info, &string_buffer_); + DumpSource(function, &string_buffer_); debug_info->set_source_disasm(string_buffer_.ToString()); string_buffer_.Reset(); } @@ -179,7 +178,7 @@ bool PPCTranslator::Translate(FunctionInfo* symbol_info, if (debug_info) { emit_flags |= PPCHIRBuilder::EMIT_DEBUG_COMMENTS; } - if (!builder_->Emit(symbol_info, emit_flags)) { + if (!builder_->Emit(function, emit_flags)) { return false; } @@ -203,27 +202,26 @@ bool PPCTranslator::Translate(FunctionInfo* symbol_info, } // Assemble to backend machine code. - if (!assembler_->Assemble(symbol_info, builder_.get(), debug_info_flags, - std::move(debug_info), out_function)) { + if (!assembler_->Assemble(function, builder_.get(), debug_info_flags, + std::move(debug_info))) { return false; } return true; }; -void PPCTranslator::DumpSource(FunctionInfo* symbol_info, +void PPCTranslator::DumpSource(GuestFunction* function, StringBuffer* string_buffer) { Memory* memory = frontend_->memory(); string_buffer->AppendFormat( - "%s fn %.8X-%.8X %s\n", symbol_info->module()->name().c_str(), - symbol_info->address(), symbol_info->end_address(), - symbol_info->name().c_str()); + "%s fn %.8X-%.8X %s\n", function->module()->name().c_str(), + function->address(), function->end_address(), function->name().c_str()); - auto blocks = scanner_->FindBlocks(symbol_info); + auto blocks = scanner_->FindBlocks(function); - uint32_t start_address = symbol_info->address(); - uint32_t end_address = symbol_info->end_address(); + uint32_t start_address = function->address(); + uint32_t end_address = function->end_address(); InstrData i; auto block_it = blocks.begin(); for (uint32_t address = start_address, offset = 0; address <= end_address; diff --git a/src/xenia/cpu/frontend/ppc_translator.h b/src/xenia/cpu/frontend/ppc_translator.h index f0263c859..7aaccf6b9 100644 --- a/src/xenia/cpu/frontend/ppc_translator.h +++ b/src/xenia/cpu/frontend/ppc_translator.h @@ -15,7 +15,7 @@ #include "xenia/base/string_buffer.h" #include "xenia/cpu/backend/assembler.h" #include "xenia/cpu/compiler/compiler.h" -#include "xenia/cpu/symbol_info.h" +#include "xenia/cpu/function.h" namespace xe { namespace cpu { @@ -30,13 +30,11 @@ class PPCTranslator { PPCTranslator(PPCFrontend* frontend); ~PPCTranslator(); - bool Translate(FunctionInfo* symbol_info, uint32_t debug_info_flags, - Function** out_function); + bool Translate(GuestFunction* function, uint32_t debug_info_flags); private: - void DumpSource(FunctionInfo* symbol_info, StringBuffer* string_buffer); + void DumpSource(GuestFunction* function, StringBuffer* string_buffer); - private: PPCFrontend* frontend_; std::unique_ptr scanner_; std::unique_ptr builder_; diff --git a/src/xenia/cpu/frontend/testing/ppc_testing_main.cc b/src/xenia/cpu/frontend/testing/ppc_testing_main.cc index abcfbe1f2..0660c80e1 100644 --- a/src/xenia/cpu/frontend/testing/ppc_testing_main.cc +++ b/src/xenia/cpu/frontend/testing/ppc_testing_main.cc @@ -224,8 +224,8 @@ class TestRunner { } // Execute test. - xe::cpu::Function* fn = nullptr; - if (!processor->ResolveFunction(test_case.address, &fn)) { + auto fn = processor->ResolveFunction(test_case.address); + if (!fn) { XELOGE("Entry function not found"); return false; } @@ -238,7 +238,9 @@ class TestRunner { bool result = CheckTestResults(test_case); if (!result) { // Also dump all disasm/etc. - fn->debug_info()->Dump(); + if (fn->is_guest()) { + static_cast(fn)->debug_info()->Dump(); + } } return result; diff --git a/src/xenia/cpu/function.cc b/src/xenia/cpu/function.cc index e9f915b61..256f3a77d 100644 --- a/src/xenia/cpu/function.cc +++ b/src/xenia/cpu/function.cc @@ -10,7 +10,7 @@ #include "xenia/cpu/function.h" #include "xenia/base/logging.h" -#include "xenia/cpu/symbol_info.h" +#include "xenia/cpu/symbol.h" #include "xenia/cpu/thread_state.h" namespace xe { @@ -18,12 +18,56 @@ namespace cpu { using xe::debug::Breakpoint; -Function::Function(FunctionInfo* symbol_info) - : address_(symbol_info->address()), symbol_info_(symbol_info) {} +Function::Function(Module* module, uint32_t address) + : Symbol(Symbol::Type::kFunction, module, address) {} Function::~Function() = default; -const SourceMapEntry* Function::LookupSourceOffset(uint32_t offset) const { +BuiltinFunction::BuiltinFunction(Module* module, uint32_t address) + : Function(module, address) { + behavior_ = Behavior::kBuiltin; +} + +BuiltinFunction::~BuiltinFunction() = default; + +void BuiltinFunction::SetupBuiltin(Handler handler, void* arg0, void* arg1) { + behavior_ = Behavior::kBuiltin; + handler_ = handler; + arg0_ = arg0; + arg1_ = arg1; +} + +bool BuiltinFunction::Call(ThreadState* thread_state, uint32_t return_address) { + // SCOPE_profile_cpu_f("cpu"); + + ThreadState* original_thread_state = ThreadState::Get(); + if (original_thread_state != thread_state) { + ThreadState::Bind(thread_state); + } + + assert_not_null(handler_); + handler_(thread_state->context(), arg0_, arg1_); + + if (original_thread_state != thread_state) { + ThreadState::Bind(original_thread_state); + } + + return true; +} + +GuestFunction::GuestFunction(Module* module, uint32_t address) + : Function(module, address) { + behavior_ = Behavior::kDefault; +} + +GuestFunction::~GuestFunction() = default; + +void GuestFunction::SetupExtern(ExternHandler handler) { + behavior_ = Behavior::kExtern; + extern_handler_ = handler; +} + +const SourceMapEntry* GuestFunction::LookupSourceOffset(uint32_t offset) const { // TODO(benvanik): binary search? We know the list is sorted by code order. for (size_t i = 0; i < source_map_.size(); ++i) { const auto& entry = source_map_[i]; @@ -34,7 +78,7 @@ const SourceMapEntry* Function::LookupSourceOffset(uint32_t offset) const { return nullptr; } -const SourceMapEntry* Function::LookupHIROffset(uint32_t offset) const { +const SourceMapEntry* GuestFunction::LookupHIROffset(uint32_t offset) const { // TODO(benvanik): binary search? We know the list is sorted by code order. for (size_t i = 0; i < source_map_.size(); ++i) { const auto& entry = source_map_[i]; @@ -45,7 +89,7 @@ const SourceMapEntry* Function::LookupHIROffset(uint32_t offset) const { return nullptr; } -const SourceMapEntry* Function::LookupCodeOffset(uint32_t offset) const { +const SourceMapEntry* GuestFunction::LookupCodeOffset(uint32_t offset) const { // TODO(benvanik): binary search? We know the list is sorted by code order. for (int64_t i = source_map_.size() - 1; i >= 0; --i) { const auto& entry = source_map_[i]; @@ -56,49 +100,7 @@ const SourceMapEntry* Function::LookupCodeOffset(uint32_t offset) const { return source_map_.empty() ? nullptr : &source_map_[0]; } -bool Function::AddBreakpoint(Breakpoint* breakpoint) { - std::lock_guard guard(lock_); - bool found = false; - for (auto other : breakpoints_) { - if (other == breakpoint) { - found = true; - break; - } - } - if (found) { - return true; - } else { - breakpoints_.push_back(breakpoint); - return AddBreakpointImpl(breakpoint); - } -} - -bool Function::RemoveBreakpoint(Breakpoint* breakpoint) { - std::lock_guard guard(lock_); - for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) { - if (*it == breakpoint) { - if (!RemoveBreakpointImpl(breakpoint)) { - return false; - } - breakpoints_.erase(it); - } - } - return false; -} - -Breakpoint* Function::FindBreakpoint(uint32_t address) { - std::lock_guard guard(lock_); - Breakpoint* result = nullptr; - for (auto breakpoint : breakpoints_) { - if (breakpoint->address() == address) { - result = breakpoint; - break; - } - } - return result; -} - -bool Function::Call(ThreadState* thread_state, uint32_t return_address) { +bool GuestFunction::Call(ThreadState* thread_state, uint32_t return_address) { // SCOPE_profile_cpu_f("cpu"); ThreadState* original_thread_state = ThreadState::Get(); @@ -106,29 +108,25 @@ bool Function::Call(ThreadState* thread_state, uint32_t return_address) { ThreadState::Bind(thread_state); } - bool result = true; - - if (symbol_info_->behavior() == FunctionBehavior::kBuiltin) { - auto handler = symbol_info_->builtin_handler(); - assert_not_null(handler); - handler(thread_state->context(), symbol_info_->builtin_arg0(), - symbol_info_->builtin_arg1()); - } else if (symbol_info_->behavior() == FunctionBehavior::kExtern) { - auto handler = symbol_info_->extern_handler(); - if (handler) { - handler(thread_state->context(), thread_state->context()->kernel_state); + bool result = false; + if (behavior_ == Behavior::kExtern) { + // Special handling for extern functions to speed things up (we don't + // trampoline into guest code only to trampoline back out). + if (extern_handler_) { + extern_handler_(thread_state->context(), + thread_state->context()->kernel_state); } else { - XELOGW("undefined extern call to %.8X %s", symbol_info_->address(), - symbol_info_->name().c_str()); + XELOGW("undefined extern call to %.8X %s", address(), name().c_str()); result = false; } } else { - CallImpl(thread_state, return_address); + result = CallImpl(thread_state, return_address); } if (original_thread_state != thread_state) { ThreadState::Bind(original_thread_state); } + return result; } diff --git a/src/xenia/cpu/function.h b/src/xenia/cpu/function.h index 74e758aaf..ad1d799b6 100644 --- a/src/xenia/cpu/function.h +++ b/src/xenia/cpu/function.h @@ -11,32 +11,89 @@ #define XENIA_CPU_FUNCTION_H_ #include -#include #include #include "xenia/base/mutex.h" #include "xenia/cpu/debug_info.h" +#include "xenia/cpu/frontend/ppc_context.h" +#include "xenia/cpu/symbol.h" #include "xenia/cpu/thread_state.h" #include "xenia/debug/breakpoint.h" +#include "xenia/debug/function_trace_data.h" namespace xe { namespace cpu { -class FunctionInfo; - struct SourceMapEntry { uint32_t source_offset; // Original source address/offset. uint32_t hir_offset; // Block ordinal (16b) | Instr ordinal (16b) uint32_t code_offset; // Offset from emitted code start. }; -class Function { +class Function : public Symbol { public: - Function(FunctionInfo* symbol_info); - virtual ~Function(); + enum class Behavior { + kDefault = 0, + kProlog, + kEpilog, + kEpilogReturn, + kBuiltin, + kExtern, + }; + + ~Function() override; uint32_t address() const { return address_; } - FunctionInfo* symbol_info() const { return symbol_info_; } + bool has_end_address() const { return end_address_ > 0; } + uint32_t end_address() const { return end_address_; } + void set_end_address(uint32_t value) { end_address_ = value; } + Behavior behavior() const { return behavior_; } + void set_behavior(Behavior value) { behavior_ = value; } + bool is_guest() const { return behavior_ != Behavior::kBuiltin; } + + virtual bool Call(ThreadState* thread_state, uint32_t return_address) = 0; + + protected: + Function(Module* module, uint32_t address); + + uint32_t end_address_ = 0; + Behavior behavior_ = Behavior::kDefault; +}; + +class BuiltinFunction : public Function { + public: + typedef void (*Handler)(frontend::PPCContext* ppc_context, void* arg0, + void* arg1); + + BuiltinFunction(Module* module, uint32_t address); + ~BuiltinFunction() override; + + void SetupBuiltin(Handler handler, void* arg0, void* arg1); + + Handler handler() const { return handler_; } + void* arg0() const { return arg0_; } + void* arg1() const { return arg1_; } + + bool Call(ThreadState* thread_state, uint32_t return_address) override; + + protected: + Handler handler_ = nullptr; + void* arg0_ = nullptr; + void* arg1_ = nullptr; +}; + +class GuestFunction : public Function { + public: + typedef void (*ExternHandler)(frontend::PPCContext* ppc_context, + kernel::KernelState* kernel_state); + + GuestFunction(Module* module, uint32_t address); + ~GuestFunction() override; + + uint32_t address() const { return address_; } + bool has_end_address() const { return end_address_ > 0; } + uint32_t end_address() const { return end_address_; } + void set_end_address(uint32_t value) { end_address_ = value; } virtual uint8_t* machine_code() const = 0; virtual size_t machine_code_length() const = 0; @@ -45,32 +102,26 @@ class Function { void set_debug_info(std::unique_ptr debug_info) { debug_info_ = std::move(debug_info); } + debug::FunctionTraceData& trace_data() { return trace_data_; } std::vector& source_map() { return source_map_; } + ExternHandler extern_handler() const { return extern_handler_; } + void SetupExtern(ExternHandler handler); + const SourceMapEntry* LookupSourceOffset(uint32_t offset) const; const SourceMapEntry* LookupHIROffset(uint32_t offset) const; const SourceMapEntry* LookupCodeOffset(uint32_t offset) const; - bool AddBreakpoint(debug::Breakpoint* breakpoint); - bool RemoveBreakpoint(debug::Breakpoint* breakpoint); - - bool Call(ThreadState* thread_state, uint32_t return_address); + bool Call(ThreadState* thread_state, uint32_t return_address) override; protected: - debug::Breakpoint* FindBreakpoint(uint32_t address); - virtual bool AddBreakpointImpl(debug::Breakpoint* breakpoint) { return 0; } - virtual bool RemoveBreakpointImpl(debug::Breakpoint* breakpoint) { return 0; } virtual bool CallImpl(ThreadState* thread_state, uint32_t return_address) = 0; protected: - uint32_t address_; - FunctionInfo* symbol_info_; std::unique_ptr debug_info_; + debug::FunctionTraceData trace_data_; std::vector source_map_; - - // TODO(benvanik): move elsewhere? DebugData? - xe::mutex lock_; - std::vector breakpoints_; + ExternHandler extern_handler_ = nullptr; }; } // namespace cpu diff --git a/src/xenia/cpu/hir/hir_builder.cc b/src/xenia/cpu/hir/hir_builder.cc index 2d14dd180..6bd644f16 100644 --- a/src/xenia/cpu/hir/hir_builder.cc +++ b/src/xenia/cpu/hir/hir_builder.cc @@ -13,10 +13,11 @@ #include #include "xenia/base/assert.h" +#include "xenia/cpu/function.h" #include "xenia/cpu/hir/block.h" #include "xenia/cpu/hir/instr.h" #include "xenia/cpu/hir/label.h" -#include "xenia/cpu/symbol_info.h" +#include "xenia/cpu/symbol.h" #include "xenia/profiling.h" // Will scribble arena memory to hopefully find use before clears. @@ -162,7 +163,7 @@ void HIRBuilder::DumpOp(StringBuffer* str, OpcodeSignatureType sig_type, break; case OPCODE_SIG_TYPE_S: if (true) { - auto target = op->symbol_info; + auto target = op->symbol; str->Append(!target->name().empty() ? target->name() : ""); } break; @@ -820,25 +821,24 @@ void HIRBuilder::TrapTrue(Value* cond, uint16_t trap_code) { EndBlock(); } -void HIRBuilder::Call(FunctionInfo* symbol_info, uint16_t call_flags) { +void HIRBuilder::Call(Function* symbol, uint16_t call_flags) { Instr* i = AppendInstr(OPCODE_CALL_info, call_flags); - i->src1.symbol_info = symbol_info; + i->src1.symbol = symbol; i->src2.value = i->src3.value = NULL; EndBlock(); } -void HIRBuilder::CallTrue(Value* cond, FunctionInfo* symbol_info, - uint16_t call_flags) { +void HIRBuilder::CallTrue(Value* cond, Function* symbol, uint16_t call_flags) { if (cond->IsConstant()) { if (cond->IsConstantTrue()) { - Call(symbol_info, call_flags); + Call(symbol, call_flags); } return; } Instr* i = AppendInstr(OPCODE_CALL_TRUE_info, call_flags); i->set_src1(cond); - i->src2.symbol_info = symbol_info; + i->src2.symbol = symbol; i->src3.value = NULL; EndBlock(); } @@ -868,9 +868,9 @@ void HIRBuilder::CallIndirectTrue(Value* cond, Value* value, EndBlock(); } -void HIRBuilder::CallExtern(FunctionInfo* symbol_info) { +void HIRBuilder::CallExtern(Function* symbol) { Instr* i = AppendInstr(OPCODE_CALL_EXTERN_info, 0); - i->src1.symbol_info = symbol_info; + i->src1.symbol = symbol; i->src2.value = i->src3.value = NULL; EndBlock(); } diff --git a/src/xenia/cpu/hir/hir_builder.h b/src/xenia/cpu/hir/hir_builder.h index 00a6f6b96..1e0966d9a 100644 --- a/src/xenia/cpu/hir/hir_builder.h +++ b/src/xenia/cpu/hir/hir_builder.h @@ -83,12 +83,11 @@ class HIRBuilder { void Trap(uint16_t trap_code = 0); void TrapTrue(Value* cond, uint16_t trap_code = 0); - void Call(FunctionInfo* symbol_info, uint16_t call_flags = 0); - void CallTrue(Value* cond, FunctionInfo* symbol_info, - uint16_t call_flags = 0); + void Call(Function* symbol, uint16_t call_flags = 0); + void CallTrue(Value* cond, Function* symbol, uint16_t call_flags = 0); void CallIndirect(Value* value, uint16_t call_flags = 0); void CallIndirectTrue(Value* cond, Value* value, uint16_t call_flags = 0); - void CallExtern(FunctionInfo* symbol_info); + void CallExtern(Function* symbol); void Return(); void ReturnTrue(Value* cond); void SetReturnAddress(Value* value); diff --git a/src/xenia/cpu/hir/instr.h b/src/xenia/cpu/hir/instr.h index eee365acd..6c9ad9731 100644 --- a/src/xenia/cpu/hir/instr.h +++ b/src/xenia/cpu/hir/instr.h @@ -15,7 +15,7 @@ namespace xe { namespace cpu { -class FunctionInfo; +class Function; } // namespace cpu } // namespace xe @@ -37,7 +37,7 @@ class Instr { uint32_t ordinal; typedef union { - FunctionInfo* symbol_info; + Function* symbol; Label* label; Value* value; uint64_t offset; diff --git a/src/xenia/cpu/instrument.cc b/src/xenia/cpu/instrument.cc deleted file mode 100644 index 432316e3b..000000000 --- a/src/xenia/cpu/instrument.cc +++ /dev/null @@ -1,114 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/cpu/instrument.h" - -#include "xenia/memory.h" -#include "xenia/cpu/function.h" -#include "xenia/cpu/processor.h" - -namespace xe { -namespace cpu { - -Instrument::Instrument(Processor* processor) - : processor_(processor), - memory_(processor->memory()), - is_attached_(false) {} - -Instrument::~Instrument() { - if (is_attached_) { - Detach(); - } -} - -bool Instrument::Attach() { - if (is_attached_) { - return false; - } - // runtime->AttachInstrument(this); - is_attached_ = true; - return true; -} - -bool Instrument::Detach() { - if (!is_attached_) { - return false; - } - is_attached_ = false; - // runtime->DetachInstrument(this); - return true; -} - -FunctionInstrument::FunctionInstrument(Processor* processor, Function* function) - : Instrument(processor), target_(function) {} - -bool FunctionInstrument::Attach() { - if (!Instrument::Attach()) { - return false; - } - - // Function impl attach: - // - add instrument to list - // JIT: transition to instrumented state - // - rewrite enter/exit to jump to instrumentation thunk - // - some sort of locking required? - - return true; -} - -bool FunctionInstrument::Detach() { - if (!Instrument::Detach()) { - return false; - } - - // - - return true; -} - -void FunctionInstrument::Enter(ThreadState* thread_state) { - // ? Placement new? How to get instance type? -} - -void FunctionInstrument::Exit(ThreadState* thread_state) { - // -} - -MemoryInstrument::MemoryInstrument(Processor* processor, uint32_t address, - uint32_t end_address) - : Instrument(processor), address_(address), end_address_(end_address) {} - -bool MemoryInstrument::Attach() { - if (!Instrument::Attach()) { - return false; - } - - // TODO(benvanik): protect memory page, catch exception - // https://github.com/frida/frida-gum/blob/master/gum/gummemoryaccessmonitor.c - - return true; -} - -bool MemoryInstrument::Detach() { - if (!Instrument::Detach()) { - return false; - } - - // - - return true; -} - -void MemoryInstrument::Access(ThreadState* thread_state, uint32_t address, - AccessType type) { - // TODO(benvanik): get thread local instance -} - -} // namespace cpu -} // namespace xe diff --git a/src/xenia/cpu/instrument.h b/src/xenia/cpu/instrument.h deleted file mode 100644 index 156e90168..000000000 --- a/src/xenia/cpu/instrument.h +++ /dev/null @@ -1,142 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_CPU_INSTRUMENT_H_ -#define XENIA_CPU_INSTRUMENT_H_ - -#include - -namespace xe { -namespace cpu { - -class Function; -class Memory; -class Processor; -class ThreadState; - -class Instrument { - public: - Instrument(Processor* processor); - virtual ~Instrument(); - - Processor* processor() const { return processor_; } - Memory* memory() const { return memory_; } - bool is_attached() const { return is_attached_; } - - virtual bool Attach(); - virtual bool Detach(); - - private: - Processor* processor_; - Memory* memory_; - bool is_attached_; -}; - -class FunctionInstrument : public Instrument { - public: - FunctionInstrument(Processor* processor, Function* function); - virtual ~FunctionInstrument() {} - - Function* target() const { return target_; } - - virtual bool Attach(); - virtual bool Detach(); - - public: - void Enter(ThreadState* thread_state); - void Exit(ThreadState* thread_state); - - protected: - class Instance { - public: - Instance(FunctionInstrument* instrument) : instrument_(instrument) {} - virtual ~Instance() {} - - FunctionInstrument* instrument() const { return instrument_; } - - virtual void OnEnter(ThreadState* thread_state) = 0; - virtual void OnExit(ThreadState* thread_state) = 0; - - // TODO(benvanik): utilities: - // Log(...) - // DebugBreak(type) - // GetArg(N)/GetReturn()/SetReturn(V) ? - // CallSelf() - // Call(target_fn/address) - // Return(opt_value) - - private: - FunctionInstrument* instrument_; - }; - - private: - Function* target_; -}; - -class MemoryInstrument : public Instrument { - public: - MemoryInstrument(Processor* processor, uint32_t address, - uint32_t end_address); - virtual ~MemoryInstrument() {} - - uint64_t address() const { return address_; } - uint64_t end_address() const { return end_address_; } - - virtual bool Attach(); - virtual bool Detach(); - - public: - enum AccessType { - ACCESS_READ = (1 << 1), - ACCESS_WRITE = (1 << 2), - }; - void Access(ThreadState* thread_state, uint32_t address, AccessType type); - - protected: - class Instance { - public: - Instance(MemoryInstrument* instrument) : instrument_(instrument) {} - virtual ~Instance() {} - - MemoryInstrument* instrument() const { return instrument_; } - - virtual void OnAccess(ThreadState* thread_state, uint32_t address, - AccessType type) = 0; - - private: - MemoryInstrument* instrument_; - }; - - private: - uint32_t address_; - uint32_t end_address_; -}; - -// ThreadInstrument -// (v Detach()) -// ThreadInstrumentInstance: -// instrument() -// v OnCreate(context, state_ptr) -// v OnStart(context, state_ptr) -// v OnResume(context, state_ptr) -// v OnSuspend(context, state_ptr) -// v OnExit(context, state_ptr) - -// ModuleInstrument -// (v Detach()) -// ModuleInstrumentInstance: -// instrument() -// v OnLoad(context) -// v OnUnload(context) -// // get proc address? - -} // namespace cpu -} // namespace xe - -#endif // XENIA_CPU_INSTRUMENT_H_ diff --git a/src/xenia/cpu/module.cc b/src/xenia/cpu/module.cc index 8bf30e68d..63ef2c4f5 100644 --- a/src/xenia/cpu/module.cc +++ b/src/xenia/cpu/module.cc @@ -26,12 +26,12 @@ Module::~Module() = default; bool Module::ContainsAddress(uint32_t address) { return true; } -SymbolInfo* Module::LookupSymbol(uint32_t address, bool wait) { +Symbol* Module::LookupSymbol(uint32_t address, bool wait) { lock_.lock(); const auto it = map_.find(address); - SymbolInfo* symbol_info = it != map_.end() ? it->second : nullptr; - if (symbol_info) { - if (symbol_info->status() == SymbolStatus::kDeclaring) { + Symbol* symbol = it != map_.end() ? it->second : nullptr; + if (symbol) { + if (symbol->status() == Symbol::Status::kDeclaring) { // Some other thread is declaring the symbol - wait. if (wait) { do { @@ -39,133 +39,130 @@ SymbolInfo* Module::LookupSymbol(uint32_t address, bool wait) { // TODO(benvanik): sleep for less time? xe::threading::Sleep(std::chrono::microseconds(100)); lock_.lock(); - } while (symbol_info->status() == SymbolStatus::kDeclaring); + } while (symbol->status() == Symbol::Status::kDeclaring); } else { // Immediate request, just return. - symbol_info = nullptr; + symbol = nullptr; } } } lock_.unlock(); - return symbol_info; + return symbol; } -SymbolStatus Module::DeclareSymbol(SymbolType type, uint32_t address, - SymbolInfo** out_symbol_info) { - *out_symbol_info = nullptr; +Symbol::Status Module::DeclareSymbol(Symbol::Type type, uint32_t address, + Symbol** out_symbol) { + *out_symbol = nullptr; lock_.lock(); auto it = map_.find(address); - SymbolInfo* symbol_info = it != map_.end() ? it->second : nullptr; - SymbolStatus status; - if (symbol_info) { + Symbol* symbol = it != map_.end() ? it->second : nullptr; + Symbol::Status status; + if (symbol) { // If we exist but are the wrong type, die. - if (symbol_info->type() != type) { + if (symbol->type() != type) { lock_.unlock(); - return SymbolStatus::kFailed; + return Symbol::Status::kFailed; } // If we aren't ready yet spin and wait. - if (symbol_info->status() == SymbolStatus::kDeclaring) { + if (symbol->status() == Symbol::Status::kDeclaring) { // Still declaring, so spin. do { lock_.unlock(); // TODO(benvanik): sleep for less time? xe::threading::Sleep(std::chrono::microseconds(100)); lock_.lock(); - } while (symbol_info->status() == SymbolStatus::kDeclaring); + } while (symbol->status() == Symbol::Status::kDeclaring); } - status = symbol_info->status(); + status = symbol->status(); } else { // Create and return for initialization. switch (type) { - case SymbolType::kFunction: - symbol_info = new FunctionInfo(this, address); + case Symbol::Type::kFunction: + symbol = CreateFunction(address).release(); break; - case SymbolType::kVariable: - symbol_info = new VariableInfo(this, address); + case Symbol::Type::kVariable: + symbol = new Symbol(Symbol::Type::kVariable, this, address); break; } - map_[address] = symbol_info; - list_.emplace_back(symbol_info); - status = SymbolStatus::kNew; + map_[address] = symbol; + list_.emplace_back(symbol); + status = Symbol::Status::kNew; } lock_.unlock(); - *out_symbol_info = symbol_info; + *out_symbol = symbol; // Get debug info from providers, if this is new. - if (status == SymbolStatus::kNew) { + if (status == Symbol::Status::kNew) { // TODO(benvanik): lookup in map data/dwarf/etc? } return status; } -SymbolStatus Module::DeclareFunction(uint32_t address, - FunctionInfo** out_symbol_info) { - SymbolInfo* symbol_info; - SymbolStatus status = - DeclareSymbol(SymbolType::kFunction, address, &symbol_info); - *out_symbol_info = (FunctionInfo*)symbol_info; +Symbol::Status Module::DeclareFunction(uint32_t address, + Function** out_function) { + Symbol* symbol; + Symbol::Status status = + DeclareSymbol(Symbol::Type::kFunction, address, &symbol); + *out_function = static_cast(symbol); return status; } -SymbolStatus Module::DeclareVariable(uint32_t address, - VariableInfo** out_symbol_info) { - SymbolInfo* symbol_info; - SymbolStatus status = - DeclareSymbol(SymbolType::kVariable, address, &symbol_info); - *out_symbol_info = (VariableInfo*)symbol_info; +Symbol::Status Module::DeclareVariable(uint32_t address, Symbol** out_symbol) { + Symbol::Status status = + DeclareSymbol(Symbol::Type::kVariable, address, out_symbol); return status; } -SymbolStatus Module::DefineSymbol(SymbolInfo* symbol_info) { +Symbol::Status Module::DefineSymbol(Symbol* symbol) { lock_.lock(); - SymbolStatus status; - if (symbol_info->status() == SymbolStatus::kDeclared) { + Symbol::Status status; + if (symbol->status() == Symbol::Status::kDeclared) { // Declared but undefined, so request caller define it. - symbol_info->set_status(SymbolStatus::kDefining); - status = SymbolStatus::kNew; - } else if (symbol_info->status() == SymbolStatus::kDefining) { + symbol->set_status(Symbol::Status::kDefining); + status = Symbol::Status::kNew; + } else if (symbol->status() == Symbol::Status::kDefining) { // Still defining, so spin. do { lock_.unlock(); // TODO(benvanik): sleep for less time? xe::threading::Sleep(std::chrono::microseconds(100)); lock_.lock(); - } while (symbol_info->status() == SymbolStatus::kDefining); - status = symbol_info->status(); + } while (symbol->status() == Symbol::Status::kDefining); + status = symbol->status(); } else { - status = symbol_info->status(); + status = symbol->status(); } lock_.unlock(); return status; } -SymbolStatus Module::DefineFunction(FunctionInfo* symbol_info) { - return DefineSymbol((SymbolInfo*)symbol_info); +Symbol::Status Module::DefineFunction(Function* symbol) { + return DefineSymbol((Symbol*)symbol); } -SymbolStatus Module::DefineVariable(VariableInfo* symbol_info) { - return DefineSymbol((SymbolInfo*)symbol_info); +Symbol::Status Module::DefineVariable(Symbol* symbol) { + return DefineSymbol((Symbol*)symbol); } -void Module::ForEachFunction(std::function callback) { +void Module::ForEachFunction(std::function callback) { std::lock_guard guard(lock_); - for (auto& symbol_info : list_) { - if (symbol_info->type() == SymbolType::kFunction) { - FunctionInfo* info = static_cast(symbol_info.get()); + for (auto& symbol : list_) { + if (symbol->type() == Symbol::Type::kFunction) { + Function* info = static_cast(symbol.get()); callback(info); } } } void Module::ForEachSymbol(size_t start_index, size_t end_index, - std::function callback) { + std::function callback) { std::lock_guard guard(lock_); start_index = std::min(start_index, list_.size()); end_index = std::min(end_index, list_.size()); for (size_t i = start_index; i <= end_index; ++i) { - auto& symbol_info = list_[i]; - callback(symbol_info.get()); + auto& symbol = list_[i]; + callback(symbol.get()); } } @@ -223,19 +220,19 @@ bool Module::ReadMap(const char* file_name) { if (type_str == "f") { // Function. - FunctionInfo* fn_info; - if (!processor_->LookupFunctionInfo(this, address, &fn_info)) { + auto function = processor_->LookupFunction(this, address); + if (!function) { continue; } // Don't overwrite names we've set elsewhere. - if (fn_info->name().empty()) { + if (function->name().empty()) { // If it's a mangled C++ name (?name@...) just use the name. // TODO(benvanik): better demangling, or leave it to clients. /*if (name[0] == '?') { size_t at = name.find('@'); name = name.substr(1, at - 1); }*/ - fn_info->set_name(name.c_str()); + function->set_name(name.c_str()); } } else { // Variable. diff --git a/src/xenia/cpu/module.h b/src/xenia/cpu/module.h index 517c59557..c933668d5 100644 --- a/src/xenia/cpu/module.h +++ b/src/xenia/cpu/module.h @@ -17,13 +17,13 @@ #include #include "xenia/base/mutex.h" -#include "xenia/cpu/symbol_info.h" +#include "xenia/cpu/function.h" +#include "xenia/cpu/symbol.h" #include "xenia/memory.h" namespace xe { namespace cpu { -class Function; class Processor; class Module { @@ -37,36 +37,36 @@ class Module { virtual bool ContainsAddress(uint32_t address); - SymbolInfo* LookupSymbol(uint32_t address, bool wait = true); - virtual SymbolStatus DeclareFunction(uint32_t address, - FunctionInfo** out_symbol_info); - virtual SymbolStatus DeclareVariable(uint32_t address, - VariableInfo** out_symbol_info); + Symbol* LookupSymbol(uint32_t address, bool wait = true); + virtual Symbol::Status DeclareFunction(uint32_t address, + Function** out_function); + virtual Symbol::Status DeclareVariable(uint32_t address, Symbol** out_symbol); - SymbolStatus DefineFunction(FunctionInfo* symbol_info); - SymbolStatus DefineVariable(VariableInfo* symbol_info); + Symbol::Status DefineFunction(Function* symbol); + Symbol::Status DefineVariable(Symbol* symbol); - void ForEachFunction(std::function callback); + void ForEachFunction(std::function callback); void ForEachSymbol(size_t start_index, size_t end_index, - std::function callback); + std::function callback); size_t QuerySymbolCount(); bool ReadMap(const char* file_name); - private: - SymbolStatus DeclareSymbol(SymbolType type, uint32_t address, - SymbolInfo** out_symbol_info); - SymbolStatus DefineSymbol(SymbolInfo* symbol_info); - protected: - Processor* processor_; - Memory* memory_; + virtual std::unique_ptr CreateFunction(uint32_t address) = 0; + + Processor* processor_ = nullptr; + Memory* memory_ = nullptr; private: + Symbol::Status DeclareSymbol(Symbol::Type type, uint32_t address, + Symbol** out_symbol); + Symbol::Status DefineSymbol(Symbol* symbol); + // TODO(benvanik): replace with a better data structure. xe::mutex lock_; - std::unordered_map map_; - std::vector> list_; + std::unordered_map map_; + std::vector> list_; }; } // namespace cpu diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 3435660e2..d8852cd39 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -42,6 +42,11 @@ class BuiltinModule : public Module { return (address & 0xFFFFFFF0) == 0xFFFFFFF0; } + protected: + std::unique_ptr CreateFunction(uint32_t address) override { + return std::unique_ptr(new BuiltinFunction(this, address)); + } + private: std::string name_; }; @@ -142,20 +147,22 @@ std::vector Processor::GetModules() { return clone; } -FunctionInfo* Processor::DefineBuiltin(const std::string& name, - FunctionInfo::BuiltinHandler handler, - void* arg0, void* arg1) { +Function* Processor::DefineBuiltin(const std::string& name, + BuiltinFunction::Handler handler, void* arg0, + void* arg1) { uint32_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->SetupBuiltin(handler, arg0, arg1); - fn_info->set_status(SymbolStatus::kDeclared); + Function* function; + builtin_module_->DeclareFunction(address, &function); + function->set_end_address(address + 4); + function->set_name(name); - return fn_info; + auto builtin_function = static_cast(function); + builtin_function->SetupBuiltin(handler, arg0, arg1); + + function->set_status(Symbol::Status::kDeclared); + return function; } Function* Processor::QueryFunction(uint32_t address) { @@ -170,40 +177,36 @@ std::vector Processor::FindFunctionsWithAddress(uint32_t address) { return entry_table_.FindWithAddress(address); } -bool Processor::ResolveFunction(uint32_t address, Function** out_function) { - *out_function = nullptr; +Function* Processor::ResolveFunction(uint32_t address) { Entry* entry; Entry::Status status = entry_table_.GetOrCreate(address, &entry); if (status == Entry::STATUS_NEW) { // Needs to be generated. We have the 'lock' on it and must do so now. // Grab symbol declaration. - FunctionInfo* symbol_info; - if (!LookupFunctionInfo(address, &symbol_info)) { - return false; + auto function = LookupFunction(address); + if (!function) { + return nullptr; } - if (!DemandFunction(symbol_info, &entry->function)) { + if (!DemandFunction(function)) { entry->status = Entry::STATUS_FAILED; - return false; + return nullptr; } - entry->end_address = symbol_info->end_address(); + entry->function = function; + entry->end_address = function->end_address(); status = entry->status = Entry::STATUS_READY; } if (status == Entry::STATUS_READY) { // Ready to use. - *out_function = entry->function; - return true; + return entry->function; } else { // Failed or bad state. - return false; + return nullptr; } } -bool Processor::LookupFunctionInfo(uint32_t address, - FunctionInfo** out_symbol_info) { - *out_symbol_info = nullptr; - +Function* Processor::LookupFunction(uint32_t address) { // TODO(benvanik): fast reject invalid addresses/log errors. // Find the module that contains the address. @@ -221,64 +224,57 @@ bool Processor::LookupFunctionInfo(uint32_t address, } if (!code_module) { // No module found that could contain the address. - return false; + return nullptr; } - return LookupFunctionInfo(code_module, address, out_symbol_info); + return LookupFunction(code_module, address); } -bool Processor::LookupFunctionInfo(Module* module, uint32_t address, - FunctionInfo** out_symbol_info) { +Function* Processor::LookupFunction(Module* module, uint32_t address) { // Atomic create/lookup symbol in module. // If we get back the NEW flag we must declare it now. - FunctionInfo* symbol_info = nullptr; - SymbolStatus symbol_status = module->DeclareFunction(address, &symbol_info); - if (symbol_status == SymbolStatus::kNew) { + Function* function = nullptr; + auto symbol_status = module->DeclareFunction(address, &function); + if (symbol_status == Symbol::Status::kNew) { // Symbol is undeclared, so declare now. - if (!frontend_->DeclareFunction(symbol_info)) { - symbol_info->set_status(SymbolStatus::kFailed); - return false; + assert_true(function->is_guest()); + if (!frontend_->DeclareFunction(static_cast(function))) { + function->set_status(Symbol::Status::kFailed); + return nullptr; } - symbol_info->set_status(SymbolStatus::kDeclared); + function->set_status(Symbol::Status::kDeclared); } - - *out_symbol_info = symbol_info; - return true; + return function; } -bool Processor::DemandFunction(FunctionInfo* symbol_info, - Function** out_function) { - *out_function = nullptr; - +bool Processor::DemandFunction(Function* function) { // Lock function for generation. If it's already being generated // by another thread this will block and return DECLARED. - Module* module = symbol_info->module(); - SymbolStatus symbol_status = module->DefineFunction(symbol_info); - if (symbol_status == SymbolStatus::kNew) { + auto module = function->module(); + auto symbol_status = module->DefineFunction(function); + if (symbol_status == Symbol::Status::kNew) { // Symbol is undefined, so define now. - Function* function = nullptr; - if (!frontend_->DefineFunction(symbol_info, debug_info_flags_, &function)) { - symbol_info->set_status(SymbolStatus::kFailed); + assert_true(function->is_guest()); + if (!frontend_->DefineFunction(static_cast(function), + debug_info_flags_)) { + function->set_status(Symbol::Status::kFailed); return false; } - symbol_info->set_function(function); // Before we give the symbol back to the rest, let the debugger know. if (debugger_) { - debugger_->OnFunctionDefined(symbol_info, function); + debugger_->OnFunctionDefined(function); } - symbol_info->set_status(SymbolStatus::kDefined); - symbol_status = symbol_info->status(); + function->set_status(Symbol::Status::kDefined); + symbol_status = function->status(); } - if (symbol_status == SymbolStatus::kFailed) { + if (symbol_status == Symbol::Status::kFailed) { // Symbol likely failed. return false; } - *out_function = symbol_info->function(); - return true; } @@ -286,8 +282,8 @@ bool Processor::Execute(ThreadState* thread_state, uint32_t address) { SCOPE_profile_cpu_f("cpu"); // Attempt to get the function. - Function* fn; - if (!ResolveFunction(address, &fn)) { + auto function = ResolveFunction(address); + if (!function) { // Symbol not found in any module. XELOGCPU("Execute(%.8X): failed to find function", address); return false; @@ -305,7 +301,7 @@ bool Processor::Execute(ThreadState* thread_state, uint32_t address) { context->lr = 0xBCBCBCBC; // Execute the function. - auto result = fn->Call(thread_state, uint32_t(context->lr)); + auto result = function->Call(thread_state, uint32_t(context->lr)); context->lr = previous_lr; context->r[1] += 64 + 112; diff --git a/src/xenia/cpu/processor.h b/src/xenia/cpu/processor.h index a5ef6da5c..356450d6d 100644 --- a/src/xenia/cpu/processor.h +++ b/src/xenia/cpu/processor.h @@ -69,17 +69,16 @@ class Processor { std::vector GetModules(); Module* builtin_module() const { return builtin_module_; } - FunctionInfo* DefineBuiltin(const std::string& name, - FunctionInfo::BuiltinHandler handler, void* arg0, - void* arg1); + Function* DefineBuiltin(const std::string& name, + BuiltinFunction::Handler handler, void* arg0, + void* arg1); Function* QueryFunction(uint32_t address); std::vector FindFunctionsWithAddress(uint32_t address); - bool LookupFunctionInfo(uint32_t address, FunctionInfo** out_symbol_info); - bool LookupFunctionInfo(Module* module, uint32_t address, - FunctionInfo** out_symbol_info); - bool ResolveFunction(uint32_t address, Function** out_function); + Function* LookupFunction(uint32_t address); + Function* LookupFunction(Module* module, uint32_t address); + Function* ResolveFunction(uint32_t address); bool Execute(ThreadState* thread_state, uint32_t address); uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t args[], @@ -89,7 +88,7 @@ class Processor { void LowerIrql(Irql old_value); private: - bool DemandFunction(FunctionInfo* symbol_info, Function** out_function); + bool DemandFunction(Function* function); Memory* memory_ = nullptr; debug::Debugger* debugger_ = nullptr; diff --git a/src/xenia/cpu/raw_module.cc b/src/xenia/cpu/raw_module.cc index e36ce2fb7..72b7d03a0 100644 --- a/src/xenia/cpu/raw_module.cc +++ b/src/xenia/cpu/raw_module.cc @@ -12,6 +12,8 @@ #include "xenia/base/filesystem.h" #include "xenia/base/platform.h" #include "xenia/base/string.h" +#include "xenia/cpu/function.h" +#include "xenia/cpu/processor.h" namespace xe { namespace cpu { @@ -60,5 +62,10 @@ bool RawModule::ContainsAddress(uint32_t address) { return address >= low_address_ && address < high_address_; } +std::unique_ptr RawModule::CreateFunction(uint32_t address) { + return std::unique_ptr( + processor_->backend()->CreateGuestFunction(this, address)); +} + } // namespace cpu } // namespace xe diff --git a/src/xenia/cpu/raw_module.h b/src/xenia/cpu/raw_module.h index d141661b8..543ae6c93 100644 --- a/src/xenia/cpu/raw_module.h +++ b/src/xenia/cpu/raw_module.h @@ -28,6 +28,9 @@ class RawModule : public Module { bool ContainsAddress(uint32_t address) override; + protected: + std::unique_ptr CreateFunction(uint32_t address) override; + private: std::string name_; uint32_t base_address_; diff --git a/src/xenia/cpu/stack_walker.h b/src/xenia/cpu/stack_walker.h index e8b00326d..061dd4e5d 100644 --- a/src/xenia/cpu/stack_walker.h +++ b/src/xenia/cpu/stack_walker.h @@ -13,7 +13,7 @@ #include #include -#include "xenia/cpu/symbol_info.h" +#include "xenia/cpu/function.h" namespace xe { namespace cpu { @@ -49,7 +49,7 @@ struct StackFrame { } host_symbol; // Contains symbol information for kGuest frames. struct { - FunctionInfo* function_info; + Function* function; } guest_symbol; }; }; diff --git a/src/xenia/cpu/stack_walker_win.cc b/src/xenia/cpu/stack_walker_win.cc index a9550c910..536079265 100644 --- a/src/xenia/cpu/stack_walker_win.cc +++ b/src/xenia/cpu/stack_walker_win.cc @@ -205,19 +205,21 @@ class Win32StackWalker : public StackWalker { if (frame.host_pc >= code_cache_min_ && frame.host_pc < code_cache_max_) { // Guest symbol, so we can look it up quickly in the code cache. frame.type = StackFrame::Type::kGuest; - auto function_info = code_cache_->LookupFunction(frame.host_pc); - if (function_info) { - frame.guest_symbol.function_info = function_info; + auto function = code_cache_->LookupFunction(frame.host_pc); + if (function) { + frame.guest_symbol.function = function; // Figure out where in guest code we are by looking up the // displacement in x64 from the JIT'ed code start to the PC. - uint32_t host_displacement = - uint32_t(frame.host_pc) - - uint32_t(uint64_t(function_info->function()->machine_code())); - auto entry = - function_info->function()->LookupCodeOffset(host_displacement); - frame.guest_pc = entry->source_offset; + if (function->is_guest()) { + auto guest_function = static_cast(function); + uint32_t host_displacement = + uint32_t(frame.host_pc) - + uint32_t(uint64_t(guest_function->machine_code())); + auto entry = guest_function->LookupCodeOffset(host_displacement); + frame.guest_pc = entry->source_offset; + } } else { - frame.guest_symbol.function_info = nullptr; + frame.guest_symbol.function = nullptr; } } else { // Host symbol, which means either emulator or system. diff --git a/src/xenia/cpu/symbol.h b/src/xenia/cpu/symbol.h new file mode 100644 index 000000000..f0f367e9e --- /dev/null +++ b/src/xenia/cpu/symbol.h @@ -0,0 +1,62 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_CPU_SYMBOL_INFO_H_ +#define XENIA_CPU_SYMBOL_INFO_H_ + +#include +#include + +namespace xe { +namespace cpu { + +class Module; + +class Symbol { + public: + enum class Type { + kFunction, + kVariable, + }; + + enum class Status { + kNew, + kDeclaring, + kDeclared, + kDefining, + kDefined, + kFailed, + }; + + Symbol(Type type, Module* module, uint32_t address) + : type_(type), module_(module), address_(address) {} + virtual ~Symbol() = default; + + Type type() const { return type_; } + Module* module() const { return module_; } + Status status() const { return status_; } + void set_status(Status value) { status_ = value; } + uint32_t address() const { return address_; } + + const std::string& name() const { return name_; } + void set_name(const std::string& value) { name_ = value; } + + protected: + Type type_ = Type::kVariable; + Module* module_ = nullptr; + Status status_ = Status::kDefining; + uint32_t address_ = 0; + + std::string name_; +}; + +} // namespace cpu +} // namespace xe + +#endif // XENIA_CPU_SYMBOL_INFO_H_ diff --git a/src/xenia/cpu/symbol_info.cc b/src/xenia/cpu/symbol_info.cc deleted file mode 100644 index b004b4c57..000000000 --- a/src/xenia/cpu/symbol_info.cc +++ /dev/null @@ -1,55 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/cpu/symbol_info.h" - -#include - -namespace xe { -namespace cpu { - -SymbolInfo::SymbolInfo(SymbolType type, Module* module, uint32_t address) - : type_(type), - module_(module), - status_(SymbolStatus::kDefining), - address_(address), - name_("") {} - -SymbolInfo::~SymbolInfo() = default; - -FunctionInfo::FunctionInfo(Module* module, uint32_t address) - : SymbolInfo(SymbolType::kFunction, module, address), - end_address_(0), - behavior_(FunctionBehavior::kDefault), - function_(nullptr) { - std::memset(&extern_info_, 0, sizeof(extern_info_)); -} - -FunctionInfo::~FunctionInfo() = default; - -void FunctionInfo::SetupBuiltin(BuiltinHandler handler, void* arg0, - void* arg1) { - behavior_ = FunctionBehavior::kBuiltin; - builtin_info_.handler = handler; - builtin_info_.arg0 = arg0; - builtin_info_.arg1 = arg1; -} - -void FunctionInfo::SetupExtern(ExternHandler handler) { - behavior_ = FunctionBehavior::kExtern; - extern_info_.handler = handler; -} - -VariableInfo::VariableInfo(Module* module, uint32_t address) - : SymbolInfo(SymbolType::kVariable, module, address) {} - -VariableInfo::~VariableInfo() = default; - -} // namespace cpu -} // namespace xe diff --git a/src/xenia/cpu/symbol_info.h b/src/xenia/cpu/symbol_info.h deleted file mode 100644 index 67e66eb0f..000000000 --- a/src/xenia/cpu/symbol_info.h +++ /dev/null @@ -1,122 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_CPU_SYMBOL_INFO_H_ -#define XENIA_CPU_SYMBOL_INFO_H_ - -#include -#include - -#include "xenia/cpu/frontend/ppc_context.h" - -namespace xe { -namespace cpu { - -class Function; -class Module; - -enum class SymbolType { - kFunction, - kVariable, -}; - -enum class SymbolStatus { - kNew, - kDeclaring, - kDeclared, - kDefining, - kDefined, - kFailed, -}; - -class SymbolInfo { - public: - SymbolInfo(SymbolType type, Module* module, uint32_t address); - virtual ~SymbolInfo(); - - SymbolType type() const { return type_; } - Module* module() const { return module_; } - SymbolStatus status() const { return status_; } - void set_status(SymbolStatus value) { status_ = value; } - uint32_t address() const { return address_; } - - const std::string& name() const { return name_; } - void set_name(const std::string& value) { name_ = value; } - - protected: - SymbolType type_; - Module* module_; - SymbolStatus status_; - uint32_t address_; - - std::string name_; -}; - -enum class FunctionBehavior { - kDefault = 0, - kProlog, - kEpilog, - kEpilogReturn, - kBuiltin, - kExtern, -}; - -class FunctionInfo : public SymbolInfo { - public: - FunctionInfo(Module* module, uint32_t address); - ~FunctionInfo() override; - - bool has_end_address() const { return end_address_ > 0; } - uint32_t end_address() const { return end_address_; } - void set_end_address(uint32_t value) { end_address_ = value; } - - FunctionBehavior behavior() const { return behavior_; } - void set_behavior(FunctionBehavior value) { behavior_ = value; } - - Function* function() const { return function_; } - void set_function(Function* value) { function_ = value; } - - typedef void (*BuiltinHandler)(frontend::PPCContext* ppc_context, void* arg0, - void* arg1); - void SetupBuiltin(BuiltinHandler handler, void* arg0, void* arg1); - BuiltinHandler builtin_handler() const { return builtin_info_.handler; } - void* builtin_arg0() const { return builtin_info_.arg0; } - void* builtin_arg1() const { return builtin_info_.arg1; } - - typedef void (*ExternHandler)(frontend::PPCContext* ppc_context, - kernel::KernelState* kernel_state); - void SetupExtern(ExternHandler handler); - ExternHandler extern_handler() const { return extern_info_.handler; } - - private: - uint32_t end_address_; - FunctionBehavior behavior_; - Function* function_; - union { - struct { - ExternHandler handler; - } extern_info_; - struct { - BuiltinHandler handler; - void* arg0; - void* arg1; - } builtin_info_; - }; -}; - -class VariableInfo : public SymbolInfo { - public: - VariableInfo(Module* module, uint32_t address); - ~VariableInfo() override; -}; - -} // namespace cpu -} // namespace xe - -#endif // XENIA_CPU_SYMBOL_INFO_H_ diff --git a/src/xenia/cpu/test_module.cc b/src/xenia/cpu/test_module.cc index c93aba42b..7c117ee79 100644 --- a/src/xenia/cpu/test_module.cc +++ b/src/xenia/cpu/test_module.cc @@ -71,29 +71,34 @@ bool TestModule::ContainsAddress(uint32_t address) { return contains_address_(address); } -SymbolStatus TestModule::DeclareFunction(uint32_t address, - FunctionInfo** out_symbol_info) { - SymbolStatus status = Module::DeclareFunction(address, out_symbol_info); - if (status == SymbolStatus::kNew) { - auto symbol_info = *out_symbol_info; +std::unique_ptr TestModule::CreateFunction(uint32_t address) { + return std::unique_ptr( + processor_->backend()->CreateGuestFunction(this, address)); +} + +Symbol::Status TestModule::DeclareFunction(uint32_t address, + Function** out_function) { + Symbol::Status status = Module::DeclareFunction(address, out_function); + if (status == Symbol::Status::kNew) { + auto function = static_cast(*out_function); // Reset() all caching when we leave. xe::make_reset_scope(compiler_); xe::make_reset_scope(assembler_); if (!generate_(*builder_.get())) { - symbol_info->set_status(SymbolStatus::kFailed); - return SymbolStatus::kFailed; + function->set_status(Symbol::Status::kFailed); + return Symbol::Status::kFailed; } + // Run optimization passes. compiler_->Compile(builder_.get()); - Function* fn = nullptr; - assembler_->Assemble(symbol_info, builder_.get(), 0, nullptr, &fn); + // Assemble the function. + assembler_->Assemble(function, builder_.get(), 0, nullptr); - symbol_info->set_function(fn); - status = SymbolStatus::kDefined; - symbol_info->set_status(status); + status = Symbol::Status::kDefined; + function->set_status(status); } return status; } diff --git a/src/xenia/cpu/test_module.h b/src/xenia/cpu/test_module.h index c228a6884..dfce7b731 100644 --- a/src/xenia/cpu/test_module.h +++ b/src/xenia/cpu/test_module.h @@ -33,8 +33,11 @@ class TestModule : public Module { bool ContainsAddress(uint32_t address) override; - SymbolStatus DeclareFunction(uint32_t address, - FunctionInfo** out_symbol_info) override; + Symbol::Status DeclareFunction(uint32_t address, + Function** out_symbol) override; + + protected: + std::unique_ptr CreateFunction(uint32_t address) override; private: std::string name_; diff --git a/src/xenia/cpu/testing/util.h b/src/xenia/cpu/testing/util.h index 44b3eb11f..9db88dd10 100644 --- a/src/xenia/cpu/testing/util.h +++ b/src/xenia/cpu/testing/util.h @@ -65,8 +65,7 @@ class TestFunction { void Run(std::function pre_call, std::function post_call) { for (auto& processor : processors) { - xe::cpu::Function* fn; - processor->ResolveFunction(0x80000000, &fn); + auto fn = processor->ResolveFunction(0x80000000); uint32_t stack_size = 64 * 1024; uint32_t stack_address = memory_size - stack_size; diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index f16d54d29..6deb8e654 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -375,12 +375,12 @@ bool XexModule::SetupLibraryImports(const char* name, } // Setup a variable and define it. - VariableInfo* var_info; + Symbol* var_info; DeclareVariable(record_addr, &var_info); var_info->set_name(import_name.GetString()); - var_info->set_status(SymbolStatus::kDeclared); + var_info->set_status(Symbol::Status::kDeclared); DefineVariable(var_info); - var_info->set_status(SymbolStatus::kDefined); + var_info->set_status(Symbol::Status::kDefined); } else if (record_type == 1) { // Thunk. if (kernel_export) { @@ -389,10 +389,10 @@ bool XexModule::SetupLibraryImports(const char* name, import_name.AppendFormat("__%s_%.3X", libbasename.c_str(), ordinal); } - FunctionInfo* fn_info; - DeclareFunction(record_addr, &fn_info); - fn_info->set_end_address(record_addr + 16 - 4); - fn_info->set_name(import_name.GetString()); + Function* function; + DeclareFunction(record_addr, &function); + function->set_end_address(record_addr + 16 - 4); + function->set_name(import_name.GetString()); if (kernel_export) { // On load we have something like this in memory: @@ -415,14 +415,14 @@ bool XexModule::SetupLibraryImports(const char* name, xe::store_and_swap(p + 0x8, 0x60000000); xe::store_and_swap(p + 0xC, 0x60000000); - FunctionInfo::ExternHandler handler = 0; + GuestFunction::ExternHandler handler = nullptr; if (kernel_export) { if (kernel_export->function_data.trampoline) { - handler = (FunctionInfo::ExternHandler) + handler = (GuestFunction::ExternHandler) kernel_export->function_data.trampoline; } else { handler = - (FunctionInfo::ExternHandler)kernel_export->function_data.shim; + (GuestFunction::ExternHandler)kernel_export->function_data.shim; } } else { XELOGW("WARNING: Imported kernel function %s is unimplemented!", @@ -430,7 +430,7 @@ bool XexModule::SetupLibraryImports(const char* name, handler = UndefinedImport; } - fn_info->SetupExtern(handler); + static_cast(function)->SetupExtern(handler); } else if (user_export_addr) { // Rewrite PPC code to set r11 to the target address // So we'll have: @@ -458,7 +458,7 @@ bool XexModule::SetupLibraryImports(const char* name, xe::store_and_swap(p + 0xC, 0x60000000); } - fn_info->set_status(SymbolStatus::kDeclared); + function->set_status(Symbol::Status::kDeclared); } else { // Bad. assert_always(); @@ -472,6 +472,11 @@ bool XexModule::ContainsAddress(uint32_t address) { return address >= low_address_ && address < high_address_; } +std::unique_ptr XexModule::CreateFunction(uint32_t address) { + return std::unique_ptr( + processor_->backend()->CreateGuestFunction(this, address)); +} + bool XexModule::FindSaveRest() { // Special stack save/restore functions. // http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/md/ppc/xxx.s.htm @@ -677,27 +682,27 @@ bool XexModule::FindSaveRest() { uint32_t address = gplr_start; for (int n = 14; n <= 31; n++) { snprintf(name, xe::countof(name), "__savegprlr_%d", n); - FunctionInfo* symbol_info; - DeclareFunction(address, &symbol_info); - symbol_info->set_end_address(address + (31 - n) * 4 + 2 * 4); - symbol_info->set_name(name); + Function* function; + DeclareFunction(address, &function); + function->set_end_address(address + (31 - n) * 4 + 2 * 4); + function->set_name(name); // TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveGprLr; - symbol_info->set_behavior(FunctionBehavior::kProlog); - symbol_info->set_status(SymbolStatus::kDeclared); + function->set_behavior(Function::Behavior::kProlog); + function->set_status(Symbol::Status::kDeclared); address += 4; } address = gplr_start + 20 * 4; for (int n = 14; n <= 31; n++) { snprintf(name, xe::countof(name), "__restgprlr_%d", n); - FunctionInfo* symbol_info; - DeclareFunction(address, &symbol_info); - symbol_info->set_end_address(address + (31 - n) * 4 + 3 * 4); - symbol_info->set_name(name); + Function* function; + DeclareFunction(address, &function); + function->set_end_address(address + (31 - n) * 4 + 3 * 4); + function->set_name(name); // TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestGprLr; - symbol_info->set_behavior(FunctionBehavior::kEpilogReturn); - symbol_info->set_status(SymbolStatus::kDeclared); + function->set_behavior(Function::Behavior::kEpilogReturn); + function->set_status(Symbol::Status::kDeclared); address += 4; } } @@ -705,27 +710,27 @@ bool XexModule::FindSaveRest() { uint32_t address = fpr_start; for (int n = 14; n <= 31; n++) { snprintf(name, xe::countof(name), "__savefpr_%d", n); - FunctionInfo* symbol_info; - DeclareFunction(address, &symbol_info); - symbol_info->set_end_address(address + (31 - n) * 4 + 1 * 4); - symbol_info->set_name(name); + Function* function; + DeclareFunction(address, &function); + function->set_end_address(address + (31 - n) * 4 + 1 * 4); + function->set_name(name); // TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveFpr; - symbol_info->set_behavior(FunctionBehavior::kProlog); - symbol_info->set_status(SymbolStatus::kDeclared); + function->set_behavior(Function::Behavior::kProlog); + function->set_status(Symbol::Status::kDeclared); address += 4; } address = fpr_start + (18 * 4) + (1 * 4); for (int n = 14; n <= 31; n++) { snprintf(name, xe::countof(name), "__restfpr_%d", n); - FunctionInfo* symbol_info; - DeclareFunction(address, &symbol_info); - symbol_info->set_end_address(address + (31 - n) * 4 + 1 * 4); - symbol_info->set_name(name); + Function* function; + DeclareFunction(address, &function); + function->set_end_address(address + (31 - n) * 4 + 1 * 4); + function->set_name(name); // TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestFpr; - symbol_info->set_behavior(FunctionBehavior::kEpilog); - symbol_info->set_status(SymbolStatus::kDeclared); + function->set_behavior(Function::Behavior::kEpilog); + function->set_status(Symbol::Status::kDeclared); address += 4; } } @@ -738,49 +743,49 @@ bool XexModule::FindSaveRest() { uint32_t address = vmx_start; for (int n = 14; n <= 31; n++) { snprintf(name, xe::countof(name), "__savevmx_%d", n); - FunctionInfo* symbol_info; - DeclareFunction(address, &symbol_info); - symbol_info->set_name(name); + Function* function; + DeclareFunction(address, &function); + function->set_name(name); // TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx; - symbol_info->set_behavior(FunctionBehavior::kProlog); - symbol_info->set_status(SymbolStatus::kDeclared); + function->set_behavior(Function::Behavior::kProlog); + function->set_status(Symbol::Status::kDeclared); address += 2 * 4; } address += 4; for (int n = 64; n <= 127; n++) { snprintf(name, xe::countof(name), "__savevmx_%d", n); - FunctionInfo* symbol_info; - DeclareFunction(address, &symbol_info); - symbol_info->set_name(name); + Function* function; + DeclareFunction(address, &function); + function->set_name(name); // TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx; - symbol_info->set_behavior(FunctionBehavior::kProlog); - symbol_info->set_status(SymbolStatus::kDeclared); + function->set_behavior(Function::Behavior::kProlog); + function->set_status(Symbol::Status::kDeclared); address += 2 * 4; } address = vmx_start + (18 * 2 * 4) + (1 * 4) + (64 * 2 * 4) + (1 * 4); for (int n = 14; n <= 31; n++) { snprintf(name, xe::countof(name), "__restvmx_%d", n); - FunctionInfo* symbol_info; - DeclareFunction(address, &symbol_info); - symbol_info->set_name(name); + Function* function; + DeclareFunction(address, &function); + function->set_name(name); // TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx; - symbol_info->set_behavior(FunctionBehavior::kEpilog); - symbol_info->set_status(SymbolStatus::kDeclared); + function->set_behavior(Function::Behavior::kEpilog); + function->set_status(Symbol::Status::kDeclared); address += 2 * 4; } address += 4; for (int n = 64; n <= 127; n++) { snprintf(name, xe::countof(name), "__restvmx_%d", n); - FunctionInfo* symbol_info; - DeclareFunction(address, &symbol_info); - symbol_info->set_name(name); + Function* function; + DeclareFunction(address, &function); + function->set_name(name); // TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx; - symbol_info->set_behavior(FunctionBehavior::kEpilog); - symbol_info->set_status(SymbolStatus::kDeclared); + function->set_behavior(Function::Behavior::kEpilog); + function->set_status(Symbol::Status::kDeclared); address += 2 * 4; } } diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index 962dcaf64..dcd73c9d3 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -77,12 +77,14 @@ class XexModule : public xe::cpu::Module { bool ContainsAddress(uint32_t address) override; + protected: + std::unique_ptr CreateFunction(uint32_t address) override; + private: bool SetupLibraryImports(const char* name, const xex2_import_library* library); bool FindSaveRest(); - private: Processor* processor_ = nullptr; kernel::KernelState* kernel_state_ = nullptr; std::string name_; diff --git a/src/xenia/debug/debug_server.cc b/src/xenia/debug/debug_server.cc index 8f001435b..1ae2f35ff 100644 --- a/src/xenia/debug/debug_server.cc +++ b/src/xenia/debug/debug_server.cc @@ -308,10 +308,10 @@ bool DebugServer::ProcessPacket(const proto::Packet* packet) { frame_body->host_function_address = frame.host_symbol.address; frame_body->guest_pc = frame.guest_pc; frame_body->guest_function_address = 0; - auto function_info = frame.guest_symbol.function_info; - if (frame.type == cpu::StackFrame::Type::kGuest && function_info) { - frame_body->guest_function_address = function_info->address(); - std::strncpy(frame_body->name, function_info->name().c_str(), + auto function = frame.guest_symbol.function; + if (frame.type == cpu::StackFrame::Type::kGuest && function) { + frame_body->guest_function_address = function->address(); + std::strncpy(frame_body->name, function->name().c_str(), xe::countof(frame_body->name)); } else { std::strncpy(frame_body->name, frame.host_symbol.name, diff --git a/src/xenia/debug/debugger.cc b/src/xenia/debug/debugger.cc index f9c7a4f02..636b713a9 100644 --- a/src/xenia/debug/debugger.cc +++ b/src/xenia/debug/debugger.cc @@ -169,10 +169,9 @@ void Debugger::DumpThreadStacks() { XELOGI(" %.2lld %.16llX %s", count - i - 1, frame.host_pc, frame.host_symbol.name); } else { - auto function_info = frame.guest_symbol.function_info; + auto function = frame.guest_symbol.function; XELOGI(" %.2lld %.16llX %.8X %s", count - i - 1, frame.host_pc, - frame.guest_pc, - function_info ? function_info->name().c_str() : "?"); + frame.guest_pc, function ? function->name().c_str() : "?"); } } } @@ -190,12 +189,13 @@ int Debugger::AddBreakpoint(Breakpoint* breakpoint) { auto fns = emulator_->processor()->FindFunctionsWithAddress(breakpoint->address()); + // TODO(benvanik): breakpoints. // Add. - for (auto fn : fns) { - if (fn->AddBreakpoint(breakpoint)) { - return 1; - } - } + // for (auto fn : fns) { + // if (fn->AddBreakpoint(breakpoint)) { + // return 1; + // } + //} return 0; } @@ -226,9 +226,10 @@ int Debugger::RemoveBreakpoint(Breakpoint* breakpoint) { emulator_->processor()->FindFunctionsWithAddress(breakpoint->address()); // Remove. - for (auto fn : fns) { + // TODO(benvanik): breakpoints. + /*for (auto fn : fns) { fn->RemoveBreakpoint(breakpoint); - } + }*/ return 0; } @@ -322,31 +323,30 @@ void Debugger::OnThreadDestroyed(xe::kernel::XThread* thread) { // TODO(benvanik): notify transports. } -void Debugger::OnFunctionDefined(cpu::FunctionInfo* symbol_info, - cpu::Function* function) { +void Debugger::OnFunctionDefined(cpu::Function* function) { + // TODO(benvanik): breakpoints? // Man, I'd love not to take this lock. - std::vector breakpoints; - { - std::lock_guard lock(mutex_); - for (uint32_t address = symbol_info->address(); - address <= symbol_info->end_address(); address += 4) { - auto range = breakpoints_.equal_range(address); - if (range.first == range.second) { - continue; - } - for (auto it = range.first; it != range.second; ++it) { - Breakpoint* breakpoint = it->second; - breakpoints.push_back(breakpoint); - } - } - } - - if (breakpoints.size()) { - // Breakpoints to add! - for (auto breakpoint : breakpoints) { - function->AddBreakpoint(breakpoint); - } - } + // std::vector breakpoints; + //{ + // std::lock_guard lock(mutex_); + // for (uint32_t address = function->address(); + // address <= function->end_address(); address += 4) { + // auto range = breakpoints_.equal_range(address); + // if (range.first == range.second) { + // continue; + // } + // for (auto it = range.first; it != range.second; ++it) { + // Breakpoint* breakpoint = it->second; + // breakpoints.push_back(breakpoint); + // } + // } + //} + // if (breakpoints.size()) { + // // Breakpoints to add! + // for (auto breakpoint : breakpoints) { + // function->AddBreakpoint(breakpoint); + // } + //} } void Debugger::OnBreakpointHit(xe::kernel::XThread* thread, diff --git a/src/xenia/debug/debugger.h b/src/xenia/debug/debugger.h index 68ac64bad..b52d51774 100644 --- a/src/xenia/debug/debugger.h +++ b/src/xenia/debug/debugger.h @@ -83,8 +83,7 @@ class Debugger { void OnThreadExit(xe::kernel::XThread* thread); void OnThreadDestroyed(xe::kernel::XThread* thread); - void OnFunctionDefined(cpu::FunctionInfo* symbol_info, - cpu::Function* function); + void OnFunctionDefined(cpu::Function* function); void OnBreakpointHit(xe::kernel::XThread* thread, Breakpoint* breakpoint);