Refactoring SymbolInfo/FunctionInfo/Function into Symbol/Function.

This commit is contained in:
Ben Vanik 2015-08-05 21:50:02 -07:00
parent 48d6e6becf
commit eaa1a8ee3a
54 changed files with 715 additions and 1027 deletions

View File

@ -15,8 +15,7 @@
namespace xe { namespace xe {
namespace cpu { namespace cpu {
class DebugInfo; class DebugInfo;
class Function; class GuestFunction;
class FunctionInfo;
namespace hir { namespace hir {
class HIRBuilder; class HIRBuilder;
} // namespace hir } // namespace hir
@ -38,10 +37,9 @@ class Assembler {
virtual void Reset(); 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, uint32_t debug_info_flags,
std::unique_ptr<DebugInfo> debug_info, std::unique_ptr<DebugInfo> debug_info) = 0;
Function** out_function) = 0;
protected: protected:
Backend* backend_; Backend* backend_;

View File

@ -16,6 +16,8 @@
namespace xe { namespace xe {
namespace cpu { namespace cpu {
class GuestFunction;
class Module;
class Processor; class Processor;
} // namespace cpu } // namespace cpu
} // namespace xe } // namespace xe
@ -46,6 +48,9 @@ class Backend {
virtual std::unique_ptr<Assembler> CreateAssembler() = 0; virtual std::unique_ptr<Assembler> CreateAssembler() = 0;
virtual std::unique_ptr<GuestFunction> CreateGuestFunction(
Module* module, uint32_t address) = 0;
protected: protected:
Processor* processor_; Processor* processor_;
MachineInfo machine_info_; MachineInfo machine_info_;

View File

@ -12,7 +12,7 @@
#include <string> #include <string>
#include "xenia/cpu/symbol_info.h" #include "xenia/cpu/function.h"
namespace xe { namespace xe {
namespace cpu { namespace cpu {
@ -29,7 +29,7 @@ class CodeCache {
// Finds a function based on the given host PC (that may be within a // Finds a function based on the given host PC (that may be within a
// function). // 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. // Finds platform-specific function unwind info for the given host PC.
virtual void* LookupUnwindInfo(uint64_t host_pc) = 0; virtual void* LookupUnwindInfo(uint64_t host_pc) = 0;

View File

@ -66,30 +66,26 @@ void X64Assembler::Reset() {
Assembler::Reset(); Assembler::Reset();
} }
bool X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder, bool X64Assembler::Assemble(GuestFunction* function, HIRBuilder* builder,
uint32_t debug_info_flags, uint32_t debug_info_flags,
std::unique_ptr<DebugInfo> debug_info, std::unique_ptr<DebugInfo> debug_info) {
Function** out_function) {
SCOPE_profile_cpu_f("cpu"); SCOPE_profile_cpu_f("cpu");
// Reset when we leave. // Reset when we leave.
xe::make_reset_scope(this); 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<X64Function>(symbol_info);
// Lower HIR -> x64. // Lower HIR -> x64.
void* machine_code = nullptr; void* machine_code = nullptr;
size_t code_size = 0; size_t code_size = 0;
if (!emitter_->Emit(symbol_info, builder, debug_info_flags, debug_info.get(), if (!emitter_->Emit(function, builder, debug_info_flags, debug_info.get(),
machine_code, code_size, fn->source_map())) { machine_code, code_size, function->source_map())) {
return false; return false;
} }
// Stash generated machine code. // Stash generated machine code.
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmMachineCode) { 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()); debug_info->set_machine_code_disasm(string_buffer_.ToString());
string_buffer_.Reset(); string_buffer_.Reset();
} }
@ -102,11 +98,10 @@ bool X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
} }
} }
fn->set_debug_info(std::move(debug_info)); function->set_debug_info(std::move(debug_info));
fn->Setup(reinterpret_cast<uint8_t*>(machine_code), code_size); static_cast<X64Function*>(function)
->Setup(reinterpret_cast<uint8_t*>(machine_code), code_size);
// Pass back ownership.
*out_function = fn.release();
return true; return true;
} }

View File

@ -35,10 +35,9 @@ class X64Assembler : public Assembler {
void Reset() override; void Reset() override;
bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder, bool Assemble(GuestFunction* function, hir::HIRBuilder* builder,
uint32_t debug_info_flags, uint32_t debug_info_flags,
std::unique_ptr<DebugInfo> debug_info, std::unique_ptr<DebugInfo> debug_info) override;
Function** out_function) override;
private: private:
void DumpMachineCode(void* machine_code, size_t code_size, void DumpMachineCode(void* machine_code, size_t code_size,

View File

@ -10,8 +10,9 @@
#include "xenia/cpu/backend/x64/x64_backend.h" #include "xenia/cpu/backend/x64/x64_backend.h"
#include "xenia/cpu/backend/x64/x64_assembler.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_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_sequences.h"
#include "xenia/cpu/backend/x64/x64_stack_layout.h" #include "xenia/cpu/backend/x64/x64_stack_layout.h"
#include "xenia/cpu/processor.h" #include "xenia/cpu/processor.h"
@ -106,6 +107,11 @@ std::unique_ptr<Assembler> X64Backend::CreateAssembler() {
return std::make_unique<X64Assembler>(this); return std::make_unique<X64Assembler>(this);
} }
std::unique_ptr<GuestFunction> X64Backend::CreateGuestFunction(
Module* module, uint32_t address) {
return std::make_unique<X64Function>(module, address);
}
using namespace Xbyak; using namespace Xbyak;
X64ThunkEmitter::X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator) X64ThunkEmitter::X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator)

View File

@ -56,6 +56,9 @@ class X64Backend : public Backend {
std::unique_ptr<Assembler> CreateAssembler() override; std::unique_ptr<Assembler> CreateAssembler() override;
std::unique_ptr<GuestFunction> CreateGuestFunction(Module* module,
uint32_t address) override;
private: private:
std::unique_ptr<X64CodeCache> code_cache_; std::unique_ptr<X64CodeCache> code_cache_;

View File

@ -17,6 +17,7 @@
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"
#include "xenia/base/memory.h" #include "xenia/base/memory.h"
#include "xenia/cpu/function.h"
namespace xe { namespace xe {
namespace cpu { 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, void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
size_t code_size, size_t stack_size, 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 // 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. // unwind table requires entries AND code to be sorted in order.
size_t low_mark; 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)); 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); uint32_t key = uint32_t(host_pc - kGeneratedCodeBase);
void* fn_entry = std::bsearch( void* fn_entry = std::bsearch(
&key, generated_code_map_.data(), generated_code_map_.size() + 1, &key, generated_code_map_.data(), generated_code_map_.size() + 1,
sizeof(std::pair<uint32_t, FunctionInfo*>), sizeof(std::pair<uint32_t, Function*>),
[](const void* key_ptr, const void* element_ptr) { [](const void* key_ptr, const void* element_ptr) {
auto key = *reinterpret_cast<const uint32_t*>(key_ptr); auto key = *reinterpret_cast<const uint32_t*>(key_ptr);
auto element = auto element =
reinterpret_cast<const std::pair<uint64_t, FunctionInfo*>*>( reinterpret_cast<const std::pair<uint64_t, GuestFunction*>*>(
element_ptr); element_ptr);
if (key < (element->first >> 32)) { if (key < (element->first >> 32)) {
return -1; return -1;
@ -239,7 +240,8 @@ FunctionInfo* X64CodeCache::LookupFunction(uint64_t host_pc) {
} }
}); });
if (fn_entry) { if (fn_entry) {
return reinterpret_cast<const std::pair<uint64_t, FunctionInfo*>*>(fn_entry) return reinterpret_cast<const std::pair<uint64_t, GuestFunction*>*>(
fn_entry)
->second; ->second;
} else { } else {
return nullptr; return nullptr;

View File

@ -50,10 +50,10 @@ class X64CodeCache : public CodeCache {
size_t code_size, size_t stack_size); size_t code_size, size_t stack_size);
void* PlaceGuestCode(uint32_t guest_address, void* machine_code, void* PlaceGuestCode(uint32_t guest_address, void* machine_code,
size_t code_size, size_t stack_size, size_t code_size, size_t stack_size,
FunctionInfo* function_info); GuestFunction* function_info);
uint32_t PlaceData(const void* data, size_t length); uint32_t PlaceData(const void* data, size_t length);
FunctionInfo* LookupFunction(uint64_t host_pc) override; GuestFunction* LookupFunction(uint64_t host_pc) override;
protected: protected:
// All executable code falls within 0x80000000 to 0x9FFFFFFF, so we can // 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. // 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. // This can be used to bsearch on host PC to find the guest function.
// The key is [start address | end address]. // The key is [start address | end address].
std::vector<std::pair<uint64_t, FunctionInfo*>> generated_code_map_; std::vector<std::pair<uint64_t, GuestFunction*>> generated_code_map_;
}; };
} // namespace x64 } // namespace x64

View File

@ -18,6 +18,7 @@
#include "xenia/base/math.h" #include "xenia/base/math.h"
#include "xenia/base/memory.h" #include "xenia/base/memory.h"
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
#include "xenia/cpu/function.h"
// When enabled, this will use Windows 8 APIs to get unwind info. // When enabled, this will use Windows 8 APIs to get unwind info.
// TODO(benvanik): figure out why the callback variant doesn't work. // TODO(benvanik): figure out why the callback variant doesn't work.

View File

@ -27,8 +27,9 @@
#include "xenia/cpu/backend/x64/x64_stack_layout.h" #include "xenia/cpu/backend/x64/x64_stack_layout.h"
#include "xenia/cpu/cpu_flags.h" #include "xenia/cpu/cpu_flags.h"
#include "xenia/cpu/debug_info.h" #include "xenia/cpu/debug_info.h"
#include "xenia/cpu/function.h"
#include "xenia/cpu/processor.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/cpu/thread_state.h"
#include "xenia/debug/debugger.h" #include "xenia/debug/debugger.h"
#include "xenia/profiling.h" #include "xenia/profiling.h"
@ -87,7 +88,7 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
X64Emitter::~X64Emitter() = default; 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, uint32_t debug_info_flags, DebugInfo* debug_info,
void*& out_code_address, size_t& out_code_size, void*& out_code_address, size_t& out_code_size,
std::vector<SourceMapEntry>& out_source_map) { std::vector<SourceMapEntry>& out_source_map) {
@ -96,6 +97,7 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
// Reset. // Reset.
debug_info_ = debug_info; debug_info_ = debug_info;
debug_info_flags_ = debug_info_flags; debug_info_flags_ = debug_info_flags;
trace_data_ = &function->trace_data();
source_map_arena_.Reset(); source_map_arena_.Reset();
// Fill the generator with code. // 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. // Copy the final code to the cache and relocate it.
out_code_size = getSize(); out_code_size = getSize();
out_code_address = Emplace(stack_size, function_info); out_code_address = Emplace(stack_size, function);
// Stash source map. // Stash source map.
source_map_arena_.CloneContents(out_source_map); source_map_arena_.CloneContents(out_source_map);
@ -114,16 +116,16 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
return true; 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. // To avoid changing xbyak, we do a switcharoo here.
// top_ points to the Xbyak buffer, and since we are in AutoGrow mode // 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 // it has pending relocations. We copy the top_ to our buffer, swap the
// pointer, relocate, then return the original scratch pointer for use. // pointer, relocate, then return the original scratch pointer for use.
uint8_t* old_address = top_; uint8_t* old_address = top_;
void* new_address; void* new_address;
if (function_info) { if (function) {
new_address = code_cache_->PlaceGuestCode(function_info->address(), top_, new_address = code_cache_->PlaceGuestCode(function->address(), top_, size_,
size_, stack_size, function_info); stack_size, function);
} else { } else {
new_address = code_cache_->PlaceHostCode(0, top_, size_, stack_size); 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. // Safe now to do some tracing.
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctions) { if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctions) {
// We require 32-bit addresses. // We require 32-bit addresses.
assert_true(uint64_t(debug_info_->trace_data().header()) < UINT_MAX); assert_true(uint64_t(trace_data_->header()) < UINT_MAX);
auto trace_header = debug_info_->trace_data().header(); auto trace_header = trace_data_->header();
// Call count. // Call count.
lock(); lock();
@ -265,11 +267,10 @@ void X64Emitter::MarkSourceOffset(const Instr* i) {
} }
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) { if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) {
auto trace_data = debug_info_->trace_data();
uint32_t instruction_index = uint32_t instruction_index =
(entry->source_offset - trace_data.start_address()) / 4; (entry->source_offset - trace_data_->start_address()) / 4;
lock(); lock();
inc(qword[low_address(trace_data.instruction_execute_counts() + inc(qword[low_address(trace_data_->instruction_execute_counts() +
instruction_index * 8)]); instruction_index * 8)]);
} }
} }
@ -341,8 +342,7 @@ extern "C" uint64_t ResolveFunction(void* raw_context,
// TODO(benvanik): required? // TODO(benvanik): required?
assert_not_zero(target_address); assert_not_zero(target_address);
Function* fn = NULL; auto fn = thread_state->processor()->ResolveFunction(target_address);
thread_state->processor()->ResolveFunction(target_address, &fn);
assert_not_null(fn); assert_not_null(fn);
auto x64_fn = static_cast<X64Function*>(fn); auto x64_fn = static_cast<X64Function*>(fn);
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code()); uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
@ -350,11 +350,11 @@ extern "C" uint64_t ResolveFunction(void* raw_context,
return addr; return addr;
} }
void X64Emitter::Call(const hir::Instr* instr, FunctionInfo* symbol_info) { void X64Emitter::Call(const hir::Instr* instr, GuestFunction* function) {
assert_not_null(symbol_info); assert_not_null(function);
auto fn = reinterpret_cast<X64Function*>(symbol_info->function()); auto fn = static_cast<X64Function*>(function);
// Resolve address to the function to call and store in rax. // 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 // TODO(benvanik): is it worth it to do this? It removes the need for
// a ResolveFunction call, but makes the table less useful. // a ResolveFunction call, but makes the table less useful.
assert_zero(uint64_t(fn->machine_code()) & 0xFFFFFFFF00000000); 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. // Load the pointer to the indirection table maintained in X64CodeCache.
// The target dword will either contain the address of the generated code // The target dword will either contain the address of the generated code
// or a thunk to ResolveAddress. // or a thunk to ResolveAddress.
mov(ebx, symbol_info->address()); mov(ebx, function->address());
mov(eax, dword[ebx]); 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) { uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) {
auto symbol_info = reinterpret_cast<FunctionInfo*>(symbol_info_ptr); auto function = reinterpret_cast<Function*>(function_ptr);
XELOGW("undefined extern call to %.8X %s", symbol_info->address(), XELOGW("undefined extern call to %.8X %s", function->address(),
symbol_info->name().c_str()); function->name().c_str());
return 0; return 0;
} }
void X64Emitter::CallExtern(const hir::Instr* instr, void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
const FunctionInfo* symbol_info) { bool undefined = true;
if (symbol_info->behavior() == FunctionBehavior::kBuiltin && if (function->behavior() == Function::Behavior::kBuiltin) {
symbol_info->builtin_handler()) { auto builtin_function = static_cast<const BuiltinFunction*>(function);
// rcx = context if (builtin_function->handler()) {
// rdx = target host function undefined = false;
// r8 = arg0 // rcx = context
// r9 = arg1 // rdx = target host function
mov(rdx, reinterpret_cast<uint64_t>(symbol_info->builtin_handler())); // r8 = arg0
mov(r8, reinterpret_cast<uint64_t>(symbol_info->builtin_arg0())); // r9 = arg1
mov(r9, reinterpret_cast<uint64_t>(symbol_info->builtin_arg1())); mov(rdx, reinterpret_cast<uint64_t>(builtin_function->handler()));
auto thunk = backend()->guest_to_host_thunk(); mov(r8, reinterpret_cast<uint64_t>(builtin_function->arg0()));
mov(rax, reinterpret_cast<uint64_t>(thunk)); mov(r9, reinterpret_cast<uint64_t>(builtin_function->arg1()));
call(rax); auto thunk = backend()->guest_to_host_thunk();
ReloadECX(); mov(rax, reinterpret_cast<uint64_t>(thunk));
ReloadEDX(); call(rax);
// rax = host return ReloadECX();
} else if (symbol_info->behavior() == FunctionBehavior::kExtern && ReloadEDX();
symbol_info->extern_handler()) { // rax = host return
// rcx = context }
// rdx = target host function } else if (function->behavior() == Function::Behavior::kExtern) {
mov(rdx, reinterpret_cast<uint64_t>(symbol_info->extern_handler())); auto extern_function = static_cast<const GuestFunction*>(function);
mov(r8, qword[rcx + offsetof(cpu::frontend::PPCContext, kernel_state)]); if (extern_function->extern_handler()) {
auto thunk = backend()->guest_to_host_thunk(); undefined = false;
mov(rax, reinterpret_cast<uint64_t>(thunk)); // rcx = context
call(rax); // rdx = target host function
ReloadECX(); mov(rdx, reinterpret_cast<uint64_t>(extern_function->extern_handler()));
ReloadEDX(); mov(r8, qword[rcx + offsetof(cpu::frontend::PPCContext, kernel_state)]);
// rax = host return auto thunk = backend()->guest_to_host_thunk();
} else { mov(rax, reinterpret_cast<uint64_t>(thunk));
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(symbol_info)); call(rax);
ReloadECX();
ReloadEDX();
// rax = host return
}
}
if (undefined) {
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(function));
} }
} }

