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

View File

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

View File

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

View File

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

View File

@ -35,10 +35,9 @@ class X64Assembler : public Assembler {
void Reset() override;
bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder,
bool Assemble(GuestFunction* function, hir::HIRBuilder* builder,
uint32_t debug_info_flags,
std::unique_ptr<DebugInfo> debug_info,
Function** out_function) override;
std::unique_ptr<DebugInfo> debug_info) override;
private:
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_assembler.h"
#include "xenia/cpu/backend/x64/x64_emitter.h"
#include "xenia/cpu/backend/x64/x64_code_cache.h"
#include "xenia/cpu/backend/x64/x64_emitter.h"
#include "xenia/cpu/backend/x64/x64_function.h"
#include "xenia/cpu/backend/x64/x64_sequences.h"
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
#include "xenia/cpu/processor.h"
@ -106,6 +107,11 @@ std::unique_ptr<Assembler> X64Backend::CreateAssembler() {
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;
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<GuestFunction> CreateGuestFunction(Module* module,
uint32_t address) override;
private:
std::unique_ptr<X64CodeCache> code_cache_;

View File

@ -17,6 +17,7 @@
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "xenia/cpu/function.h"
namespace xe {
namespace cpu {
@ -120,7 +121,7 @@ void* X64CodeCache::PlaceHostCode(uint32_t guest_address, void* machine_code,
void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
size_t code_size, size_t stack_size,
FunctionInfo* function_info) {
GuestFunction* function_info) {
// Hold a lock while we bump the pointers up. This is important as the
// unwind table requires entries AND code to be sorted in order.
size_t low_mark;
@ -220,15 +221,15 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
return uint32_t(uintptr_t(data_address));
}
FunctionInfo* X64CodeCache::LookupFunction(uint64_t host_pc) {
GuestFunction* X64CodeCache::LookupFunction(uint64_t host_pc) {
uint32_t key = uint32_t(host_pc - kGeneratedCodeBase);
void* fn_entry = std::bsearch(
&key, generated_code_map_.data(), generated_code_map_.size() + 1,
sizeof(std::pair<uint32_t, FunctionInfo*>),
sizeof(std::pair<uint32_t, Function*>),
[](const void* key_ptr, const void* element_ptr) {
auto key = *reinterpret_cast<const uint32_t*>(key_ptr);
auto element =
reinterpret_cast<const std::pair<uint64_t, FunctionInfo*>*>(
reinterpret_cast<const std::pair<uint64_t, GuestFunction*>*>(
element_ptr);
if (key < (element->first >> 32)) {
return -1;
@ -239,7 +240,8 @@ FunctionInfo* X64CodeCache::LookupFunction(uint64_t host_pc) {
}
});
if (fn_entry) {
return reinterpret_cast<const std::pair<uint64_t, FunctionInfo*>*>(fn_entry)
return reinterpret_cast<const std::pair<uint64_t, GuestFunction*>*>(
fn_entry)
->second;
} else {
return nullptr;

View File

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

View File

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

View File

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

View File

@ -18,8 +18,8 @@ namespace cpu {
namespace backend {
namespace x64 {
X64Function::X64Function(FunctionInfo* symbol_info)
: Function(symbol_info), machine_code_(nullptr), machine_code_length_(0) {}
X64Function::X64Function(Module* module, uint32_t address)
: GuestFunction(module, address) {}
X64Function::~X64Function() {
// machine_code_ is freed by code cache.
@ -30,14 +30,6 @@ void X64Function::Setup(uint8_t* machine_code, size_t machine_code_length) {
machine_code_length_ = machine_code_length;
}
bool X64Function::AddBreakpointImpl(debug::Breakpoint* breakpoint) {
return false;
}
bool X64Function::RemoveBreakpointImpl(debug::Breakpoint* breakpoint) {
return false;
}
bool X64Function::CallImpl(ThreadState* thread_state, uint32_t return_address) {
auto backend =
reinterpret_cast<X64Backend*>(thread_state->processor()->backend());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,32 +11,89 @@
#define XENIA_CPU_FUNCTION_H_
#include <memory>
#include <mutex>
#include <vector>
#include "xenia/base/mutex.h"
#include "xenia/cpu/debug_info.h"
#include "xenia/cpu/frontend/ppc_context.h"
#include "xenia/cpu/symbol.h"
#include "xenia/cpu/thread_state.h"
#include "xenia/debug/breakpoint.h"
#include "xenia/debug/function_trace_data.h"
namespace xe {
namespace cpu {
class FunctionInfo;
struct SourceMapEntry {
uint32_t source_offset; // Original source address/offset.
uint32_t hir_offset; // Block ordinal (16b) | Instr ordinal (16b)
uint32_t code_offset; // Offset from emitted code start.
};
class Function {
class Function : public Symbol {
public:
Function(FunctionInfo* symbol_info);
virtual ~Function();
enum class Behavior {
kDefault = 0,
kProlog,
kEpilog,
kEpilogReturn,
kBuiltin,
kExtern,
};
~Function() override;
uint32_t address() const { return address_; }
FunctionInfo* symbol_info() const { return symbol_info_; }
bool has_end_address() const { return end_address_ > 0; }
uint32_t end_address() const { return end_address_; }
void set_end_address(uint32_t value) { end_address_ = value; }
Behavior behavior() const { return behavior_; }
void set_behavior(Behavior value) { behavior_ = value; }
bool is_guest() const { return behavior_ != Behavior::kBuiltin; }
virtual bool Call(ThreadState* thread_state, uint32_t return_address) = 0;
protected:
Function(Module* module, uint32_t address);
uint32_t end_address_ = 0;
Behavior behavior_ = Behavior::kDefault;
};
class BuiltinFunction : public Function {
public:
typedef void (*Handler)(frontend::PPCContext* ppc_context, void* arg0,
void* arg1);
BuiltinFunction(Module* module, uint32_t address);
~BuiltinFunction() override;
void SetupBuiltin(Handler handler, void* arg0, void* arg1);
Handler handler() const { return handler_; }
void* arg0() const { return arg0_; }
void* arg1() const { return arg1_; }
bool Call(ThreadState* thread_state, uint32_t return_address) override;
protected:
Handler handler_ = nullptr;
void* arg0_ = nullptr;
void* arg1_ = nullptr;
};
class GuestFunction : public Function {
public:
typedef void (*ExternHandler)(frontend::PPCContext* ppc_context,
kernel::KernelState* kernel_state);
GuestFunction(Module* module, uint32_t address);
~GuestFunction() override;
uint32_t address() const { return address_; }
bool has_end_address() const { return end_address_ > 0; }
uint32_t end_address() const { return end_address_; }
void set_end_address(uint32_t value) { end_address_ = value; }
virtual uint8_t* machine_code() const = 0;
virtual size_t machine_code_length() const = 0;
@ -45,32 +102,26 @@ class Function {
void set_debug_info(std::unique_ptr<DebugInfo> debug_info) {
debug_info_ = std::move(debug_info);
}
debug::FunctionTraceData& trace_data() { return trace_data_; }
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* LookupHIROffset(uint32_t offset) const;
const SourceMapEntry* LookupCodeOffset(uint32_t offset) const;
bool AddBreakpoint(debug::Breakpoint* breakpoint);
bool RemoveBreakpoint(debug::Breakpoint* breakpoint);
bool Call(ThreadState* thread_state, uint32_t return_address);
bool Call(ThreadState* thread_state, uint32_t return_address) override;
protected:
debug::Breakpoint* FindBreakpoint(uint32_t address);
virtual bool AddBreakpointImpl(debug::Breakpoint* breakpoint) { return 0; }
virtual bool RemoveBreakpointImpl(debug::Breakpoint* breakpoint) { return 0; }
virtual bool CallImpl(ThreadState* thread_state, uint32_t return_address) = 0;
protected:
uint32_t address_;
FunctionInfo* symbol_info_;
std::unique_ptr<DebugInfo> debug_info_;
debug::FunctionTraceData trace_data_;
std::vector<SourceMapEntry> source_map_;
// TODO(benvanik): move elsewhere? DebugData?
xe::mutex lock_;
std::vector<debug::Breakpoint*> breakpoints_;
ExternHandler extern_handler_ = nullptr;
};
} // namespace cpu

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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