View File

@ -17,7 +17,6 @@
#include "xenia/cpu/hir/hir_builder.h" #include "xenia/cpu/hir/hir_builder.h"
#include "xenia/cpu/hir/instr.h" #include "xenia/cpu/hir/instr.h"
#include "xenia/cpu/hir/value.h" #include "xenia/cpu/hir/value.h"
#include "xenia/cpu/symbol_info.h"
#include "xenia/debug/function_trace_data.h" #include "xenia/debug/function_trace_data.h"
#include "xenia/memory.h" #include "xenia/memory.h"
@ -27,10 +26,7 @@
namespace xe { namespace xe {
namespace cpu { namespace cpu {
class DebugInfo;
class FunctionInfo;
class Processor; class Processor;
class SymbolInfo;
} // namespace cpu } // namespace cpu
} // namespace xe } // namespace xe
@ -118,7 +114,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
Processor* processor() const { return processor_; } Processor* processor() const { return processor_; }
X64Backend* backend() const { return backend_; } 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, uint32_t debug_info_flags, DebugInfo* debug_info,
void*& out_code_address, size_t& out_code_size, void*& out_code_address, size_t& out_code_size,
std::vector<SourceMapEntry>& out_source_map); std::vector<SourceMapEntry>& out_source_map);
@ -163,9 +159,9 @@ class X64Emitter : public Xbyak::CodeGenerator {
void Trap(uint16_t trap_type = 0); void Trap(uint16_t trap_type = 0);
void UnimplementedInstr(const hir::Instr* i); 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 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(void* fn);
void CallNative(uint64_t (*fn)(void* raw_context)); void CallNative(uint64_t (*fn)(void* raw_context));
void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0)); 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_; } size_t stack_size() const { return stack_size_; }
protected: 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); bool Emit(hir::HIRBuilder* builder, size_t& out_stack_size);
void EmitGetCurrentThreadId(); void EmitGetCurrentThreadId();
void EmitTraceUserCallReturn(); void EmitTraceUserCallReturn();
@ -218,6 +214,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
DebugInfo* debug_info_ = nullptr; DebugInfo* debug_info_ = nullptr;
uint32_t debug_info_flags_ = 0; uint32_t debug_info_flags_ = 0;
debug::FunctionTraceData* trace_data_ = nullptr;
Arena source_map_arena_; Arena source_map_arena_;
size_t stack_size_ = 0; size_t stack_size_ = 0;

View File

@ -18,8 +18,8 @@ namespace cpu {
namespace backend { namespace backend {
namespace x64 { namespace x64 {
X64Function::X64Function(FunctionInfo* symbol_info) X64Function::X64Function(Module* module, uint32_t address)
: Function(symbol_info), machine_code_(nullptr), machine_code_length_(0) {} : GuestFunction(module, address) {}
X64Function::~X64Function() { X64Function::~X64Function() {
// machine_code_ is freed by code cache. // 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; 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) { bool X64Function::CallImpl(ThreadState* thread_state, uint32_t return_address) {
auto backend = auto backend =
reinterpret_cast<X64Backend*>(thread_state->processor()->backend()); reinterpret_cast<X64Backend*>(thread_state->processor()->backend());

View File

@ -11,7 +11,6 @@
#define XENIA_BACKEND_X64_X64_FUNCTION_H_ #define XENIA_BACKEND_X64_X64_FUNCTION_H_
#include "xenia/cpu/function.h" #include "xenia/cpu/function.h"
#include "xenia/cpu/symbol_info.h"
#include "xenia/cpu/thread_state.h" #include "xenia/cpu/thread_state.h"
namespace xe { namespace xe {
@ -19,10 +18,10 @@ namespace cpu {
namespace backend { namespace backend {
namespace x64 { namespace x64 {
class X64Function : public Function { class X64Function : public GuestFunction {
public: public:
X64Function(FunctionInfo* symbol_info); X64Function(Module* module, uint32_t address);
virtual ~X64Function(); ~X64Function() override;
uint8_t* machine_code() const override { return machine_code_; } uint8_t* machine_code() const override { return machine_code_; }
size_t machine_code_length() const override { return machine_code_length_; } 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); void Setup(uint8_t* machine_code, size_t machine_code_length);
protected: protected:
bool AddBreakpointImpl(debug::Breakpoint* breakpoint) override;
bool RemoveBreakpointImpl(debug::Breakpoint* breakpoint) override;
bool CallImpl(ThreadState* thread_state, uint32_t return_address) override; bool CallImpl(ThreadState* thread_state, uint32_t return_address) override;
private: private:
uint8_t* machine_code_; uint8_t* machine_code_ = nullptr;
size_t machine_code_length_; size_t machine_code_length_ = 0;
}; };
} // namespace x64 } // namespace x64

View File

@ -154,7 +154,7 @@ struct OffsetOp : Op<OffsetOp, KEY_TYPE_O> {
}; };
struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> { struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
FunctionInfo* value; Function* value;
protected: protected:
template <typename T, KeyType KEY_TYPE> template <typename T, KeyType KEY_TYPE>
@ -162,7 +162,7 @@ struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
template <hir::Opcode OPCODE, typename... Ts> template <hir::Opcode OPCODE, typename... Ts>
friend struct I; friend struct I;
bool Load(const Instr::Op& op) { bool Load(const Instr::Op& op) {
this->value = op.symbol_info; this->value = op.symbol;
return true; return true;
} }
}; };
@ -856,7 +856,8 @@ EMITTER_OPCODE_TABLE(OPCODE_TRAP_TRUE, TRAP_TRUE_I8, TRAP_TRUE_I16,
// ============================================================================ // ============================================================================
struct CALL : Sequence<CALL, I<OPCODE_CALL, VoidOp, SymbolOp>> { struct CALL : Sequence<CALL, I<OPCODE_CALL, VoidOp, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) { 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<GuestFunction*>(i.src1.value));
} }
}; };
EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL); EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
@ -867,60 +868,66 @@ EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
struct CALL_TRUE_I8 struct CALL_TRUE_I8
: Sequence<CALL_TRUE_I8, I<OPCODE_CALL_TRUE, VoidOp, I8Op, SymbolOp>> { : Sequence<CALL_TRUE_I8, I<OPCODE_CALL_TRUE, VoidOp, I8Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) { static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1); e.test(i.src1, i.src1);
Xbyak::Label skip; Xbyak::Label skip;
e.jz(skip); e.jz(skip);
e.Call(i.instr, i.src2.value); e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip); e.L(skip);
} }
}; };
struct CALL_TRUE_I16 struct CALL_TRUE_I16
: Sequence<CALL_TRUE_I16, I<OPCODE_CALL_TRUE, VoidOp, I16Op, SymbolOp>> { : Sequence<CALL_TRUE_I16, I<OPCODE_CALL_TRUE, VoidOp, I16Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) { static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1); e.test(i.src1, i.src1);
Xbyak::Label skip; Xbyak::Label skip;
e.jz(skip); e.jz(skip);
e.Call(i.instr, i.src2.value); e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip); e.L(skip);
} }
}; };
struct CALL_TRUE_I32 struct CALL_TRUE_I32
: Sequence<CALL_TRUE_I32, I<OPCODE_CALL_TRUE, VoidOp, I32Op, SymbolOp>> { : Sequence<CALL_TRUE_I32, I<OPCODE_CALL_TRUE, VoidOp, I32Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) { static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1); e.test(i.src1, i.src1);
Xbyak::Label skip; Xbyak::Label skip;
e.jz(skip); e.jz(skip);
e.Call(i.instr, i.src2.value); e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip); e.L(skip);
} }
}; };
struct CALL_TRUE_I64 struct CALL_TRUE_I64
: Sequence<CALL_TRUE_I64, I<OPCODE_CALL_TRUE, VoidOp, I64Op, SymbolOp>> { : Sequence<CALL_TRUE_I64, I<OPCODE_CALL_TRUE, VoidOp, I64Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) { static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1); e.test(i.src1, i.src1);
Xbyak::Label skip; Xbyak::Label skip;
e.jz(skip); e.jz(skip);
e.Call(i.instr, i.src2.value); e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip); e.L(skip);
} }
}; };
struct CALL_TRUE_F32 struct CALL_TRUE_F32
: Sequence<CALL_TRUE_F32, I<OPCODE_CALL_TRUE, VoidOp, F32Op, SymbolOp>> { : Sequence<CALL_TRUE_F32, I<OPCODE_CALL_TRUE, VoidOp, F32Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) { static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.vptest(i.src1, i.src1); e.vptest(i.src1, i.src1);
Xbyak::Label skip; Xbyak::Label skip;
e.jz(skip); e.jz(skip);
e.Call(i.instr, i.src2.value); e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip); e.L(skip);
} }
}; };
struct CALL_TRUE_F64 struct CALL_TRUE_F64
: Sequence<CALL_TRUE_F64, I<OPCODE_CALL_TRUE, VoidOp, F64Op, SymbolOp>> { : Sequence<CALL_TRUE_F64, I<OPCODE_CALL_TRUE, VoidOp, F64Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) { static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.vptest(i.src1, i.src1); e.vptest(i.src1, i.src1);
Xbyak::Label skip; Xbyak::Label skip;
e.jz(skip); e.jz(skip);
e.Call(i.instr, i.src2.value); e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip); e.L(skip);
} }
}; };

View File

@ -91,9 +91,9 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
case OPCODE_CALL_TRUE: case OPCODE_CALL_TRUE:
if (i->src1.value->IsConstant()) { if (i->src1.value->IsConstant()) {
if (i->src1.value->IsConstantTrue()) { 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->Replace(&OPCODE_CALL_info, i->flags);
i->src1.symbol_info = symbol_info; i->src1.symbol = symbol;
} else { } else {
i->Remove(); i->Remove();
} }
@ -101,13 +101,13 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
break; break;
case OPCODE_CALL_INDIRECT: case OPCODE_CALL_INDIRECT:
if (i->src1.value->IsConstant()) { if (i->src1.value->IsConstant()) {
FunctionInfo* symbol_info; auto function = processor_->LookupFunction(
if (!processor_->LookupFunctionInfo( uint32_t(i->src1.value->constant.i32));
(uint32_t)i->src1.value->constant.i32, &symbol_info)) { if (!function) {
break; break;
} }
i->Replace(&OPCODE_CALL_info, i->flags); i->Replace(&OPCODE_CALL_info, i->flags);
i->src1.symbol_info = symbol_info; i->src1.symbol = function;
} }
break; break;
case OPCODE_CALL_INDIRECT_TRUE: case OPCODE_CALL_INDIRECT_TRUE:

View File

@ -9,6 +9,7 @@
#include "xenia/cpu/debug_info.h" #include "xenia/cpu/debug_info.h"
#include <cstdio>
#include <cstdlib> #include <cstdlib>
namespace xe { namespace xe {

View File

@ -13,8 +13,6 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include "xenia/debug/function_trace_data.h"
namespace xe { namespace xe {
namespace cpu { namespace cpu {
@ -37,6 +35,9 @@ enum DebugInfoFlags : uint32_t {
kDebugInfoAll = 0xFFFFFFFF, kDebugInfoAll = 0xFFFFFFFF,
}; };
// DEPRECATED
// This will be getting removed or refactored to contain only on-demand
// disassembly data.
class DebugInfo { class DebugInfo {
public: public:
DebugInfo(); DebugInfo();
@ -53,8 +54,6 @@ class DebugInfo {
instruction_result_count_ = value; instruction_result_count_ = value;
} }
debug::FunctionTraceData& trace_data() { return trace_data_; }
const char* source_disasm() const { return source_disasm_; } const char* source_disasm() const { return source_disasm_; }
void set_source_disasm(char* value) { source_disasm_ = value; } void set_source_disasm(char* value) { source_disasm_ = value; }
const char* raw_hir_disasm() const { return raw_hir_disasm_; } const char* raw_hir_disasm() const { return raw_hir_disasm_; }
@ -70,8 +69,6 @@ class DebugInfo {
uint32_t address_reference_count_; uint32_t address_reference_count_;
uint32_t instruction_result_count_; uint32_t instruction_result_count_;
debug::FunctionTraceData trace_data_;
char* source_disasm_; char* source_disasm_;
char* raw_hir_disasm_; char* raw_hir_disasm_;
char* hir_disasm_; char* hir_disasm_;

View File

@ -53,7 +53,7 @@ int InstrEmit_branch(PPCHIRBuilder& f, const char* src, uint64_t cia,
// recursion. // recursion.
uint32_t nia_value = nia->AsUint64() & 0xFFFFFFFF; uint32_t nia_value = nia->AsUint64() & 0xFFFFFFFF;
bool is_recursion = false; bool is_recursion = false;
if (nia_value == f.symbol_info()->address() && lk) { if (nia_value == f.function()->address() && lk) {
is_recursion = true; is_recursion = true;
} }
Label* label = is_recursion ? NULL : f.LookupLabel(nia_value); 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 { } else {
// Call function. // Call function.
auto symbol_info = f.LookupFunction(nia_value); auto function = f.LookupFunction(nia_value);
if (cond) { if (cond) {
if (!expect_true) { if (!expect_true) {
cond = f.IsFalse(cond); cond = f.IsFalse(cond);
} }
f.CallTrue(cond, symbol_info, call_flags); f.CallTrue(cond, function, call_flags);
} else { } else {
f.Call(symbol_info, call_flags); f.Call(function, call_flags);
} }
} }
} else { } else {
@ -419,7 +419,7 @@ XEEMITTER(mcrf, 0x4C000000, XL)(PPCHIRBuilder& f, InstrData& i) {
// System linkage (A-24) // System linkage (A-24)
XEEMITTER(sc, 0x44000002, SC)(PPCHIRBuilder& f, InstrData& i) { XEEMITTER(sc, 0x44000002, SC)(PPCHIRBuilder& f, InstrData& i) {
f.CallExtern(f.symbol_info()); f.CallExtern(f.function());
return 0; return 0;
} }

View File

@ -86,7 +86,7 @@ bool PPCFrontend::Initialize() {
return true; return true;
} }
bool PPCFrontend::DeclareFunction(FunctionInfo* symbol_info) { bool PPCFrontend::DeclareFunction(GuestFunction* function) {
// Could scan or something here. // Could scan or something here.
// Could also check to see if it's a well-known function type and classify // Could also check to see if it's a well-known function type and classify
// for later. // for later.
@ -95,12 +95,10 @@ bool PPCFrontend::DeclareFunction(FunctionInfo* symbol_info) {
return true; return true;
} }
bool PPCFrontend::DefineFunction(FunctionInfo* symbol_info, bool PPCFrontend::DefineFunction(GuestFunction* function,
uint32_t debug_info_flags, uint32_t debug_info_flags) {
Function** out_function) { auto translator = translator_pool_.Allocate(this);
PPCTranslator* translator = translator_pool_.Allocate(this); bool result = translator->Translate(function, debug_info_flags);
bool result =
translator->Translate(symbol_info, debug_info_flags, out_function);
translator_pool_.Release(translator); translator_pool_.Release(translator);
return result; return result;
} }

View File

@ -17,7 +17,6 @@
#include "xenia/base/type_pool.h" #include "xenia/base/type_pool.h"
#include "xenia/cpu/frontend/context_info.h" #include "xenia/cpu/frontend/context_info.h"
#include "xenia/cpu/function.h" #include "xenia/cpu/function.h"
#include "xenia/cpu/symbol_info.h"
#include "xenia/memory.h" #include "xenia/memory.h"
namespace xe { namespace xe {
@ -35,8 +34,8 @@ class PPCTranslator;
struct PPCBuiltins { struct PPCBuiltins {
xe::mutex global_lock; xe::mutex global_lock;
bool global_lock_taken; bool global_lock_taken;
FunctionInfo* check_global_lock; Function* check_global_lock;
FunctionInfo* handle_global_lock; Function* handle_global_lock;
}; };
class PPCFrontend { class PPCFrontend {
@ -51,9 +50,8 @@ class PPCFrontend {
ContextInfo* context_info() const { return context_info_.get(); } ContextInfo* context_info() const { return context_info_.get(); }
PPCBuiltins* builtins() { return &builtins_; } PPCBuiltins* builtins() { return &builtins_; }
bool DeclareFunction(FunctionInfo* symbol_info); bool DeclareFunction(GuestFunction* function);
bool DefineFunction(FunctionInfo* symbol_info, uint32_t debug_info_flags, bool DefineFunction(GuestFunction* function, uint32_t debug_info_flags);
Function** out_function);
private: private:
Processor* processor_; Processor* processor_;

View File

@ -40,27 +40,29 @@ PPCHIRBuilder::PPCHIRBuilder(PPCFrontend* frontend)
PPCHIRBuilder::~PPCHIRBuilder() = default; PPCHIRBuilder::~PPCHIRBuilder() = default;
void PPCHIRBuilder::Reset() { void PPCHIRBuilder::Reset() {
function_ = nullptr;
start_address_ = 0; start_address_ = 0;
instr_count_ = 0;
instr_offset_list_ = NULL; instr_offset_list_ = NULL;
label_list_ = NULL; label_list_ = NULL;
with_debug_info_ = false; with_debug_info_ = false;
HIRBuilder::Reset(); HIRBuilder::Reset();
} }
bool PPCHIRBuilder::Emit(FunctionInfo* symbol_info, uint32_t flags) { bool PPCHIRBuilder::Emit(GuestFunction* function, uint32_t flags) {
SCOPE_profile_cpu_f("cpu"); SCOPE_profile_cpu_f("cpu");
Memory* memory = frontend_->memory(); Memory* memory = frontend_->memory();
symbol_info_ = symbol_info; function_ = function;
start_address_ = symbol_info->address(); start_address_ = function_->address();
instr_count_ = (symbol_info->end_address() - symbol_info->address()) / 4 + 1; instr_count_ = (function_->end_address() - function_->address()) / 4 + 1;
with_debug_info_ = (flags & EMIT_DEBUG_COMMENTS) == EMIT_DEBUG_COMMENTS; with_debug_info_ = (flags & EMIT_DEBUG_COMMENTS) == EMIT_DEBUG_COMMENTS;
if (with_debug_info_) { if (with_debug_info_) {
CommentFormat("%s fn %.8X-%.8X %s", symbol_info->module()->name().c_str(), CommentFormat("%s fn %.8X-%.8X %s", function_->module()->name().c_str(),
symbol_info->address(), symbol_info->end_address(), function_->address(), function_->end_address(),
symbol_info->name().c_str()); function_->name().c_str());
} }
// Allocate offset list. // Allocate offset list.
@ -78,8 +80,8 @@ bool PPCHIRBuilder::Emit(FunctionInfo* symbol_info, uint32_t flags) {
// Always mark entry with label. // Always mark entry with label.
label_list_[0] = NewLabel(); label_list_[0] = NewLabel();
uint32_t start_address = symbol_info->address(); uint32_t start_address = function_->address();
uint32_t end_address = symbol_info->end_address(); uint32_t end_address = function_->end_address();
InstrData i; InstrData i;
for (uint32_t address = start_address, offset = 0; address <= end_address; for (uint32_t address = start_address, offset = 0; address <= end_address;
address += 4, offset++) { address += 4, offset++) {
@ -165,13 +167,8 @@ void PPCHIRBuilder::AnnotateLabel(uint32_t address, Label* label) {
memcpy(label->name, name_buffer, sizeof(name_buffer)); memcpy(label->name, name_buffer, sizeof(name_buffer));
} }
FunctionInfo* PPCHIRBuilder::LookupFunction(uint32_t address) { Function* PPCHIRBuilder::LookupFunction(uint32_t address) {
Processor* processor = frontend_->processor(); return frontend_->processor()->LookupFunction(address);
FunctionInfo* symbol_info;
if (!processor->LookupFunctionInfo(address, &symbol_info)) {
return nullptr;
}
return symbol_info;
} }
Label* PPCHIRBuilder::LookupLabel(uint32_t address) { Label* PPCHIRBuilder::LookupLabel(uint32_t address) {

View File

@ -13,7 +13,6 @@
#include "xenia/base/string_buffer.h" #include "xenia/base/string_buffer.h"
#include "xenia/cpu/hir/hir_builder.h" #include "xenia/cpu/hir/hir_builder.h"
#include "xenia/cpu/function.h" #include "xenia/cpu/function.h"
#include "xenia/cpu/symbol_info.h"
namespace xe { namespace xe {
namespace cpu { namespace cpu {
@ -28,18 +27,18 @@ class PPCHIRBuilder : public hir::HIRBuilder {
public: public:
PPCHIRBuilder(PPCFrontend* frontend); PPCHIRBuilder(PPCFrontend* frontend);
virtual ~PPCHIRBuilder(); ~PPCHIRBuilder() override;
virtual void Reset(); void Reset() override;
enum EmitFlags { enum EmitFlags {
// Emit comment nodes. // Emit comment nodes.
EMIT_DEBUG_COMMENTS = 1 << 0, 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_; } GuestFunction* function() const { return function_; }
FunctionInfo* LookupFunction(uint32_t address); Function* LookupFunction(uint32_t address);
Label* LookupLabel(uint32_t address); Label* LookupLabel(uint32_t address);
Value* LoadLR(); Value* LoadLR();
@ -83,7 +82,6 @@ class PPCHIRBuilder : public hir::HIRBuilder {
private: private:
void AnnotateLabel(uint32_t address, Label* label); void AnnotateLabel(uint32_t address, Label* label);
private:
PPCFrontend* frontend_; PPCFrontend* frontend_;
// Reset whenever needed: // Reset whenever needed:
@ -91,7 +89,7 @@ class PPCHIRBuilder : public hir::HIRBuilder {
// Reset each Emit: // Reset each Emit:
bool with_debug_info_; bool with_debug_info_;
FunctionInfo* symbol_info_; GuestFunction* function_;
uint64_t start_address_; uint64_t start_address_;
uint64_t instr_count_; uint64_t instr_count_;
Instr** instr_offset_list_; Instr** instr_offset_list_;

View File

@ -35,11 +35,10 @@ PPCScanner::~PPCScanner() {}
bool PPCScanner::IsRestGprLr(uint32_t address) { bool PPCScanner::IsRestGprLr(uint32_t address) {
auto function = frontend_->processor()->QueryFunction(address); auto function = frontend_->processor()->QueryFunction(address);
return function && return function && function->behavior() == Function::Behavior::kEpilogReturn;
function->symbol_info()->behavior() == FunctionBehavior::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 // This is a simple basic block analyizer. It walks the start address to the
// end address looking for branches. Each span of instructions between // end address looking for branches. Each span of instructions between
// branches is considered a basic block. When the last blr (that has no // 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(); Memory* memory = frontend_->memory();
LOGPPC("Analyzing function %.8X...", symbol_info->address()); LOGPPC("Analyzing function %.8X...", function->address());
// For debug info, only if needed. // For debug info, only if needed.
uint32_t address_reference_count = 0; uint32_t address_reference_count = 0;
uint32_t instruction_result_count = 0; uint32_t instruction_result_count = 0;
uint32_t start_address = static_cast<uint32_t>(symbol_info->address()); uint32_t start_address = static_cast<uint32_t>(function->address());
uint32_t end_address = static_cast<uint32_t>(symbol_info->end_address()); uint32_t end_address = static_cast<uint32_t>(function->end_address());
uint32_t address = start_address; uint32_t address = start_address;
uint32_t furthest_target = start_address; uint32_t furthest_target = start_address;
size_t blocks_found = 0; 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, LOGPPC("Function ran under: %.8X-%.8X ended at %.8X", start_address,
end_address, address + 4); 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. // If there's spare bits at the end, split the function.
// TODO(benvanik): splitting? // TODO(benvanik): splitting?
@ -289,13 +288,13 @@ bool PPCScanner::Scan(FunctionInfo* symbol_info, DebugInfo* debug_info) {
return true; return true;
} }
std::vector<BlockInfo> PPCScanner::FindBlocks(FunctionInfo* symbol_info) { std::vector<BlockInfo> PPCScanner::FindBlocks(GuestFunction* function) {
Memory* memory = frontend_->memory(); Memory* memory = frontend_->memory();
std::map<uint32_t, BlockInfo> block_map; std::map<uint32_t, BlockInfo> block_map;
uint32_t start_address = symbol_info->address(); uint32_t start_address = function->address();
uint32_t end_address = symbol_info->end_address(); uint32_t end_address = function->end_address();
bool in_block = false; bool in_block = false;
uint32_t block_start = 0; uint32_t block_start = 0;
InstrData i; InstrData i;

View File

@ -13,7 +13,7 @@
#include <vector> #include <vector>
#include "xenia/cpu/debug_info.h" #include "xenia/cpu/debug_info.h"
#include "xenia/cpu/symbol_info.h" #include "xenia/cpu/function.h"
namespace xe { namespace xe {
namespace cpu { namespace cpu {
@ -21,25 +21,24 @@ namespace frontend {
class PPCFrontend; class PPCFrontend;
typedef struct BlockInfo_t { struct BlockInfo {
uint32_t start_address; uint32_t start_address;
uint32_t end_address; uint32_t end_address;
} BlockInfo; };
class PPCScanner { class PPCScanner {
public: public:
PPCScanner(PPCFrontend* frontend); PPCScanner(PPCFrontend* frontend);
~PPCScanner(); ~PPCScanner();
bool Scan(FunctionInfo* symbol_info, DebugInfo* debug_info); bool Scan(GuestFunction* function, DebugInfo* debug_info);
std::vector<BlockInfo> FindBlocks(FunctionInfo* symbol_info); std::vector<BlockInfo> FindBlocks(GuestFunction* function);
private: private:
bool IsRestGprLr(uint32_t address); bool IsRestGprLr(uint32_t address);
private: PPCFrontend* frontend_ = nullptr;
PPCFrontend* frontend_;
}; };
} // namespace frontend } // namespace frontend

View File

@ -99,9 +99,8 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) {
PPCTranslator::~PPCTranslator() = default; PPCTranslator::~PPCTranslator() = default;
bool PPCTranslator::Translate(FunctionInfo* symbol_info, bool PPCTranslator::Translate(GuestFunction* function,
uint32_t debug_info_flags, uint32_t debug_info_flags) {
Function** out_function) {
SCOPE_profile_cpu_f("cpu"); SCOPE_profile_cpu_f("cpu");
// Reset() all caching when we leave. // 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. // 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; return false;
} }
@ -153,19 +152,19 @@ bool PPCTranslator::Translate(FunctionInfo* symbol_info,
if (debug_info_flags & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) { if (debug_info_flags & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) {
// Additional space for instruction coverage counts. // Additional space for instruction coverage counts.
trace_data_size += debug::FunctionTraceData::SizeOfInstructionCounts( 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); uint8_t* trace_data = debugger->AllocateFunctionTraceData(trace_data_size);
if (trace_data) { if (trace_data) {
debug_info->trace_data().Reset(trace_data, trace_data_size, function->trace_data().Reset(trace_data, trace_data_size,
symbol_info->address(), function->address(),
symbol_info->end_address()); function->end_address());
} }
} }
// Stash source. // Stash source.
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmSource) { if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmSource) {
DumpSource(symbol_info, &string_buffer_); DumpSource(function, &string_buffer_);
debug_info->set_source_disasm(string_buffer_.ToString()); debug_info->set_source_disasm(string_buffer_.ToString());
string_buffer_.Reset(); string_buffer_.Reset();
} }
@ -179,7 +178,7 @@ bool PPCTranslator::Translate(FunctionInfo* symbol_info,
if (debug_info) { if (debug_info) {
emit_flags |= PPCHIRBuilder::EMIT_DEBUG_COMMENTS; emit_flags |= PPCHIRBuilder::EMIT_DEBUG_COMMENTS;
} }
if (!builder_->Emit(symbol_info, emit_flags)) { if (!builder_->Emit(function, emit_flags)) {
return false; return false;
} }
@ -203,27 +202,26 @@ bool PPCTranslator::Translate(FunctionInfo* symbol_info,
} }
// Assemble to backend machine code. // Assemble to backend machine code.
if (!assembler_->Assemble(symbol_info, builder_.get(), debug_info_flags, if (!assembler_->Assemble(function, builder_.get(), debug_info_flags,
std::move(debug_info), out_function)) { std::move(debug_info))) {
return false; return false;
} }
return true; return true;
}; };
void PPCTranslator::DumpSource(FunctionInfo* symbol_info, void PPCTranslator::DumpSource(GuestFunction* function,
StringBuffer* string_buffer) { StringBuffer* string_buffer) {
Memory* memory = frontend_->memory(); Memory* memory = frontend_->memory();
string_buffer->AppendFormat( string_buffer->AppendFormat(
"%s fn %.8X-%.8X %s\n", symbol_info->module()->name().c_str(), "%s fn %.8X-%.8X %s\n", function->module()->name().c_str(),
symbol_info->address(), symbol_info->end_address(), function->address(), function->end_address(), function->name().c_str());
symbol_info->name().c_str());
auto blocks = scanner_->FindBlocks(symbol_info); auto blocks = scanner_->FindBlocks(function);
uint32_t start_address = symbol_info->address(); uint32_t start_address = function->address();
uint32_t end_address = symbol_info->end_address(); uint32_t end_address = function->end_address();
InstrData i; InstrData i;
auto block_it = blocks.begin(); auto block_it = blocks.begin();
for (uint32_t address = start_address, offset = 0; address <= end_address; for (uint32_t address = start_address, offset = 0; address <= end_address;

View File

@ -15,7 +15,7 @@
#include "xenia/base/string_buffer.h" #include "xenia/base/string_buffer.h"
#include "xenia/cpu/backend/assembler.h" #include "xenia/cpu/backend/assembler.h"
#include "xenia/cpu/compiler/compiler.h" #include "xenia/cpu/compiler/compiler.h"
#include "xenia/cpu/symbol_info.h" #include "xenia/cpu/function.h"
namespace xe { namespace xe {
namespace cpu { namespace cpu {
@ -30,13 +30,11 @@ class PPCTranslator {
PPCTranslator(PPCFrontend* frontend); PPCTranslator(PPCFrontend* frontend);
~PPCTranslator(); ~PPCTranslator();
bool Translate(FunctionInfo* symbol_info, uint32_t debug_info_flags, bool Translate(GuestFunction* function, uint32_t debug_info_flags);
Function** out_function);
private: private:
void DumpSource(FunctionInfo* symbol_info, StringBuffer* string_buffer); void DumpSource(GuestFunction* function, StringBuffer* string_buffer);
private:
PPCFrontend* frontend_; PPCFrontend* frontend_;
std::unique_ptr<PPCScanner> scanner_; std::unique_ptr<PPCScanner> scanner_;
std::unique_ptr<PPCHIRBuilder> builder_; std::unique_ptr<PPCHIRBuilder> builder_;

View File

@ -224,8 +224,8 @@ class TestRunner {
} }
// Execute test. // Execute test.
xe::cpu::Function* fn = nullptr; auto fn = processor->ResolveFunction(test_case.address);
if (!processor->ResolveFunction(test_case.address, &fn)) { if (!fn) {
XELOGE("Entry function not found"); XELOGE("Entry function not found");
return false; return false;
} }
@ -238,7 +238,9 @@ class TestRunner {
bool result = CheckTestResults(test_case); bool result = CheckTestResults(test_case);
if (!result) { if (!result) {
// Also dump all disasm/etc. // Also dump all disasm/etc.
fn->debug_info()->Dump(); if (fn->is_guest()) {
static_cast<xe::cpu::GuestFunction*>(fn)->debug_info()->Dump();
}
} }
return result; return result;

View File

@ -10,7 +10,7 @@
#include "xenia/cpu/function.h" #include "xenia/cpu/function.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/cpu/symbol_info.h" #include "xenia/cpu/symbol.h"
#include "xenia/cpu/thread_state.h" #include "xenia/cpu/thread_state.h"
namespace xe { namespace xe {
@ -18,12 +18,56 @@ namespace cpu {
using xe::debug::Breakpoint; using xe::debug::Breakpoint;
Function::Function(FunctionInfo* symbol_info) Function::Function(Module* module, uint32_t address)
: address_(symbol_info->address()), symbol_info_(symbol_info) {} : Symbol(Symbol::Type::kFunction, module, address) {}
Function::~Function() = default; 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. // TODO(benvanik): binary search? We know the list is sorted by code order.
for (size_t i = 0; i < source_map_.size(); ++i) { for (size_t i = 0; i < source_map_.size(); ++i) {
const auto& entry = source_map_[i]; const auto& entry = source_map_[i];
@ -34,7 +78,7 @@ const SourceMapEntry* Function::LookupSourceOffset(uint32_t offset) const {
return nullptr; 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. // TODO(benvanik): binary search? We know the list is sorted by code order.
for (size_t i = 0; i < source_map_.size(); ++i) { for (size_t i = 0; i < source_map_.size(); ++i) {
const auto& entry = source_map_[i]; const auto& entry = source_map_[i];
@ -45,7 +89,7 @@ const SourceMapEntry* Function::LookupHIROffset(uint32_t offset) const {
return nullptr; 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. // TODO(benvanik): binary search? We know the list is sorted by code order.
for (int64_t i = source_map_.size() - 1; i >= 0; --i) { for (int64_t i = source_map_.size() - 1; i >= 0; --i) {
const auto& entry = source_map_[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]; return source_map_.empty() ? nullptr : &source_map_[0];
} }
bool Function::AddBreakpoint(Breakpoint* breakpoint) { bool GuestFunction::Call(ThreadState* thread_state, uint32_t return_address) {
std::lock_guard<xe::mutex> 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<xe::mutex> 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<xe::mutex> 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) {
// SCOPE_profile_cpu_f("cpu"); // SCOPE_profile_cpu_f("cpu");
ThreadState* original_thread_state = ThreadState::Get(); ThreadState* original_thread_state = ThreadState::Get();
@ -106,29 +108,25 @@ bool Function::Call(ThreadState* thread_state, uint32_t return_address) {
ThreadState::Bind(thread_state); ThreadState::Bind(thread_state);
} }
bool result = true; bool result = false;
if (behavior_ == Behavior::kExtern) {
if (symbol_info_->behavior() == FunctionBehavior::kBuiltin) { // Special handling for extern functions to speed things up (we don't
auto handler = symbol_info_->builtin_handler(); // trampoline into guest code only to trampoline back out).
assert_not_null(handler); if (extern_handler_) {
handler(thread_state->context(), symbol_info_->builtin_arg0(), extern_handler_(thread_state->context(),
symbol_info_->builtin_arg1()); thread_state->context()->kernel_state);
} else if (symbol_info_->behavior() == FunctionBehavior::kExtern) {
auto handler = symbol_info_->extern_handler();
if (handler) {
handler(thread_state->context(), thread_state->context()->kernel_state);
} else { } else {
XELOGW("undefined extern call to %.8X %s", symbol_info_->address(), XELOGW("undefined extern call to %.8X %s", address(), name().c_str());
symbol_info_->name().c_str());
result = false; result = false;
} }
} else { } else {
CallImpl(thread_state, return_address); result = CallImpl(thread_state, return_address);
} }
if (original_thread_state != thread_state) { if (original_thread_state != thread_state) {
ThreadState::Bind(original_thread_state); ThreadState::Bind(original_thread_state);
} }
return result; return result;
} }

View File

@ -11,32 +11,89 @@
#define XENIA_CPU_FUNCTION_H_ #define XENIA_CPU_FUNCTION_H_
#include <memory> #include <memory>
#include <mutex>
#include <vector> #include <vector>
#include "xenia/base/mutex.h" #include "xenia/base/mutex.h"
#include "xenia/cpu/debug_info.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/cpu/thread_state.h"
#include "xenia/debug/breakpoint.h" #include "xenia/debug/breakpoint.h"
#include "xenia/debug/function_trace_data.h"
namespace xe { namespace xe {
namespace cpu { namespace cpu {
class FunctionInfo;
struct SourceMapEntry { struct SourceMapEntry {
uint32_t source_offset; // Original source address/offset. uint32_t source_offset; // Original source address/offset.
uint32_t hir_offset; // Block ordinal (16b) | Instr ordinal (16b) uint32_t hir_offset; // Block ordinal (16b) | Instr ordinal (16b)
uint32_t code_offset; // Offset from emitted code start. uint32_t code_offset; // Offset from emitted code start.
}; };
class Function { class Function : public Symbol {
public: public:
Function(FunctionInfo* symbol_info); enum class Behavior {
virtual ~Function(); kDefault = 0,
kProlog,
kEpilog,
kEpilogReturn,
kBuiltin,
kExtern,
};
~Function() override;
uint32_t address() const { return address_; } 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 uint8_t* machine_code() const = 0;
virtual size_t machine_code_length() const = 0; virtual size_t machine_code_length() const = 0;
@ -45,32 +102,26 @@ class Function {
void set_debug_info(std::unique_ptr<DebugInfo> debug_info) { void set_debug_info(std::unique_ptr<DebugInfo> debug_info) {
debug_info_ = std::move(debug_info); debug_info_ = std::move(debug_info);
} }
debug::FunctionTraceData& trace_data() { return trace_data_; }
std::vector<SourceMapEntry>& source_map() { return source_map_; } std::vector<SourceMapEntry>& 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* LookupSourceOffset(uint32_t offset) const;
const SourceMapEntry* LookupHIROffset(uint32_t offset) const; const SourceMapEntry* LookupHIROffset(uint32_t offset) const;
const SourceMapEntry* LookupCodeOffset(uint32_t offset) const; const SourceMapEntry* LookupCodeOffset(uint32_t offset) const;
bool AddBreakpoint(debug::Breakpoint* breakpoint); bool Call(ThreadState* thread_state, uint32_t return_address) override;
bool RemoveBreakpoint(debug::Breakpoint* breakpoint);
bool Call(ThreadState* thread_state, uint32_t return_address);
protected: 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; virtual bool CallImpl(ThreadState* thread_state, uint32_t return_address) = 0;
protected: protected:
uint32_t address_;
FunctionInfo* symbol_info_;
std::unique_ptr<DebugInfo> debug_info_; std::unique_ptr<DebugInfo> debug_info_;
debug::FunctionTraceData trace_data_;
std::vector<SourceMapEntry> source_map_; std::vector<SourceMapEntry> source_map_;
ExternHandler extern_handler_ = nullptr;
// TODO(benvanik): move elsewhere? DebugData?
xe::mutex lock_;
std::vector<debug::Breakpoint*> breakpoints_;
}; };
} // namespace cpu } // namespace cpu

View File

@ -13,10 +13,11 @@
#include <cstring> #include <cstring>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/cpu/function.h"
#include "xenia/cpu/hir/block.h" #include "xenia/cpu/hir/block.h"
#include "xenia/cpu/hir/instr.h" #include "xenia/cpu/hir/instr.h"
#include "xenia/cpu/hir/label.h" #include "xenia/cpu/hir/label.h"
#include "xenia/cpu/symbol_info.h" #include "xenia/cpu/symbol.h"
#include "xenia/profiling.h" #include "xenia/profiling.h"
// Will scribble arena memory to hopefully find use before clears. // Will scribble arena memory to hopefully find use before clears.
@ -162,7 +163,7 @@ void HIRBuilder::DumpOp(StringBuffer* str, OpcodeSignatureType sig_type,
break; break;
case OPCODE_SIG_TYPE_S: case OPCODE_SIG_TYPE_S:
if (true) { if (true) {
auto target = op->symbol_info; auto target = op->symbol;
str->Append(!target->name().empty() ? target->name() : "<fn>"); str->Append(!target->name().empty() ? target->name() : "<fn>");
} }
break; break;
@ -820,25 +821,24 @@ void HIRBuilder::TrapTrue(Value* cond, uint16_t trap_code) {
EndBlock(); 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); 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; i->src2.value = i->src3.value = NULL;
EndBlock(); EndBlock();
} }
void HIRBuilder::CallTrue(Value* cond, FunctionInfo* symbol_info, void HIRBuilder::CallTrue(Value* cond, Function* symbol, uint16_t call_flags) {
uint16_t call_flags) {
if (cond->IsConstant()) { if (cond->IsConstant()) {
if (cond->IsConstantTrue()) { if (cond->IsConstantTrue()) {
Call(symbol_info, call_flags); Call(symbol, call_flags);
} }
return; return;
} }
Instr* i = AppendInstr(OPCODE_CALL_TRUE_info, call_flags); Instr* i = AppendInstr(OPCODE_CALL_TRUE_info, call_flags);
i->set_src1(cond); i->set_src1(cond);
i->src2.symbol_info = symbol_info; i->src2.symbol = symbol;
i->src3.value = NULL; i->src3.value = NULL;
EndBlock(); EndBlock();
} }
@ -868,9 +868,9 @@ void HIRBuilder::CallIndirectTrue(Value* cond, Value* value,
EndBlock(); EndBlock();
} }
void HIRBuilder::CallExtern(FunctionInfo* symbol_info) { void HIRBuilder::CallExtern(Function* symbol) {
Instr* i = AppendInstr(OPCODE_CALL_EXTERN_info, 0); 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; i->src2.value = i->src3.value = NULL;
EndBlock(); EndBlock();
} }

View File

@ -83,12 +83,11 @@ class HIRBuilder {
void Trap(uint16_t trap_code = 0); void Trap(uint16_t trap_code = 0);
void TrapTrue(Value* cond, 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 Call(Function* symbol, uint16_t call_flags = 0);
void CallTrue(Value* cond, FunctionInfo* symbol_info, void CallTrue(Value* cond, Function* symbol, uint16_t call_flags = 0);
uint16_t call_flags = 0);
void CallIndirect(Value* value, 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 CallIndirectTrue(Value* cond, Value* value, uint16_t call_flags = 0);
void CallExtern(FunctionInfo* symbol_info); void CallExtern(Function* symbol);
void Return(); void Return();
void ReturnTrue(Value* cond); void ReturnTrue(Value* cond);
void SetReturnAddress(Value* value); void SetReturnAddress(Value* value);

View File

@ -15,7 +15,7 @@
namespace xe { namespace xe {
namespace cpu { namespace cpu {
class FunctionInfo; class Function;
} // namespace cpu } // namespace cpu
} // namespace xe } // namespace xe
@ -37,7 +37,7 @@ class Instr {
uint32_t ordinal; uint32_t ordinal;
typedef union { typedef union {
FunctionInfo* symbol_info; Function* symbol;
Label* label; Label* label;
Value* value; Value* value;
uint64_t offset; uint64_t offset;

View File

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

View File

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

View File

@ -26,12 +26,12 @@ Module::~Module() = default;
bool Module::ContainsAddress(uint32_t address) { return true; } 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(); lock_.lock();
const auto it = map_.find(address); const auto it = map_.find(address);
SymbolInfo* symbol_info = it != map_.end() ? it->second : nullptr; Symbol* symbol = it != map_.end() ? it->second : nullptr;
if (symbol_info) { if (symbol) {
if (symbol_info->status() == SymbolStatus::kDeclaring) { if (symbol->status() == Symbol::Status::kDeclaring) {
// Some other thread is declaring the symbol - wait. // Some other thread is declaring the symbol - wait.
if (wait) { if (wait) {
do { do {
@ -39,133 +39,130 @@ SymbolInfo* Module::LookupSymbol(uint32_t address, bool wait) {
// TODO(benvanik): sleep for less time? // TODO(benvanik): sleep for less time?
xe::threading::Sleep(std::chrono::microseconds(100)); xe::threading::Sleep(std::chrono::microseconds(100));
lock_.lock(); lock_.lock();
} while (symbol_info->status() == SymbolStatus::kDeclaring); } while (symbol->status() == Symbol::Status::kDeclaring);
} else { } else {
// Immediate request, just return. // Immediate request, just return.
symbol_info = nullptr; symbol = nullptr;
} }
} }
} }
lock_.unlock(); lock_.unlock();
return symbol_info; return symbol;
} }
SymbolStatus Module::DeclareSymbol(SymbolType type, uint32_t address, Symbol::Status Module::DeclareSymbol(Symbol::Type type, uint32_t address,
SymbolInfo** out_symbol_info) { Symbol** out_symbol) {
*out_symbol_info = nullptr; *out_symbol = nullptr;
lock_.lock(); lock_.lock();
auto it = map_.find(address); auto it = map_.find(address);
SymbolInfo* symbol_info = it != map_.end() ? it->second : nullptr; Symbol* symbol = it != map_.end() ? it->second : nullptr;
SymbolStatus status; Symbol::Status status;
if (symbol_info) { if (symbol) {
// If we exist but are the wrong type, die. // If we exist but are the wrong type, die.
if (symbol_info->type() != type) { if (symbol->type() != type) {
lock_.unlock(); lock_.unlock();
return SymbolStatus::kFailed; return Symbol::Status::kFailed;
} }
// If we aren't ready yet spin and wait. // 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. // Still declaring, so spin.
do { do {
lock_.unlock(); lock_.unlock();
// TODO(benvanik): sleep for less time? // TODO(benvanik): sleep for less time?
xe::threading::Sleep(std::chrono::microseconds(100)); xe::threading::Sleep(std::chrono::microseconds(100));
lock_.lock(); lock_.lock();
} while (symbol_info->status() == SymbolStatus::kDeclaring); } while (symbol->status() == Symbol::Status::kDeclaring);
} }
status = symbol_info->status(); status = symbol->status();
} else { } else {
// Create and return for initialization. // Create and return for initialization.
switch (type) { switch (type) {
case SymbolType::kFunction: case Symbol::Type::kFunction:
symbol_info = new FunctionInfo(this, address); symbol = CreateFunction(address).release();
break; break;
case SymbolType::kVariable: case Symbol::Type::kVariable:
symbol_info = new VariableInfo(this, address); symbol = new Symbol(Symbol::Type::kVariable, this, address);
break; break;
} }
map_[address] = symbol_info; map_[address] = symbol;
list_.emplace_back(symbol_info); list_.emplace_back(symbol);
status = SymbolStatus::kNew; status = Symbol::Status::kNew;
} }
lock_.unlock(); lock_.unlock();
*out_symbol_info = symbol_info; *out_symbol = symbol;
// Get debug info from providers, if this is new. // 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? // TODO(benvanik): lookup in map data/dwarf/etc?
} }
return status; return status;
} }
SymbolStatus Module::DeclareFunction(uint32_t address, Symbol::Status Module::DeclareFunction(uint32_t address,
FunctionInfo** out_symbol_info) { Function** out_function) {
SymbolInfo* symbol_info; Symbol* symbol;
SymbolStatus status = Symbol::Status status =
DeclareSymbol(SymbolType::kFunction, address, &symbol_info); DeclareSymbol(Symbol::Type::kFunction, address, &symbol);
*out_symbol_info = (FunctionInfo*)symbol_info; *out_function = static_cast<Function*>(symbol);
return status; return status;
} }
SymbolStatus Module::DeclareVariable(uint32_t address, Symbol::Status Module::DeclareVariable(uint32_t address, Symbol** out_symbol) {
VariableInfo** out_symbol_info) { Symbol::Status status =
SymbolInfo* symbol_info; DeclareSymbol(Symbol::Type::kVariable, address, out_symbol);
SymbolStatus status =
DeclareSymbol(SymbolType::kVariable, address, &symbol_info);
*out_symbol_info = (VariableInfo*)symbol_info;
return status; return status;
} }
SymbolStatus Module::DefineSymbol(SymbolInfo* symbol_info) { Symbol::Status Module::DefineSymbol(Symbol* symbol) {
lock_.lock(); lock_.lock();
SymbolStatus status; Symbol::Status status;
if (symbol_info->status() == SymbolStatus::kDeclared) { if (symbol->status() == Symbol::Status::kDeclared) {
// Declared but undefined, so request caller define it. // Declared but undefined, so request caller define it.
symbol_info->set_status(SymbolStatus::kDefining); symbol->set_status(Symbol::Status::kDefining);
status = SymbolStatus::kNew; status = Symbol::Status::kNew;
} else if (symbol_info->status() == SymbolStatus::kDefining) { } else if (symbol->status() == Symbol::Status::kDefining) {
// Still defining, so spin. // Still defining, so spin.
do { do {
lock_.unlock(); lock_.unlock();
// TODO(benvanik): sleep for less time? // TODO(benvanik): sleep for less time?
xe::threading::Sleep(std::chrono::microseconds(100)); xe::threading::Sleep(std::chrono::microseconds(100));
lock_.lock(); lock_.lock();
} while (symbol_info->status() == SymbolStatus::kDefining); } while (symbol->status() == Symbol::Status::kDefining);
status = symbol_info->status(); status = symbol->status();
} else { } else {
status = symbol_info->status(); status = symbol->status();
} }
lock_.unlock(); lock_.unlock();
return status; return status;
} }
SymbolStatus Module::DefineFunction(FunctionInfo* symbol_info) { Symbol::Status Module::DefineFunction(Function* symbol) {
return DefineSymbol((SymbolInfo*)symbol_info); return DefineSymbol((Symbol*)symbol);
} }
SymbolStatus Module::DefineVariable(VariableInfo* symbol_info) { Symbol::Status Module::DefineVariable(Symbol* symbol) {
return DefineSymbol((SymbolInfo*)symbol_info); return DefineSymbol((Symbol*)symbol);
} }
void Module::ForEachFunction(std::function<void(FunctionInfo*)> callback) { void Module::ForEachFunction(std::function<void(Function*)> callback) {
std::lock_guard<xe::mutex> guard(lock_); std::lock_guard<xe::mutex> guard(lock_);
for (auto& symbol_info : list_) { for (auto& symbol : list_) {
if (symbol_info->type() == SymbolType::kFunction) { if (symbol->type() == Symbol::Type::kFunction) {
FunctionInfo* info = static_cast<FunctionInfo*>(symbol_info.get()); Function* info = static_cast<Function*>(symbol.get());
callback(info); callback(info);
} }
} }
} }
void Module::ForEachSymbol(size_t start_index, size_t end_index, void Module::ForEachSymbol(size_t start_index, size_t end_index,
std::function<void(SymbolInfo*)> callback) { std::function<void(Symbol*)> callback) {
std::lock_guard<xe::mutex> guard(lock_); std::lock_guard<xe::mutex> guard(lock_);
start_index = std::min(start_index, list_.size()); start_index = std::min(start_index, list_.size());
end_index = std::min(end_index, list_.size()); end_index = std::min(end_index, list_.size());
for (size_t i = start_index; i <= end_index; ++i) { for (size_t i = start_index; i <= end_index; ++i) {
auto& symbol_info = list_[i]; auto& symbol = list_[i];
callback(symbol_info.get()); callback(symbol.get());
} }
} }
@ -223,19 +220,19 @@ bool Module::ReadMap(const char* file_name) {
if (type_str == "f") { if (type_str == "f") {
// Function. // Function.
FunctionInfo* fn_info; auto function = processor_->LookupFunction(this, address);
if (!processor_->LookupFunctionInfo(this, address, &fn_info)) { if (!function) {
continue; continue;
} }
// Don't overwrite names we've set elsewhere. // 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. // If it's a mangled C++ name (?name@...) just use the name.
// TODO(benvanik): better demangling, or leave it to clients. // TODO(benvanik): better demangling, or leave it to clients.
/*if (name[0] == '?') { /*if (name[0] == '?') {
size_t at = name.find('@'); size_t at = name.find('@');
name = name.substr(1, at - 1); name = name.substr(1, at - 1);
}*/ }*/
fn_info->set_name(name.c_str()); function->set_name(name.c_str());
} }
} else { } else {
// Variable. // Variable.

View File

@ -17,13 +17,13 @@
#include <vector> #include <vector>
#include "xenia/base/mutex.h" #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" #include "xenia/memory.h"
namespace xe { namespace xe {
namespace cpu { namespace cpu {
class Function;
class Processor; class Processor;
class Module { class Module {
@ -37,36 +37,36 @@ class Module {
virtual bool ContainsAddress(uint32_t address); virtual bool ContainsAddress(uint32_t address);
SymbolInfo* LookupSymbol(uint32_t address, bool wait = true); Symbol* LookupSymbol(uint32_t address, bool wait = true);
virtual SymbolStatus DeclareFunction(uint32_t address, virtual Symbol::Status DeclareFunction(uint32_t address,
FunctionInfo** out_symbol_info); Function** out_function);
virtual SymbolStatus DeclareVariable(uint32_t address, virtual Symbol::Status DeclareVariable(uint32_t address, Symbol** out_symbol);
VariableInfo** out_symbol_info);
SymbolStatus DefineFunction(FunctionInfo* symbol_info); Symbol::Status DefineFunction(Function* symbol);
SymbolStatus DefineVariable(VariableInfo* symbol_info); Symbol::Status DefineVariable(Symbol* symbol);
void ForEachFunction(std::function<void(FunctionInfo*)> callback); void ForEachFunction(std::function<void(Function*)> callback);
void ForEachSymbol(size_t start_index, size_t end_index, void ForEachSymbol(size_t start_index, size_t end_index,
std::function<void(SymbolInfo*)> callback); std::function<void(Symbol*)> callback);
size_t QuerySymbolCount(); size_t QuerySymbolCount();
bool ReadMap(const char* file_name); bool ReadMap(const char* file_name);
private:
SymbolStatus DeclareSymbol(SymbolType type, uint32_t address,
SymbolInfo** out_symbol_info);
SymbolStatus DefineSymbol(SymbolInfo* symbol_info);
protected: protected:
Processor* processor_; virtual std::unique_ptr<Function> CreateFunction(uint32_t address) = 0;
Memory* memory_;
Processor* processor_ = nullptr;
Memory* memory_ = nullptr;
private: 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. // TODO(benvanik): replace with a better data structure.
xe::mutex lock_; xe::mutex lock_;
std::unordered_map<uint32_t, SymbolInfo*> map_; std::unordered_map<uint32_t, Symbol*> map_;
std::vector<std::unique_ptr<SymbolInfo>> list_; std::vector<std::unique_ptr<Symbol>> list_;
}; };
} // namespace cpu } // namespace cpu

View File

@ -42,6 +42,11 @@ class BuiltinModule : public Module {
return (address & 0xFFFFFFF0) == 0xFFFFFFF0; return (address & 0xFFFFFFF0) == 0xFFFFFFF0;
} }
protected:
std::unique_ptr<Function> CreateFunction(uint32_t address) override {
return std::unique_ptr<Function>(new BuiltinFunction(this, address));
}
private: private:
std::string name_; std::string name_;
}; };
@ -142,20 +147,22 @@ std::vector<Module*> Processor::GetModules() {
return clone; return clone;
} }
FunctionInfo* Processor::DefineBuiltin(const std::string& name, Function* Processor::DefineBuiltin(const std::string& name,
FunctionInfo::BuiltinHandler handler, BuiltinFunction::Handler handler, void* arg0,
void* arg0, void* arg1) { void* arg1) {
uint32_t address = next_builtin_address_; uint32_t address = next_builtin_address_;
next_builtin_address_ += 4; next_builtin_address_ += 4;
FunctionInfo* fn_info; Function* function;
builtin_module_->DeclareFunction(address, &fn_info); builtin_module_->DeclareFunction(address, &function);
fn_info->set_end_address(address + 4); function->set_end_address(address + 4);
fn_info->set_name(name); function->set_name(name);
fn_info->SetupBuiltin(handler, arg0, arg1);
fn_info->set_status(SymbolStatus::kDeclared);
return fn_info; auto builtin_function = static_cast<BuiltinFunction*>(function);
builtin_function->SetupBuiltin(handler, arg0, arg1);
function->set_status(Symbol::Status::kDeclared);
return function;
} }
Function* Processor::QueryFunction(uint32_t address) { Function* Processor::QueryFunction(uint32_t address) {
@ -170,40 +177,36 @@ std::vector<Function*> Processor::FindFunctionsWithAddress(uint32_t address) {
return entry_table_.FindWithAddress(address); return entry_table_.FindWithAddress(address);
} }
bool Processor::ResolveFunction(uint32_t address, Function** out_function) { Function* Processor::ResolveFunction(uint32_t address) {
*out_function = nullptr;
Entry* entry; Entry* entry;
Entry::Status status = entry_table_.GetOrCreate(address, &entry); Entry::Status status = entry_table_.GetOrCreate(address, &entry);
if (status == Entry::STATUS_NEW) { if (status == Entry::STATUS_NEW) {
// Needs to be generated. We have the 'lock' on it and must do so now. // Needs to be generated. We have the 'lock' on it and must do so now.
// Grab symbol declaration. // Grab symbol declaration.
FunctionInfo* symbol_info; auto function = LookupFunction(address);
if (!LookupFunctionInfo(address, &symbol_info)) { if (!function) {
return false; return nullptr;
} }
if (!DemandFunction(symbol_info, &entry->function)) { if (!DemandFunction(function)) {
entry->status = Entry::STATUS_FAILED; 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; status = entry->status = Entry::STATUS_READY;
} }
if (status == Entry::STATUS_READY) { if (status == Entry::STATUS_READY) {
// Ready to use. // Ready to use.
*out_function = entry->function; return entry->function;
return true;
} else { } else {
// Failed or bad state. // Failed or bad state.
return false; return nullptr;
} }
} }
bool Processor::LookupFunctionInfo(uint32_t address, Function* Processor::LookupFunction(uint32_t address) {
FunctionInfo** out_symbol_info) {
*out_symbol_info = nullptr;
// TODO(benvanik): fast reject invalid addresses/log errors. // TODO(benvanik): fast reject invalid addresses/log errors.
// Find the module that contains the address. // Find the module that contains the address.
@ -221,64 +224,57 @@ bool Processor::LookupFunctionInfo(uint32_t address,
} }
if (!code_module) { if (!code_module) {
// No module found that could contain the address. // 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, Function* Processor::LookupFunction(Module* module, uint32_t address) {
FunctionInfo** out_symbol_info) {
// Atomic create/lookup symbol in module. // Atomic create/lookup symbol in module.
// If we get back the NEW flag we must declare it now. // If we get back the NEW flag we must declare it now.
FunctionInfo* symbol_info = nullptr; Function* function = nullptr;
SymbolStatus symbol_status = module->DeclareFunction(address, &symbol_info); auto symbol_status = module->DeclareFunction(address, &function);
if (symbol_status == SymbolStatus::kNew) { if (symbol_status == Symbol::Status::kNew) {
// Symbol is undeclared, so declare now. // Symbol is undeclared, so declare now.
if (!frontend_->DeclareFunction(symbol_info)) { assert_true(function->is_guest());
symbol_info->set_status(SymbolStatus::kFailed); if (!frontend_->DeclareFunction(static_cast<GuestFunction*>(function))) {
return false; function->set_status(Symbol::Status::kFailed);
return nullptr;
} }
symbol_info->set_status(SymbolStatus::kDeclared); function->set_status(Symbol::Status::kDeclared);
} }
return function;
*out_symbol_info = symbol_info;
return true;
} }
bool Processor::DemandFunction(FunctionInfo* symbol_info, bool Processor::DemandFunction(Function* function) {
Function** out_function) {
*out_function = nullptr;
// Lock function for generation. If it's already being generated // Lock function for generation. If it's already being generated
// by another thread this will block and return DECLARED. // by another thread this will block and return DECLARED.
Module* module = symbol_info->module(); auto module = function->module();
SymbolStatus symbol_status = module->DefineFunction(symbol_info); auto symbol_status = module->DefineFunction(function);
if (symbol_status == SymbolStatus::kNew) { if (symbol_status == Symbol::Status::kNew) {
// Symbol is undefined, so define now. // Symbol is undefined, so define now.
Function* function = nullptr; assert_true(function->is_guest());
if (!frontend_->DefineFunction(symbol_info, debug_info_flags_, &function)) { if (!frontend_->DefineFunction(static_cast<GuestFunction*>(function),
symbol_info->set_status(SymbolStatus::kFailed); debug_info_flags_)) {
function->set_status(Symbol::Status::kFailed);
return false; return false;
} }
symbol_info->set_function(function);
// Before we give the symbol back to the rest, let the debugger know. // Before we give the symbol back to the rest, let the debugger know.
if (debugger_) { if (debugger_) {
debugger_->OnFunctionDefined(symbol_info, function); debugger_->OnFunctionDefined(function);
} }
symbol_info->set_status(SymbolStatus::kDefined); function->set_status(Symbol::Status::kDefined);
symbol_status = symbol_info->status(); symbol_status = function->status();
} }
if (symbol_status == SymbolStatus::kFailed) { if (symbol_status == Symbol::Status::kFailed) {
// Symbol likely failed. // Symbol likely failed.
return false; return false;
} }
*out_function = symbol_info->function();
return true; return true;
} }
@ -286,8 +282,8 @@ bool Processor::Execute(ThreadState* thread_state, uint32_t address) {
SCOPE_profile_cpu_f("cpu"); SCOPE_profile_cpu_f("cpu");
// Attempt to get the function. // Attempt to get the function.
Function* fn; auto function = ResolveFunction(address);
if (!ResolveFunction(address, &fn)) { if (!function) {
// Symbol not found in any module. // Symbol not found in any module.
XELOGCPU("Execute(%.8X): failed to find function", address); XELOGCPU("Execute(%.8X): failed to find function", address);
return false; return false;
@ -305,7 +301,7 @@ bool Processor::Execute(ThreadState* thread_state, uint32_t address) {
context->lr = 0xBCBCBCBC; context->lr = 0xBCBCBCBC;
// Execute the function. // 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->lr = previous_lr;
context->r[1] += 64 + 112; context->r[1] += 64 + 112;

View File

@ -69,17 +69,16 @@ class Processor {
std::vector<Module*> GetModules(); std::vector<Module*> GetModules();
Module* builtin_module() const { return builtin_module_; } Module* builtin_module() const { return builtin_module_; }
FunctionInfo* DefineBuiltin(const std::string& name, Function* DefineBuiltin(const std::string& name,
FunctionInfo::BuiltinHandler handler, void* arg0, BuiltinFunction::Handler handler, void* arg0,
void* arg1); void* arg1);
Function* QueryFunction(uint32_t address); Function* QueryFunction(uint32_t address);
std::vector<Function*> FindFunctionsWithAddress(uint32_t address); std::vector<Function*> FindFunctionsWithAddress(uint32_t address);
bool LookupFunctionInfo(uint32_t address, FunctionInfo** out_symbol_info); Function* LookupFunction(uint32_t address);
bool LookupFunctionInfo(Module* module, uint32_t address, Function* LookupFunction(Module* module, uint32_t address);
FunctionInfo** out_symbol_info); Function* ResolveFunction(uint32_t address);
bool ResolveFunction(uint32_t address, Function** out_function);
bool Execute(ThreadState* thread_state, uint32_t address); bool Execute(ThreadState* thread_state, uint32_t address);
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t args[], uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t args[],
@ -89,7 +88,7 @@ class Processor {
void LowerIrql(Irql old_value); void LowerIrql(Irql old_value);
private: private:
bool DemandFunction(FunctionInfo* symbol_info, Function** out_function); bool DemandFunction(Function* function);
Memory* memory_ = nullptr; Memory* memory_ = nullptr;
debug::Debugger* debugger_ = nullptr; debug::Debugger* debugger_ = nullptr;

View File

@ -12,6 +12,8 @@
#include "xenia/base/filesystem.h" #include "xenia/base/filesystem.h"
#include "xenia/base/platform.h" #include "xenia/base/platform.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"
#include "xenia/cpu/function.h"
#include "xenia/cpu/processor.h"
namespace xe { namespace xe {
namespace cpu { namespace cpu {
@ -60,5 +62,10 @@ bool RawModule::ContainsAddress(uint32_t address) {
return address >= low_address_ && address < high_address_; return address >= low_address_ && address < high_address_;
} }
std::unique_ptr<Function> RawModule::CreateFunction(uint32_t address) {
return std::unique_ptr<Function>(
processor_->backend()->CreateGuestFunction(this, address));
}
} // namespace cpu } // namespace cpu
} // namespace xe } // namespace xe

View File

@ -28,6 +28,9 @@ class RawModule : public Module {
bool ContainsAddress(uint32_t address) override; bool ContainsAddress(uint32_t address) override;
protected:
std::unique_ptr<Function> CreateFunction(uint32_t address) override;
private: private:
std::string name_; std::string name_;
uint32_t base_address_; uint32_t base_address_;

View File

@ -13,7 +13,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "xenia/cpu/symbol_info.h" #include "xenia/cpu/function.h"
namespace xe { namespace xe {
namespace cpu { namespace cpu {
@ -49,7 +49,7 @@ struct StackFrame {
} host_symbol; } host_symbol;
// Contains symbol information for kGuest frames. // Contains symbol information for kGuest frames.
struct { struct {
FunctionInfo* function_info; Function* function;
} guest_symbol; } guest_symbol;
}; };
}; };

View File

@ -205,19 +205,21 @@ class Win32StackWalker : public StackWalker {
if (frame.host_pc >= code_cache_min_ && frame.host_pc < code_cache_max_) { 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. // Guest symbol, so we can look it up quickly in the code cache.
frame.type = StackFrame::Type::kGuest; frame.type = StackFrame::Type::kGuest;
auto function_info = code_cache_->LookupFunction(frame.host_pc); auto function = code_cache_->LookupFunction(frame.host_pc);
if (function_info) { if (function) {
frame.guest_symbol.function_info = function_info; frame.guest_symbol.function = function;
// Figure out where in guest code we are by looking up the // Figure out where in guest code we are by looking up the
// displacement in x64 from the JIT'ed code start to the PC. // displacement in x64 from the JIT'ed code start to the PC.
uint32_t host_displacement = if (function->is_guest()) {
uint32_t(frame.host_pc) - auto guest_function = static_cast<GuestFunction*>(function);
uint32_t(uint64_t(function_info->function()->machine_code())); uint32_t host_displacement =
auto entry = uint32_t(frame.host_pc) -
function_info->function()->LookupCodeOffset(host_displacement); uint32_t(uint64_t(guest_function->machine_code()));
frame.guest_pc = entry->source_offset; auto entry = guest_function->LookupCodeOffset(host_displacement);
frame.guest_pc = entry->source_offset;
}
} else { } else {
frame.guest_symbol.function_info = nullptr; frame.guest_symbol.function = nullptr;
} }
} else { } else {
// Host symbol, which means either emulator or system. // Host symbol, which means either emulator or system.

62
src/xenia/cpu/symbol.h Normal file
View File

@ -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 <cstdint>
#include <string>
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_

View File

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

View File

@ -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 <cstdint>
#include <string>
#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_

View File

@ -71,29 +71,34 @@ bool TestModule::ContainsAddress(uint32_t address) {
return contains_address_(address); return contains_address_(address);
} }
SymbolStatus TestModule::DeclareFunction(uint32_t address, std::unique_ptr<Function> TestModule::CreateFunction(uint32_t address) {
FunctionInfo** out_symbol_info) { return std::unique_ptr<Function>(
SymbolStatus status = Module::DeclareFunction(address, out_symbol_info); processor_->backend()->CreateGuestFunction(this, address));
if (status == SymbolStatus::kNew) { }
auto symbol_info = *out_symbol_info;
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<GuestFunction*>(*out_function);
// Reset() all caching when we leave. // Reset() all caching when we leave.
xe::make_reset_scope(compiler_); xe::make_reset_scope(compiler_);
xe::make_reset_scope(assembler_); xe::make_reset_scope(assembler_);
if (!generate_(*builder_.get())) { if (!generate_(*builder_.get())) {
symbol_info->set_status(SymbolStatus::kFailed); function->set_status(Symbol::Status::kFailed);
return SymbolStatus::kFailed; return Symbol::Status::kFailed;
} }
// Run optimization passes.
compiler_->Compile(builder_.get()); compiler_->Compile(builder_.get());
Function* fn = nullptr; // Assemble the function.
assembler_->Assemble(symbol_info, builder_.get(), 0, nullptr, &fn); assembler_->Assemble(function, builder_.get(), 0, nullptr);
symbol_info->set_function(fn); status = Symbol::Status::kDefined;
status = SymbolStatus::kDefined; function->set_status(status);
symbol_info->set_status(status);
} }
return status; return status;
} }

View File

@ -33,8 +33,11 @@ class TestModule : public Module {
bool ContainsAddress(uint32_t address) override; bool ContainsAddress(uint32_t address) override;
SymbolStatus DeclareFunction(uint32_t address, Symbol::Status DeclareFunction(uint32_t address,
FunctionInfo** out_symbol_info) override; Function** out_symbol) override;
protected:
std::unique_ptr<Function> CreateFunction(uint32_t address) override;
private: private:
std::string name_; std::string name_;

View File

@ -65,8 +65,7 @@ class TestFunction {
void Run(std::function<void(PPCContext*)> pre_call, void Run(std::function<void(PPCContext*)> pre_call,
std::function<void(PPCContext*)> post_call) { std::function<void(PPCContext*)> post_call) {
for (auto& processor : processors) { for (auto& processor : processors) {
xe::cpu::Function* fn; auto fn = processor->ResolveFunction(0x80000000);
processor->ResolveFunction(0x80000000, &fn);
uint32_t stack_size = 64 * 1024; uint32_t stack_size = 64 * 1024;
uint32_t stack_address = memory_size - stack_size; uint32_t stack_address = memory_size - stack_size;

View File

@ -375,12 +375,12 @@ bool XexModule::SetupLibraryImports(const char* name,
} }
// Setup a variable and define it. // Setup a variable and define it.
VariableInfo* var_info; Symbol* var_info;
DeclareVariable(record_addr, &var_info); DeclareVariable(record_addr, &var_info);
var_info->set_name(import_name.GetString()); var_info->set_name(import_name.GetString());
var_info->set_status(SymbolStatus::kDeclared); var_info->set_status(Symbol::Status::kDeclared);
DefineVariable(var_info); DefineVariable(var_info);
var_info->set_status(SymbolStatus::kDefined); var_info->set_status(Symbol::Status::kDefined);
} else if (record_type == 1) { } else if (record_type == 1) {
// Thunk. // Thunk.
if (kernel_export) { if (kernel_export) {
@ -389,10 +389,10 @@ bool XexModule::SetupLibraryImports(const char* name,
import_name.AppendFormat("__%s_%.3X", libbasename.c_str(), ordinal); import_name.AppendFormat("__%s_%.3X", libbasename.c_str(), ordinal);
} }
FunctionInfo* fn_info; Function* function;
DeclareFunction(record_addr, &fn_info); DeclareFunction(record_addr, &function);
fn_info->set_end_address(record_addr + 16 - 4); function->set_end_address(record_addr + 16 - 4);
fn_info->set_name(import_name.GetString()); function->set_name(import_name.GetString());
if (kernel_export) { if (kernel_export) {
// On load we have something like this in memory: // On load we have something like this in memory:
@ -415,14 +415,14 @@ bool XexModule::SetupLibraryImports(const char* name,
xe::store_and_swap<uint32_t>(p + 0x8, 0x60000000); xe::store_and_swap<uint32_t>(p + 0x8, 0x60000000);
xe::store_and_swap<uint32_t>(p + 0xC, 0x60000000); xe::store_and_swap<uint32_t>(p + 0xC, 0x60000000);
FunctionInfo::ExternHandler handler = 0; GuestFunction::ExternHandler handler = nullptr;
if (kernel_export) { if (kernel_export) {
if (kernel_export->function_data.trampoline) { if (kernel_export->function_data.trampoline) {
handler = (FunctionInfo::ExternHandler) handler = (GuestFunction::ExternHandler)
kernel_export->function_data.trampoline; kernel_export->function_data.trampoline;
} else { } else {
handler = handler =
(FunctionInfo::ExternHandler)kernel_export->function_data.shim; (GuestFunction::ExternHandler)kernel_export->function_data.shim;
} }
} else { } else {
XELOGW("WARNING: Imported kernel function %s is unimplemented!", XELOGW("WARNING: Imported kernel function %s is unimplemented!",
@ -430,7 +430,7 @@ bool XexModule::SetupLibraryImports(const char* name,
handler = UndefinedImport; handler = UndefinedImport;
} }
fn_info->SetupExtern(handler); static_cast<GuestFunction*>(function)->SetupExtern(handler);
} else if (user_export_addr) { } else if (user_export_addr) {
// Rewrite PPC code to set r11 to the target address // Rewrite PPC code to set r11 to the target address
// So we'll have: // So we'll have:
@ -458,7 +458,7 @@ bool XexModule::SetupLibraryImports(const char* name,
xe::store_and_swap<uint32_t>(p + 0xC, 0x60000000); xe::store_and_swap<uint32_t>(p + 0xC, 0x60000000);
} }
fn_info->set_status(SymbolStatus::kDeclared); function->set_status(Symbol::Status::kDeclared);
} else { } else {
// Bad. // Bad.
assert_always(); assert_always();
@ -472,6 +472,11 @@ bool XexModule::ContainsAddress(uint32_t address) {
return address >= low_address_ && address < high_address_; return address >= low_address_ && address < high_address_;
} }
std::unique_ptr<Function> XexModule::CreateFunction(uint32_t address) {
return std::unique_ptr<Function>(
processor_->backend()->CreateGuestFunction(this, address));
}
bool XexModule::FindSaveRest() { bool XexModule::FindSaveRest() {
// Special stack save/restore functions. // Special stack save/restore functions.
// http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/md/ppc/xxx.s.htm // 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; uint32_t address = gplr_start;
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__savegprlr_%d", n); snprintf(name, xe::countof(name), "__savegprlr_%d", n);
FunctionInfo* symbol_info; Function* function;
DeclareFunction(address, &symbol_info); DeclareFunction(address, &function);
symbol_info->set_end_address(address + (31 - n) * 4 + 2 * 4); function->set_end_address(address + (31 - n) * 4 + 2 * 4);
symbol_info->set_name(name); function->set_name(name);
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveGprLr; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveGprLr;
symbol_info->set_behavior(FunctionBehavior::kProlog); function->set_behavior(Function::Behavior::kProlog);
symbol_info->set_status(SymbolStatus::kDeclared); function->set_status(Symbol::Status::kDeclared);
address += 4; address += 4;
} }
address = gplr_start + 20 * 4; address = gplr_start + 20 * 4;
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__restgprlr_%d", n); snprintf(name, xe::countof(name), "__restgprlr_%d", n);
FunctionInfo* symbol_info; Function* function;
DeclareFunction(address, &symbol_info); DeclareFunction(address, &function);
symbol_info->set_end_address(address + (31 - n) * 4 + 3 * 4); function->set_end_address(address + (31 - n) * 4 + 3 * 4);
symbol_info->set_name(name); function->set_name(name);
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestGprLr; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestGprLr;
symbol_info->set_behavior(FunctionBehavior::kEpilogReturn); function->set_behavior(Function::Behavior::kEpilogReturn);
symbol_info->set_status(SymbolStatus::kDeclared); function->set_status(Symbol::Status::kDeclared);
address += 4; address += 4;
} }
} }
@ -705,27 +710,27 @@ bool XexModule::FindSaveRest() {
uint32_t address = fpr_start; uint32_t address = fpr_start;
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__savefpr_%d", n); snprintf(name, xe::countof(name), "__savefpr_%d", n);
FunctionInfo* symbol_info; Function* function;
DeclareFunction(address, &symbol_info); DeclareFunction(address, &function);
symbol_info->set_end_address(address + (31 - n) * 4 + 1 * 4); function->set_end_address(address + (31 - n) * 4 + 1 * 4);
symbol_info->set_name(name); function->set_name(name);
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveFpr; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveFpr;
symbol_info->set_behavior(FunctionBehavior::kProlog); function->set_behavior(Function::Behavior::kProlog);
symbol_info->set_status(SymbolStatus::kDeclared); function->set_status(Symbol::Status::kDeclared);
address += 4; address += 4;
} }
address = fpr_start + (18 * 4) + (1 * 4); address = fpr_start + (18 * 4) + (1 * 4);
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__restfpr_%d", n); snprintf(name, xe::countof(name), "__restfpr_%d", n);
FunctionInfo* symbol_info; Function* function;
DeclareFunction(address, &symbol_info); DeclareFunction(address, &function);
symbol_info->set_end_address(address + (31 - n) * 4 + 1 * 4); function->set_end_address(address + (31 - n) * 4 + 1 * 4);
symbol_info->set_name(name); function->set_name(name);
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestFpr; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestFpr;
symbol_info->set_behavior(FunctionBehavior::kEpilog); function->set_behavior(Function::Behavior::kEpilog);
symbol_info->set_status(SymbolStatus::kDeclared); function->set_status(Symbol::Status::kDeclared);
address += 4; address += 4;
} }
} }
@ -738,49 +743,49 @@ bool XexModule::FindSaveRest() {
uint32_t address = vmx_start; uint32_t address = vmx_start;
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__savevmx_%d", n); snprintf(name, xe::countof(name), "__savevmx_%d", n);
FunctionInfo* symbol_info; Function* function;
DeclareFunction(address, &symbol_info); DeclareFunction(address, &function);
symbol_info->set_name(name); function->set_name(name);
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
symbol_info->set_behavior(FunctionBehavior::kProlog); function->set_behavior(Function::Behavior::kProlog);
symbol_info->set_status(SymbolStatus::kDeclared); function->set_status(Symbol::Status::kDeclared);
address += 2 * 4; address += 2 * 4;
} }
address += 4; address += 4;
for (int n = 64; n <= 127; n++) { for (int n = 64; n <= 127; n++) {
snprintf(name, xe::countof(name), "__savevmx_%d", n); snprintf(name, xe::countof(name), "__savevmx_%d", n);
FunctionInfo* symbol_info; Function* function;
DeclareFunction(address, &symbol_info); DeclareFunction(address, &function);
symbol_info->set_name(name); function->set_name(name);
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
symbol_info->set_behavior(FunctionBehavior::kProlog); function->set_behavior(Function::Behavior::kProlog);
symbol_info->set_status(SymbolStatus::kDeclared); function->set_status(Symbol::Status::kDeclared);
address += 2 * 4; address += 2 * 4;
} }
address = vmx_start + (18 * 2 * 4) + (1 * 4) + (64 * 2 * 4) + (1 * 4); address = vmx_start + (18 * 2 * 4) + (1 * 4) + (64 * 2 * 4) + (1 * 4);
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__restvmx_%d", n); snprintf(name, xe::countof(name), "__restvmx_%d", n);
FunctionInfo* symbol_info; Function* function;
DeclareFunction(address, &symbol_info); DeclareFunction(address, &function);
symbol_info->set_name(name); function->set_name(name);
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx;
symbol_info->set_behavior(FunctionBehavior::kEpilog); function->set_behavior(Function::Behavior::kEpilog);
symbol_info->set_status(SymbolStatus::kDeclared); function->set_status(Symbol::Status::kDeclared);
address += 2 * 4; address += 2 * 4;
} }
address += 4; address += 4;
for (int n = 64; n <= 127; n++) { for (int n = 64; n <= 127; n++) {
snprintf(name, xe::countof(name), "__restvmx_%d", n); snprintf(name, xe::countof(name), "__restvmx_%d", n);
FunctionInfo* symbol_info; Function* function;
DeclareFunction(address, &symbol_info); DeclareFunction(address, &function);
symbol_info->set_name(name); function->set_name(name);
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx;
symbol_info->set_behavior(FunctionBehavior::kEpilog); function->set_behavior(Function::Behavior::kEpilog);
symbol_info->set_status(SymbolStatus::kDeclared); function->set_status(Symbol::Status::kDeclared);
address += 2 * 4; address += 2 * 4;
} }
} }

View File

@ -77,12 +77,14 @@ class XexModule : public xe::cpu::Module {
bool ContainsAddress(uint32_t address) override; bool ContainsAddress(uint32_t address) override;
protected:
std::unique_ptr<Function> CreateFunction(uint32_t address) override;
private: private:
bool SetupLibraryImports(const char* name, bool SetupLibraryImports(const char* name,
const xex2_import_library* library); const xex2_import_library* library);
bool FindSaveRest(); bool FindSaveRest();
private:
Processor* processor_ = nullptr; Processor* processor_ = nullptr;
kernel::KernelState* kernel_state_ = nullptr; kernel::KernelState* kernel_state_ = nullptr;
std::string name_; std::string name_;

View File

@ -308,10 +308,10 @@ bool DebugServer::ProcessPacket(const proto::Packet* packet) {
frame_body->host_function_address = frame.host_symbol.address; frame_body->host_function_address = frame.host_symbol.address;
frame_body->guest_pc = frame.guest_pc; frame_body->guest_pc = frame.guest_pc;
frame_body->guest_function_address = 0; frame_body->guest_function_address = 0;
auto function_info = frame.guest_symbol.function_info; auto function = frame.guest_symbol.function;
if (frame.type == cpu::StackFrame::Type::kGuest && function_info) { if (frame.type == cpu::StackFrame::Type::kGuest && function) {
frame_body->guest_function_address = function_info->address(); frame_body->guest_function_address = function->address();
std::strncpy(frame_body->name, function_info->name().c_str(), std::strncpy(frame_body->name, function->name().c_str(),
xe::countof(frame_body->name)); xe::countof(frame_body->name));
} else { } else {
std::strncpy(frame_body->name, frame.host_symbol.name, std::strncpy(frame_body->name, frame.host_symbol.name,

View File

@ -169,10 +169,9 @@ void Debugger::DumpThreadStacks() {
XELOGI(" %.2lld %.16llX %s", count - i - 1, frame.host_pc, XELOGI(" %.2lld %.16llX %s", count - i - 1, frame.host_pc,
frame.host_symbol.name); frame.host_symbol.name);
} else { } 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, XELOGI(" %.2lld %.16llX %.8X %s", count - i - 1, frame.host_pc,
frame.guest_pc, frame.guest_pc, function ? function->name().c_str() : "?");
function_info ? function_info->name().c_str() : "?");
} }
} }
} }
@ -190,12 +189,13 @@ int Debugger::AddBreakpoint(Breakpoint* breakpoint) {
auto fns = auto fns =
emulator_->processor()->FindFunctionsWithAddress(breakpoint->address()); emulator_->processor()->FindFunctionsWithAddress(breakpoint->address());
// TODO(benvanik): breakpoints.
// Add. // Add.
for (auto fn : fns) { // for (auto fn : fns) {
if (fn->AddBreakpoint(breakpoint)) { // if (fn->AddBreakpoint(breakpoint)) {
return 1; // return 1;
} // }
} //}
return 0; return 0;
} }
@ -226,9 +226,10 @@ int Debugger::RemoveBreakpoint(Breakpoint* breakpoint) {
emulator_->processor()->FindFunctionsWithAddress(breakpoint->address()); emulator_->processor()->FindFunctionsWithAddress(breakpoint->address());
// Remove. // Remove.
for (auto fn : fns) { // TODO(benvanik): breakpoints.
/*for (auto fn : fns) {
fn->RemoveBreakpoint(breakpoint); fn->RemoveBreakpoint(breakpoint);
} }*/
return 0; return 0;
} }
@ -322,31 +323,30 @@ void Debugger::OnThreadDestroyed(xe::kernel::XThread* thread) {
// TODO(benvanik): notify transports. // TODO(benvanik): notify transports.
} }
void Debugger::OnFunctionDefined(cpu::FunctionInfo* symbol_info, void Debugger::OnFunctionDefined(cpu::Function* function) {
cpu::Function* function) { // TODO(benvanik): breakpoints?
// Man, I'd love not to take this lock. // Man, I'd love not to take this lock.
std::vector<Breakpoint*> breakpoints; // std::vector<Breakpoint*> breakpoints;
{ //{
std::lock_guard<std::recursive_mutex> lock(mutex_); // std::lock_guard<std::recursive_mutex> lock(mutex_);
for (uint32_t address = symbol_info->address(); // for (uint32_t address = function->address();
address <= symbol_info->end_address(); address += 4) { // address <= function->end_address(); address += 4) {
auto range = breakpoints_.equal_range(address); // auto range = breakpoints_.equal_range(address);
if (range.first == range.second) { // if (range.first == range.second) {
continue; // continue;
} // }
for (auto it = range.first; it != range.second; ++it) { // for (auto it = range.first; it != range.second; ++it) {
Breakpoint* breakpoint = it->second; // Breakpoint* breakpoint = it->second;
breakpoints.push_back(breakpoint); // breakpoints.push_back(breakpoint);
} // }
} // }
} //}
// if (breakpoints.size()) {
if (breakpoints.size()) { // // Breakpoints to add!
// Breakpoints to add! // for (auto breakpoint : breakpoints) {
for (auto breakpoint : breakpoints) { // function->AddBreakpoint(breakpoint);
function->AddBreakpoint(breakpoint); // }
} //}
}
} }
void Debugger::OnBreakpointHit(xe::kernel::XThread* thread, void Debugger::OnBreakpointHit(xe::kernel::XThread* thread,

View File

@ -83,8 +83,7 @@ class Debugger {
void OnThreadExit(xe::kernel::XThread* thread); void OnThreadExit(xe::kernel::XThread* thread);
void OnThreadDestroyed(xe::kernel::XThread* thread); void OnThreadDestroyed(xe::kernel::XThread* thread);
void OnFunctionDefined(cpu::FunctionInfo* symbol_info, void OnFunctionDefined(cpu::Function* function);
cpu::Function* function);
void OnBreakpointHit(xe::kernel::XThread* thread, Breakpoint* breakpoint); void OnBreakpointHit(xe::kernel::XThread* thread, Breakpoint* breakpoint);