Refactoring SymbolInfo/FunctionInfo/Function into Symbol/Function.
This commit is contained in:
parent
48d6e6becf
commit
eaa1a8ee3a
|
@ -15,8 +15,7 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
class DebugInfo;
|
class DebugInfo;
|
||||||
class Function;
|
class GuestFunction;
|
||||||
class FunctionInfo;
|
|
||||||
namespace hir {
|
namespace hir {
|
||||||
class HIRBuilder;
|
class HIRBuilder;
|
||||||
} // namespace hir
|
} // namespace hir
|
||||||
|
@ -38,10 +37,9 @@ class Assembler {
|
||||||
|
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
|
|
||||||
virtual bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
virtual bool Assemble(GuestFunction* function, hir::HIRBuilder* builder,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags,
|
||||||
std::unique_ptr<DebugInfo> debug_info,
|
std::unique_ptr<DebugInfo> debug_info) = 0;
|
||||||
Function** out_function) = 0;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Backend* backend_;
|
Backend* backend_;
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
class GuestFunction;
|
||||||
|
class Module;
|
||||||
class Processor;
|
class Processor;
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
@ -46,6 +48,9 @@ class Backend {
|
||||||
|
|
||||||
virtual std::unique_ptr<Assembler> CreateAssembler() = 0;
|
virtual std::unique_ptr<Assembler> CreateAssembler() = 0;
|
||||||
|
|
||||||
|
virtual std::unique_ptr<GuestFunction> CreateGuestFunction(
|
||||||
|
Module* module, uint32_t address) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Processor* processor_;
|
Processor* processor_;
|
||||||
MachineInfo machine_info_;
|
MachineInfo machine_info_;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "xenia/cpu/symbol_info.h"
|
#include "xenia/cpu/function.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
@ -29,7 +29,7 @@ class CodeCache {
|
||||||
|
|
||||||
// Finds a function based on the given host PC (that may be within a
|
// Finds a function based on the given host PC (that may be within a
|
||||||
// function).
|
// function).
|
||||||
virtual FunctionInfo* LookupFunction(uint64_t host_pc) = 0;
|
virtual GuestFunction* LookupFunction(uint64_t host_pc) = 0;
|
||||||
|
|
||||||
// Finds platform-specific function unwind info for the given host PC.
|
// Finds platform-specific function unwind info for the given host PC.
|
||||||
virtual void* LookupUnwindInfo(uint64_t host_pc) = 0;
|
virtual void* LookupUnwindInfo(uint64_t host_pc) = 0;
|
||||||
|
|
|
@ -66,30 +66,26 @@ void X64Assembler::Reset() {
|
||||||
Assembler::Reset();
|
Assembler::Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
|
bool X64Assembler::Assemble(GuestFunction* function, HIRBuilder* builder,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags,
|
||||||
std::unique_ptr<DebugInfo> debug_info,
|
std::unique_ptr<DebugInfo> debug_info) {
|
||||||
Function** out_function) {
|
|
||||||
SCOPE_profile_cpu_f("cpu");
|
SCOPE_profile_cpu_f("cpu");
|
||||||
|
|
||||||
// Reset when we leave.
|
// Reset when we leave.
|
||||||
xe::make_reset_scope(this);
|
xe::make_reset_scope(this);
|
||||||
|
|
||||||
// Create now, and populate as we go.
|
|
||||||
// We may throw it away if we fail.
|
|
||||||
auto fn = std::make_unique<X64Function>(symbol_info);
|
|
||||||
|
|
||||||
// Lower HIR -> x64.
|
// Lower HIR -> x64.
|
||||||
void* machine_code = nullptr;
|
void* machine_code = nullptr;
|
||||||
size_t code_size = 0;
|
size_t code_size = 0;
|
||||||
if (!emitter_->Emit(symbol_info, builder, debug_info_flags, debug_info.get(),
|
if (!emitter_->Emit(function, builder, debug_info_flags, debug_info.get(),
|
||||||
machine_code, code_size, fn->source_map())) {
|
machine_code, code_size, function->source_map())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stash generated machine code.
|
// Stash generated machine code.
|
||||||
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmMachineCode) {
|
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmMachineCode) {
|
||||||
DumpMachineCode(machine_code, code_size, fn->source_map(), &string_buffer_);
|
DumpMachineCode(machine_code, code_size, function->source_map(),
|
||||||
|
&string_buffer_);
|
||||||
debug_info->set_machine_code_disasm(string_buffer_.ToString());
|
debug_info->set_machine_code_disasm(string_buffer_.ToString());
|
||||||
string_buffer_.Reset();
|
string_buffer_.Reset();
|
||||||
}
|
}
|
||||||
|
@ -102,11 +98,10 @@ bool X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn->set_debug_info(std::move(debug_info));
|
function->set_debug_info(std::move(debug_info));
|
||||||
fn->Setup(reinterpret_cast<uint8_t*>(machine_code), code_size);
|
static_cast<X64Function*>(function)
|
||||||
|
->Setup(reinterpret_cast<uint8_t*>(machine_code), code_size);
|
||||||
|
|
||||||
// Pass back ownership.
|
|
||||||
*out_function = fn.release();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,9 @@ class X64Assembler : public Assembler {
|
||||||
|
|
||||||
void Reset() override;
|
void Reset() override;
|
||||||
|
|
||||||
bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
bool Assemble(GuestFunction* function, hir::HIRBuilder* builder,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags,
|
||||||
std::unique_ptr<DebugInfo> debug_info,
|
std::unique_ptr<DebugInfo> debug_info) override;
|
||||||
Function** out_function) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DumpMachineCode(void* machine_code, size_t code_size,
|
void DumpMachineCode(void* machine_code, size_t code_size,
|
||||||
|
|
|
@ -10,8 +10,9 @@
|
||||||
#include "xenia/cpu/backend/x64/x64_backend.h"
|
#include "xenia/cpu/backend/x64/x64_backend.h"
|
||||||
|
|
||||||
#include "xenia/cpu/backend/x64/x64_assembler.h"
|
#include "xenia/cpu/backend/x64/x64_assembler.h"
|
||||||
#include "xenia/cpu/backend/x64/x64_emitter.h"
|
|
||||||
#include "xenia/cpu/backend/x64/x64_code_cache.h"
|
#include "xenia/cpu/backend/x64/x64_code_cache.h"
|
||||||
|
#include "xenia/cpu/backend/x64/x64_emitter.h"
|
||||||
|
#include "xenia/cpu/backend/x64/x64_function.h"
|
||||||
#include "xenia/cpu/backend/x64/x64_sequences.h"
|
#include "xenia/cpu/backend/x64/x64_sequences.h"
|
||||||
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
|
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
|
@ -106,6 +107,11 @@ std::unique_ptr<Assembler> X64Backend::CreateAssembler() {
|
||||||
return std::make_unique<X64Assembler>(this);
|
return std::make_unique<X64Assembler>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GuestFunction> X64Backend::CreateGuestFunction(
|
||||||
|
Module* module, uint32_t address) {
|
||||||
|
return std::make_unique<X64Function>(module, address);
|
||||||
|
}
|
||||||
|
|
||||||
using namespace Xbyak;
|
using namespace Xbyak;
|
||||||
|
|
||||||
X64ThunkEmitter::X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator)
|
X64ThunkEmitter::X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator)
|
||||||
|
|
|
@ -56,6 +56,9 @@ class X64Backend : public Backend {
|
||||||
|
|
||||||
std::unique_ptr<Assembler> CreateAssembler() override;
|
std::unique_ptr<Assembler> CreateAssembler() override;
|
||||||
|
|
||||||
|
std::unique_ptr<GuestFunction> CreateGuestFunction(Module* module,
|
||||||
|
uint32_t address) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<X64CodeCache> code_cache_;
|
std::unique_ptr<X64CodeCache> code_cache_;
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/base/memory.h"
|
#include "xenia/base/memory.h"
|
||||||
|
#include "xenia/cpu/function.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
@ -120,7 +121,7 @@ void* X64CodeCache::PlaceHostCode(uint32_t guest_address, void* machine_code,
|
||||||
|
|
||||||
void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||||
size_t code_size, size_t stack_size,
|
size_t code_size, size_t stack_size,
|
||||||
FunctionInfo* function_info) {
|
GuestFunction* function_info) {
|
||||||
// Hold a lock while we bump the pointers up. This is important as the
|
// Hold a lock while we bump the pointers up. This is important as the
|
||||||
// unwind table requires entries AND code to be sorted in order.
|
// unwind table requires entries AND code to be sorted in order.
|
||||||
size_t low_mark;
|
size_t low_mark;
|
||||||
|
@ -220,15 +221,15 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
||||||
return uint32_t(uintptr_t(data_address));
|
return uint32_t(uintptr_t(data_address));
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionInfo* X64CodeCache::LookupFunction(uint64_t host_pc) {
|
GuestFunction* X64CodeCache::LookupFunction(uint64_t host_pc) {
|
||||||
uint32_t key = uint32_t(host_pc - kGeneratedCodeBase);
|
uint32_t key = uint32_t(host_pc - kGeneratedCodeBase);
|
||||||
void* fn_entry = std::bsearch(
|
void* fn_entry = std::bsearch(
|
||||||
&key, generated_code_map_.data(), generated_code_map_.size() + 1,
|
&key, generated_code_map_.data(), generated_code_map_.size() + 1,
|
||||||
sizeof(std::pair<uint32_t, FunctionInfo*>),
|
sizeof(std::pair<uint32_t, Function*>),
|
||||||
[](const void* key_ptr, const void* element_ptr) {
|
[](const void* key_ptr, const void* element_ptr) {
|
||||||
auto key = *reinterpret_cast<const uint32_t*>(key_ptr);
|
auto key = *reinterpret_cast<const uint32_t*>(key_ptr);
|
||||||
auto element =
|
auto element =
|
||||||
reinterpret_cast<const std::pair<uint64_t, FunctionInfo*>*>(
|
reinterpret_cast<const std::pair<uint64_t, GuestFunction*>*>(
|
||||||
element_ptr);
|
element_ptr);
|
||||||
if (key < (element->first >> 32)) {
|
if (key < (element->first >> 32)) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -239,7 +240,8 @@ FunctionInfo* X64CodeCache::LookupFunction(uint64_t host_pc) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (fn_entry) {
|
if (fn_entry) {
|
||||||
return reinterpret_cast<const std::pair<uint64_t, FunctionInfo*>*>(fn_entry)
|
return reinterpret_cast<const std::pair<uint64_t, GuestFunction*>*>(
|
||||||
|
fn_entry)
|
||||||
->second;
|
->second;
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -50,10 +50,10 @@ class X64CodeCache : public CodeCache {
|
||||||
size_t code_size, size_t stack_size);
|
size_t code_size, size_t stack_size);
|
||||||
void* PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
void* PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||||
size_t code_size, size_t stack_size,
|
size_t code_size, size_t stack_size,
|
||||||
FunctionInfo* function_info);
|
GuestFunction* function_info);
|
||||||
uint32_t PlaceData(const void* data, size_t length);
|
uint32_t PlaceData(const void* data, size_t length);
|
||||||
|
|
||||||
FunctionInfo* LookupFunction(uint64_t host_pc) override;
|
GuestFunction* LookupFunction(uint64_t host_pc) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// All executable code falls within 0x80000000 to 0x9FFFFFFF, so we can
|
// All executable code falls within 0x80000000 to 0x9FFFFFFF, so we can
|
||||||
|
@ -111,7 +111,7 @@ class X64CodeCache : public CodeCache {
|
||||||
// Sorted map by host PC base offsets to source function info.
|
// Sorted map by host PC base offsets to source function info.
|
||||||
// This can be used to bsearch on host PC to find the guest function.
|
// This can be used to bsearch on host PC to find the guest function.
|
||||||
// The key is [start address | end address].
|
// The key is [start address | end address].
|
||||||
std::vector<std::pair<uint64_t, FunctionInfo*>> generated_code_map_;
|
std::vector<std::pair<uint64_t, GuestFunction*>> generated_code_map_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace x64
|
} // namespace x64
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/base/memory.h"
|
#include "xenia/base/memory.h"
|
||||||
#include "xenia/base/platform_win.h"
|
#include "xenia/base/platform_win.h"
|
||||||
|
#include "xenia/cpu/function.h"
|
||||||
|
|
||||||
// When enabled, this will use Windows 8 APIs to get unwind info.
|
// When enabled, this will use Windows 8 APIs to get unwind info.
|
||||||
// TODO(benvanik): figure out why the callback variant doesn't work.
|
// TODO(benvanik): figure out why the callback variant doesn't work.
|
||||||
|
|
|
@ -27,8 +27,9 @@
|
||||||
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
|
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
|
||||||
#include "xenia/cpu/cpu_flags.h"
|
#include "xenia/cpu/cpu_flags.h"
|
||||||
#include "xenia/cpu/debug_info.h"
|
#include "xenia/cpu/debug_info.h"
|
||||||
|
#include "xenia/cpu/function.h"
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
#include "xenia/cpu/symbol_info.h"
|
#include "xenia/cpu/symbol.h"
|
||||||
#include "xenia/cpu/thread_state.h"
|
#include "xenia/cpu/thread_state.h"
|
||||||
#include "xenia/debug/debugger.h"
|
#include "xenia/debug/debugger.h"
|
||||||
#include "xenia/profiling.h"
|
#include "xenia/profiling.h"
|
||||||
|
@ -87,7 +88,7 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
|
||||||
|
|
||||||
X64Emitter::~X64Emitter() = default;
|
X64Emitter::~X64Emitter() = default;
|
||||||
|
|
||||||
bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
|
bool X64Emitter::Emit(GuestFunction* function, HIRBuilder* builder,
|
||||||
uint32_t debug_info_flags, DebugInfo* debug_info,
|
uint32_t debug_info_flags, DebugInfo* debug_info,
|
||||||
void*& out_code_address, size_t& out_code_size,
|
void*& out_code_address, size_t& out_code_size,
|
||||||
std::vector<SourceMapEntry>& out_source_map) {
|
std::vector<SourceMapEntry>& out_source_map) {
|
||||||
|
@ -96,6 +97,7 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
|
||||||
// Reset.
|
// Reset.
|
||||||
debug_info_ = debug_info;
|
debug_info_ = debug_info;
|
||||||
debug_info_flags_ = debug_info_flags;
|
debug_info_flags_ = debug_info_flags;
|
||||||
|
trace_data_ = &function->trace_data();
|
||||||
source_map_arena_.Reset();
|
source_map_arena_.Reset();
|
||||||
|
|
||||||
// Fill the generator with code.
|
// Fill the generator with code.
|
||||||
|
@ -106,7 +108,7 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
|
||||||
|
|
||||||
// Copy the final code to the cache and relocate it.
|
// Copy the final code to the cache and relocate it.
|
||||||
out_code_size = getSize();
|
out_code_size = getSize();
|
||||||
out_code_address = Emplace(stack_size, function_info);
|
out_code_address = Emplace(stack_size, function);
|
||||||
|
|
||||||
// Stash source map.
|
// Stash source map.
|
||||||
source_map_arena_.CloneContents(out_source_map);
|
source_map_arena_.CloneContents(out_source_map);
|
||||||
|
@ -114,16 +116,16 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* X64Emitter::Emplace(size_t stack_size, FunctionInfo* function_info) {
|
void* X64Emitter::Emplace(size_t stack_size, GuestFunction* function) {
|
||||||
// To avoid changing xbyak, we do a switcharoo here.
|
// To avoid changing xbyak, we do a switcharoo here.
|
||||||
// top_ points to the Xbyak buffer, and since we are in AutoGrow mode
|
// top_ points to the Xbyak buffer, and since we are in AutoGrow mode
|
||||||
// it has pending relocations. We copy the top_ to our buffer, swap the
|
// it has pending relocations. We copy the top_ to our buffer, swap the
|
||||||
// pointer, relocate, then return the original scratch pointer for use.
|
// pointer, relocate, then return the original scratch pointer for use.
|
||||||
uint8_t* old_address = top_;
|
uint8_t* old_address = top_;
|
||||||
void* new_address;
|
void* new_address;
|
||||||
if (function_info) {
|
if (function) {
|
||||||
new_address = code_cache_->PlaceGuestCode(function_info->address(), top_,
|
new_address = code_cache_->PlaceGuestCode(function->address(), top_, size_,
|
||||||
size_, stack_size, function_info);
|
stack_size, function);
|
||||||
} else {
|
} else {
|
||||||
new_address = code_cache_->PlaceHostCode(0, top_, size_, stack_size);
|
new_address = code_cache_->PlaceHostCode(0, top_, size_, stack_size);
|
||||||
}
|
}
|
||||||
|
@ -177,8 +179,8 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
||||||
// Safe now to do some tracing.
|
// Safe now to do some tracing.
|
||||||
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctions) {
|
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctions) {
|
||||||
// We require 32-bit addresses.
|
// We require 32-bit addresses.
|
||||||
assert_true(uint64_t(debug_info_->trace_data().header()) < UINT_MAX);
|
assert_true(uint64_t(trace_data_->header()) < UINT_MAX);
|
||||||
auto trace_header = debug_info_->trace_data().header();
|
auto trace_header = trace_data_->header();
|
||||||
|
|
||||||
// Call count.
|
// Call count.
|
||||||
lock();
|
lock();
|
||||||
|
@ -265,11 +267,10 @@ void X64Emitter::MarkSourceOffset(const Instr* i) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) {
|
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) {
|
||||||
auto trace_data = debug_info_->trace_data();
|
|
||||||
uint32_t instruction_index =
|
uint32_t instruction_index =
|
||||||
(entry->source_offset - trace_data.start_address()) / 4;
|
(entry->source_offset - trace_data_->start_address()) / 4;
|
||||||
lock();
|
lock();
|
||||||
inc(qword[low_address(trace_data.instruction_execute_counts() +
|
inc(qword[low_address(trace_data_->instruction_execute_counts() +
|
||||||
instruction_index * 8)]);
|
instruction_index * 8)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,8 +342,7 @@ extern "C" uint64_t ResolveFunction(void* raw_context,
|
||||||
// TODO(benvanik): required?
|
// TODO(benvanik): required?
|
||||||
assert_not_zero(target_address);
|
assert_not_zero(target_address);
|
||||||
|
|
||||||
Function* fn = NULL;
|
auto fn = thread_state->processor()->ResolveFunction(target_address);
|
||||||
thread_state->processor()->ResolveFunction(target_address, &fn);
|
|
||||||
assert_not_null(fn);
|
assert_not_null(fn);
|
||||||
auto x64_fn = static_cast<X64Function*>(fn);
|
auto x64_fn = static_cast<X64Function*>(fn);
|
||||||
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
|
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
|
||||||
|
@ -350,11 +350,11 @@ extern "C" uint64_t ResolveFunction(void* raw_context,
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void X64Emitter::Call(const hir::Instr* instr, FunctionInfo* symbol_info) {
|
void X64Emitter::Call(const hir::Instr* instr, GuestFunction* function) {
|
||||||
assert_not_null(symbol_info);
|
assert_not_null(function);
|
||||||
auto fn = reinterpret_cast<X64Function*>(symbol_info->function());
|
auto fn = static_cast<X64Function*>(function);
|
||||||
// Resolve address to the function to call and store in rax.
|
// Resolve address to the function to call and store in rax.
|
||||||
if (fn) {
|
if (fn->machine_code()) {
|
||||||
// TODO(benvanik): is it worth it to do this? It removes the need for
|
// TODO(benvanik): is it worth it to do this? It removes the need for
|
||||||
// a ResolveFunction call, but makes the table less useful.
|
// a ResolveFunction call, but makes the table less useful.
|
||||||
assert_zero(uint64_t(fn->machine_code()) & 0xFFFFFFFF00000000);
|
assert_zero(uint64_t(fn->machine_code()) & 0xFFFFFFFF00000000);
|
||||||
|
@ -363,7 +363,7 @@ void X64Emitter::Call(const hir::Instr* instr, FunctionInfo* symbol_info) {
|
||||||
// Load the pointer to the indirection table maintained in X64CodeCache.
|
// Load the pointer to the indirection table maintained in X64CodeCache.
|
||||||
// The target dword will either contain the address of the generated code
|
// The target dword will either contain the address of the generated code
|
||||||
// or a thunk to ResolveAddress.
|
// or a thunk to ResolveAddress.
|
||||||
mov(ebx, symbol_info->address());
|
mov(ebx, function->address());
|
||||||
mov(eax, dword[ebx]);
|
mov(eax, dword[ebx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,43 +418,50 @@ void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t UndefinedCallExtern(void* raw_context, uint64_t symbol_info_ptr) {
|
uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) {
|
||||||
auto symbol_info = reinterpret_cast<FunctionInfo*>(symbol_info_ptr);
|
auto function = reinterpret_cast<Function*>(function_ptr);
|
||||||
XELOGW("undefined extern call to %.8X %s", symbol_info->address(),
|
XELOGW("undefined extern call to %.8X %s", function->address(),
|
||||||
symbol_info->name().c_str());
|
function->name().c_str());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
void X64Emitter::CallExtern(const hir::Instr* instr,
|
void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
|
||||||
const FunctionInfo* symbol_info) {
|
bool undefined = true;
|
||||||
if (symbol_info->behavior() == FunctionBehavior::kBuiltin &&
|
if (function->behavior() == Function::Behavior::kBuiltin) {
|
||||||
symbol_info->builtin_handler()) {
|
auto builtin_function = static_cast<const BuiltinFunction*>(function);
|
||||||
// rcx = context
|
if (builtin_function->handler()) {
|
||||||
// rdx = target host function
|
undefined = false;
|
||||||
// r8 = arg0
|
// rcx = context
|
||||||
// r9 = arg1
|
// rdx = target host function
|
||||||
mov(rdx, reinterpret_cast<uint64_t>(symbol_info->builtin_handler()));
|
// r8 = arg0
|
||||||
mov(r8, reinterpret_cast<uint64_t>(symbol_info->builtin_arg0()));
|
// r9 = arg1
|
||||||
mov(r9, reinterpret_cast<uint64_t>(symbol_info->builtin_arg1()));
|
mov(rdx, reinterpret_cast<uint64_t>(builtin_function->handler()));
|
||||||
auto thunk = backend()->guest_to_host_thunk();
|
mov(r8, reinterpret_cast<uint64_t>(builtin_function->arg0()));
|
||||||
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
mov(r9, reinterpret_cast<uint64_t>(builtin_function->arg1()));
|
||||||
call(rax);
|
auto thunk = backend()->guest_to_host_thunk();
|
||||||
ReloadECX();
|
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
||||||
ReloadEDX();
|
call(rax);
|
||||||
// rax = host return
|
ReloadECX();
|
||||||
} else if (symbol_info->behavior() == FunctionBehavior::kExtern &&
|
ReloadEDX();
|
||||||
symbol_info->extern_handler()) {
|
// rax = host return
|
||||||
// rcx = context
|
}
|
||||||
// rdx = target host function
|
} else if (function->behavior() == Function::Behavior::kExtern) {
|
||||||
mov(rdx, reinterpret_cast<uint64_t>(symbol_info->extern_handler()));
|
auto extern_function = static_cast<const GuestFunction*>(function);
|
||||||
mov(r8, qword[rcx + offsetof(cpu::frontend::PPCContext, kernel_state)]);
|
if (extern_function->extern_handler()) {
|
||||||
auto thunk = backend()->guest_to_host_thunk();
|
undefined = false;
|
||||||
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
// rcx = context
|
||||||
call(rax);
|
// rdx = target host function
|
||||||
ReloadECX();
|
mov(rdx, reinterpret_cast<uint64_t>(extern_function->extern_handler()));
|
||||||
ReloadEDX();
|
mov(r8, qword[rcx + offsetof(cpu::frontend::PPCContext, kernel_state)]);
|
||||||
// rax = host return
|
auto thunk = backend()->guest_to_host_thunk();
|
||||||
} else {
|
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
||||||
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(symbol_info));
|
call(rax);
|
||||||
|
ReloadECX();
|
||||||
|
ReloadEDX();
|
||||||
|
// rax = host return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (undefined) {
|
||||||
|
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(function));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "xenia/cpu/hir/hir_builder.h"
|
#include "xenia/cpu/hir/hir_builder.h"
|
||||||
#include "xenia/cpu/hir/instr.h"
|
#include "xenia/cpu/hir/instr.h"
|
||||||
#include "xenia/cpu/hir/value.h"
|
#include "xenia/cpu/hir/value.h"
|
||||||
#include "xenia/cpu/symbol_info.h"
|
|
||||||
#include "xenia/debug/function_trace_data.h"
|
#include "xenia/debug/function_trace_data.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
|
|
||||||
|
@ -27,10 +26,7 @@
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
class DebugInfo;
|
|
||||||
class FunctionInfo;
|
|
||||||
class Processor;
|
class Processor;
|
||||||
class SymbolInfo;
|
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
@ -118,7 +114,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||||
Processor* processor() const { return processor_; }
|
Processor* processor() const { return processor_; }
|
||||||
X64Backend* backend() const { return backend_; }
|
X64Backend* backend() const { return backend_; }
|
||||||
|
|
||||||
bool Emit(FunctionInfo* function_info, hir::HIRBuilder* builder,
|
bool Emit(GuestFunction* function, hir::HIRBuilder* builder,
|
||||||
uint32_t debug_info_flags, DebugInfo* debug_info,
|
uint32_t debug_info_flags, DebugInfo* debug_info,
|
||||||
void*& out_code_address, size_t& out_code_size,
|
void*& out_code_address, size_t& out_code_size,
|
||||||
std::vector<SourceMapEntry>& out_source_map);
|
std::vector<SourceMapEntry>& out_source_map);
|
||||||
|
@ -163,9 +159,9 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||||
void Trap(uint16_t trap_type = 0);
|
void Trap(uint16_t trap_type = 0);
|
||||||
void UnimplementedInstr(const hir::Instr* i);
|
void UnimplementedInstr(const hir::Instr* i);
|
||||||
|
|
||||||
void Call(const hir::Instr* instr, FunctionInfo* symbol_info);
|
void Call(const hir::Instr* instr, GuestFunction* function);
|
||||||
void CallIndirect(const hir::Instr* instr, const Xbyak::Reg64& reg);
|
void CallIndirect(const hir::Instr* instr, const Xbyak::Reg64& reg);
|
||||||
void CallExtern(const hir::Instr* instr, const FunctionInfo* symbol_info);
|
void CallExtern(const hir::Instr* instr, const Function* function);
|
||||||
void CallNative(void* fn);
|
void CallNative(void* fn);
|
||||||
void CallNative(uint64_t (*fn)(void* raw_context));
|
void CallNative(uint64_t (*fn)(void* raw_context));
|
||||||
void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0));
|
void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0));
|
||||||
|
@ -199,7 +195,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||||
size_t stack_size() const { return stack_size_; }
|
size_t stack_size() const { return stack_size_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void* Emplace(size_t stack_size, FunctionInfo* function_info = nullptr);
|
void* Emplace(size_t stack_size, GuestFunction* function = nullptr);
|
||||||
bool Emit(hir::HIRBuilder* builder, size_t& out_stack_size);
|
bool Emit(hir::HIRBuilder* builder, size_t& out_stack_size);
|
||||||
void EmitGetCurrentThreadId();
|
void EmitGetCurrentThreadId();
|
||||||
void EmitTraceUserCallReturn();
|
void EmitTraceUserCallReturn();
|
||||||
|
@ -218,6 +214,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||||
|
|
||||||
DebugInfo* debug_info_ = nullptr;
|
DebugInfo* debug_info_ = nullptr;
|
||||||
uint32_t debug_info_flags_ = 0;
|
uint32_t debug_info_flags_ = 0;
|
||||||
|
debug::FunctionTraceData* trace_data_ = nullptr;
|
||||||
Arena source_map_arena_;
|
Arena source_map_arena_;
|
||||||
|
|
||||||
size_t stack_size_ = 0;
|
size_t stack_size_ = 0;
|
||||||
|
|
|
@ -18,8 +18,8 @@ namespace cpu {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
namespace x64 {
|
namespace x64 {
|
||||||
|
|
||||||
X64Function::X64Function(FunctionInfo* symbol_info)
|
X64Function::X64Function(Module* module, uint32_t address)
|
||||||
: Function(symbol_info), machine_code_(nullptr), machine_code_length_(0) {}
|
: GuestFunction(module, address) {}
|
||||||
|
|
||||||
X64Function::~X64Function() {
|
X64Function::~X64Function() {
|
||||||
// machine_code_ is freed by code cache.
|
// machine_code_ is freed by code cache.
|
||||||
|
@ -30,14 +30,6 @@ void X64Function::Setup(uint8_t* machine_code, size_t machine_code_length) {
|
||||||
machine_code_length_ = machine_code_length;
|
machine_code_length_ = machine_code_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool X64Function::AddBreakpointImpl(debug::Breakpoint* breakpoint) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool X64Function::RemoveBreakpointImpl(debug::Breakpoint* breakpoint) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool X64Function::CallImpl(ThreadState* thread_state, uint32_t return_address) {
|
bool X64Function::CallImpl(ThreadState* thread_state, uint32_t return_address) {
|
||||||
auto backend =
|
auto backend =
|
||||||
reinterpret_cast<X64Backend*>(thread_state->processor()->backend());
|
reinterpret_cast<X64Backend*>(thread_state->processor()->backend());
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#define XENIA_BACKEND_X64_X64_FUNCTION_H_
|
#define XENIA_BACKEND_X64_X64_FUNCTION_H_
|
||||||
|
|
||||||
#include "xenia/cpu/function.h"
|
#include "xenia/cpu/function.h"
|
||||||
#include "xenia/cpu/symbol_info.h"
|
|
||||||
#include "xenia/cpu/thread_state.h"
|
#include "xenia/cpu/thread_state.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -19,10 +18,10 @@ namespace cpu {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
namespace x64 {
|
namespace x64 {
|
||||||
|
|
||||||
class X64Function : public Function {
|
class X64Function : public GuestFunction {
|
||||||
public:
|
public:
|
||||||
X64Function(FunctionInfo* symbol_info);
|
X64Function(Module* module, uint32_t address);
|
||||||
virtual ~X64Function();
|
~X64Function() override;
|
||||||
|
|
||||||
uint8_t* machine_code() const override { return machine_code_; }
|
uint8_t* machine_code() const override { return machine_code_; }
|
||||||
size_t machine_code_length() const override { return machine_code_length_; }
|
size_t machine_code_length() const override { return machine_code_length_; }
|
||||||
|
@ -30,13 +29,11 @@ class X64Function : public Function {
|
||||||
void Setup(uint8_t* machine_code, size_t machine_code_length);
|
void Setup(uint8_t* machine_code, size_t machine_code_length);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool AddBreakpointImpl(debug::Breakpoint* breakpoint) override;
|
|
||||||
bool RemoveBreakpointImpl(debug::Breakpoint* breakpoint) override;
|
|
||||||
bool CallImpl(ThreadState* thread_state, uint32_t return_address) override;
|
bool CallImpl(ThreadState* thread_state, uint32_t return_address) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t* machine_code_;
|
uint8_t* machine_code_ = nullptr;
|
||||||
size_t machine_code_length_;
|
size_t machine_code_length_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace x64
|
} // namespace x64
|
||||||
|
|
|
@ -154,7 +154,7 @@ struct OffsetOp : Op<OffsetOp, KEY_TYPE_O> {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
|
struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
|
||||||
FunctionInfo* value;
|
Function* value;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <typename T, KeyType KEY_TYPE>
|
template <typename T, KeyType KEY_TYPE>
|
||||||
|
@ -162,7 +162,7 @@ struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
|
||||||
template <hir::Opcode OPCODE, typename... Ts>
|
template <hir::Opcode OPCODE, typename... Ts>
|
||||||
friend struct I;
|
friend struct I;
|
||||||
bool Load(const Instr::Op& op) {
|
bool Load(const Instr::Op& op) {
|
||||||
this->value = op.symbol_info;
|
this->value = op.symbol;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -856,7 +856,8 @@ EMITTER_OPCODE_TABLE(OPCODE_TRAP_TRUE, TRAP_TRUE_I8, TRAP_TRUE_I16,
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
struct CALL : Sequence<CALL, I<OPCODE_CALL, VoidOp, SymbolOp>> {
|
struct CALL : Sequence<CALL, I<OPCODE_CALL, VoidOp, SymbolOp>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
e.Call(i.instr, i.src1.value);
|
assert_true(i.src1.value->is_guest());
|
||||||
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src1.value));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
|
EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
|
||||||
|
@ -867,60 +868,66 @@ EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
|
||||||
struct CALL_TRUE_I8
|
struct CALL_TRUE_I8
|
||||||
: Sequence<CALL_TRUE_I8, I<OPCODE_CALL_TRUE, VoidOp, I8Op, SymbolOp>> {
|
: Sequence<CALL_TRUE_I8, I<OPCODE_CALL_TRUE, VoidOp, I8Op, SymbolOp>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
|
assert_true(i.src2.value->is_guest());
|
||||||
e.test(i.src1, i.src1);
|
e.test(i.src1, i.src1);
|
||||||
Xbyak::Label skip;
|
Xbyak::Label skip;
|
||||||
e.jz(skip);
|
e.jz(skip);
|
||||||
e.Call(i.instr, i.src2.value);
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||||
e.L(skip);
|
e.L(skip);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct CALL_TRUE_I16
|
struct CALL_TRUE_I16
|
||||||
: Sequence<CALL_TRUE_I16, I<OPCODE_CALL_TRUE, VoidOp, I16Op, SymbolOp>> {
|
: Sequence<CALL_TRUE_I16, I<OPCODE_CALL_TRUE, VoidOp, I16Op, SymbolOp>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
|
assert_true(i.src2.value->is_guest());
|
||||||
e.test(i.src1, i.src1);
|
e.test(i.src1, i.src1);
|
||||||
Xbyak::Label skip;
|
Xbyak::Label skip;
|
||||||
e.jz(skip);
|
e.jz(skip);
|
||||||
e.Call(i.instr, i.src2.value);
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||||
e.L(skip);
|
e.L(skip);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct CALL_TRUE_I32
|
struct CALL_TRUE_I32
|
||||||
: Sequence<CALL_TRUE_I32, I<OPCODE_CALL_TRUE, VoidOp, I32Op, SymbolOp>> {
|
: Sequence<CALL_TRUE_I32, I<OPCODE_CALL_TRUE, VoidOp, I32Op, SymbolOp>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
|
assert_true(i.src2.value->is_guest());
|
||||||
e.test(i.src1, i.src1);
|
e.test(i.src1, i.src1);
|
||||||
Xbyak::Label skip;
|
Xbyak::Label skip;
|
||||||
e.jz(skip);
|
e.jz(skip);
|
||||||
e.Call(i.instr, i.src2.value);
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||||
e.L(skip);
|
e.L(skip);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct CALL_TRUE_I64
|
struct CALL_TRUE_I64
|
||||||
: Sequence<CALL_TRUE_I64, I<OPCODE_CALL_TRUE, VoidOp, I64Op, SymbolOp>> {
|
: Sequence<CALL_TRUE_I64, I<OPCODE_CALL_TRUE, VoidOp, I64Op, SymbolOp>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
|
assert_true(i.src2.value->is_guest());
|
||||||
e.test(i.src1, i.src1);
|
e.test(i.src1, i.src1);
|
||||||
Xbyak::Label skip;
|
Xbyak::Label skip;
|
||||||
e.jz(skip);
|
e.jz(skip);
|
||||||
e.Call(i.instr, i.src2.value);
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||||
e.L(skip);
|
e.L(skip);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct CALL_TRUE_F32
|
struct CALL_TRUE_F32
|
||||||
: Sequence<CALL_TRUE_F32, I<OPCODE_CALL_TRUE, VoidOp, F32Op, SymbolOp>> {
|
: Sequence<CALL_TRUE_F32, I<OPCODE_CALL_TRUE, VoidOp, F32Op, SymbolOp>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
|
assert_true(i.src2.value->is_guest());
|
||||||
e.vptest(i.src1, i.src1);
|
e.vptest(i.src1, i.src1);
|
||||||
Xbyak::Label skip;
|
Xbyak::Label skip;
|
||||||
e.jz(skip);
|
e.jz(skip);
|
||||||
e.Call(i.instr, i.src2.value);
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||||
e.L(skip);
|
e.L(skip);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct CALL_TRUE_F64
|
struct CALL_TRUE_F64
|
||||||
: Sequence<CALL_TRUE_F64, I<OPCODE_CALL_TRUE, VoidOp, F64Op, SymbolOp>> {
|
: Sequence<CALL_TRUE_F64, I<OPCODE_CALL_TRUE, VoidOp, F64Op, SymbolOp>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
|
assert_true(i.src2.value->is_guest());
|
||||||
e.vptest(i.src1, i.src1);
|
e.vptest(i.src1, i.src1);
|
||||||
Xbyak::Label skip;
|
Xbyak::Label skip;
|
||||||
e.jz(skip);
|
e.jz(skip);
|
||||||
e.Call(i.instr, i.src2.value);
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||||
e.L(skip);
|
e.L(skip);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -91,9 +91,9 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||||
case OPCODE_CALL_TRUE:
|
case OPCODE_CALL_TRUE:
|
||||||
if (i->src1.value->IsConstant()) {
|
if (i->src1.value->IsConstant()) {
|
||||||
if (i->src1.value->IsConstantTrue()) {
|
if (i->src1.value->IsConstantTrue()) {
|
||||||
auto symbol_info = i->src2.symbol_info;
|
auto symbol = i->src2.symbol;
|
||||||
i->Replace(&OPCODE_CALL_info, i->flags);
|
i->Replace(&OPCODE_CALL_info, i->flags);
|
||||||
i->src1.symbol_info = symbol_info;
|
i->src1.symbol = symbol;
|
||||||
} else {
|
} else {
|
||||||
i->Remove();
|
i->Remove();
|
||||||
}
|
}
|
||||||
|
@ -101,13 +101,13 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||||
break;
|
break;
|
||||||
case OPCODE_CALL_INDIRECT:
|
case OPCODE_CALL_INDIRECT:
|
||||||
if (i->src1.value->IsConstant()) {
|
if (i->src1.value->IsConstant()) {
|
||||||
FunctionInfo* symbol_info;
|
auto function = processor_->LookupFunction(
|
||||||
if (!processor_->LookupFunctionInfo(
|
uint32_t(i->src1.value->constant.i32));
|
||||||
(uint32_t)i->src1.value->constant.i32, &symbol_info)) {
|
if (!function) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i->Replace(&OPCODE_CALL_info, i->flags);
|
i->Replace(&OPCODE_CALL_info, i->flags);
|
||||||
i->src1.symbol_info = symbol_info;
|
i->src1.symbol = function;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OPCODE_CALL_INDIRECT_TRUE:
|
case OPCODE_CALL_INDIRECT_TRUE:
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "xenia/cpu/debug_info.h"
|
#include "xenia/cpu/debug_info.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "xenia/debug/function_trace_data.h"
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
|
||||||
|
@ -37,6 +35,9 @@ enum DebugInfoFlags : uint32_t {
|
||||||
kDebugInfoAll = 0xFFFFFFFF,
|
kDebugInfoAll = 0xFFFFFFFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// DEPRECATED
|
||||||
|
// This will be getting removed or refactored to contain only on-demand
|
||||||
|
// disassembly data.
|
||||||
class DebugInfo {
|
class DebugInfo {
|
||||||
public:
|
public:
|
||||||
DebugInfo();
|
DebugInfo();
|
||||||
|
@ -53,8 +54,6 @@ class DebugInfo {
|
||||||
instruction_result_count_ = value;
|
instruction_result_count_ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug::FunctionTraceData& trace_data() { return trace_data_; }
|
|
||||||
|
|
||||||
const char* source_disasm() const { return source_disasm_; }
|
const char* source_disasm() const { return source_disasm_; }
|
||||||
void set_source_disasm(char* value) { source_disasm_ = value; }
|
void set_source_disasm(char* value) { source_disasm_ = value; }
|
||||||
const char* raw_hir_disasm() const { return raw_hir_disasm_; }
|
const char* raw_hir_disasm() const { return raw_hir_disasm_; }
|
||||||
|
@ -70,8 +69,6 @@ class DebugInfo {
|
||||||
uint32_t address_reference_count_;
|
uint32_t address_reference_count_;
|
||||||
uint32_t instruction_result_count_;
|
uint32_t instruction_result_count_;
|
||||||
|
|
||||||
debug::FunctionTraceData trace_data_;
|
|
||||||
|
|
||||||
char* source_disasm_;
|
char* source_disasm_;
|
||||||
char* raw_hir_disasm_;
|
char* raw_hir_disasm_;
|
||||||
char* hir_disasm_;
|
char* hir_disasm_;
|
||||||
|
|
|
@ -53,7 +53,7 @@ int InstrEmit_branch(PPCHIRBuilder& f, const char* src, uint64_t cia,
|
||||||
// recursion.
|
// recursion.
|
||||||
uint32_t nia_value = nia->AsUint64() & 0xFFFFFFFF;
|
uint32_t nia_value = nia->AsUint64() & 0xFFFFFFFF;
|
||||||
bool is_recursion = false;
|
bool is_recursion = false;
|
||||||
if (nia_value == f.symbol_info()->address() && lk) {
|
if (nia_value == f.function()->address() && lk) {
|
||||||
is_recursion = true;
|
is_recursion = true;
|
||||||
}
|
}
|
||||||
Label* label = is_recursion ? NULL : f.LookupLabel(nia_value);
|
Label* label = is_recursion ? NULL : f.LookupLabel(nia_value);
|
||||||
|
@ -71,14 +71,14 @@ int InstrEmit_branch(PPCHIRBuilder& f, const char* src, uint64_t cia,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Call function.
|
// Call function.
|
||||||
auto symbol_info = f.LookupFunction(nia_value);
|
auto function = f.LookupFunction(nia_value);
|
||||||
if (cond) {
|
if (cond) {
|
||||||
if (!expect_true) {
|
if (!expect_true) {
|
||||||
cond = f.IsFalse(cond);
|
cond = f.IsFalse(cond);
|
||||||
}
|
}
|
||||||
f.CallTrue(cond, symbol_info, call_flags);
|
f.CallTrue(cond, function, call_flags);
|
||||||
} else {
|
} else {
|
||||||
f.Call(symbol_info, call_flags);
|
f.Call(function, call_flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -419,7 +419,7 @@ XEEMITTER(mcrf, 0x4C000000, XL)(PPCHIRBuilder& f, InstrData& i) {
|
||||||
// System linkage (A-24)
|
// System linkage (A-24)
|
||||||
|
|
||||||
XEEMITTER(sc, 0x44000002, SC)(PPCHIRBuilder& f, InstrData& i) {
|
XEEMITTER(sc, 0x44000002, SC)(PPCHIRBuilder& f, InstrData& i) {
|
||||||
f.CallExtern(f.symbol_info());
|
f.CallExtern(f.function());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ bool PPCFrontend::Initialize() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PPCFrontend::DeclareFunction(FunctionInfo* symbol_info) {
|
bool PPCFrontend::DeclareFunction(GuestFunction* function) {
|
||||||
// Could scan or something here.
|
// Could scan or something here.
|
||||||
// Could also check to see if it's a well-known function type and classify
|
// Could also check to see if it's a well-known function type and classify
|
||||||
// for later.
|
// for later.
|
||||||
|
@ -95,12 +95,10 @@ bool PPCFrontend::DeclareFunction(FunctionInfo* symbol_info) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PPCFrontend::DefineFunction(FunctionInfo* symbol_info,
|
bool PPCFrontend::DefineFunction(GuestFunction* function,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags) {
|
||||||
Function** out_function) {
|
auto translator = translator_pool_.Allocate(this);
|
||||||
PPCTranslator* translator = translator_pool_.Allocate(this);
|
bool result = translator->Translate(function, debug_info_flags);
|
||||||
bool result =
|
|
||||||
translator->Translate(symbol_info, debug_info_flags, out_function);
|
|
||||||
translator_pool_.Release(translator);
|
translator_pool_.Release(translator);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "xenia/base/type_pool.h"
|
#include "xenia/base/type_pool.h"
|
||||||
#include "xenia/cpu/frontend/context_info.h"
|
#include "xenia/cpu/frontend/context_info.h"
|
||||||
#include "xenia/cpu/function.h"
|
#include "xenia/cpu/function.h"
|
||||||
#include "xenia/cpu/symbol_info.h"
|
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -35,8 +34,8 @@ class PPCTranslator;
|
||||||
struct PPCBuiltins {
|
struct PPCBuiltins {
|
||||||
xe::mutex global_lock;
|
xe::mutex global_lock;
|
||||||
bool global_lock_taken;
|
bool global_lock_taken;
|
||||||
FunctionInfo* check_global_lock;
|
Function* check_global_lock;
|
||||||
FunctionInfo* handle_global_lock;
|
Function* handle_global_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PPCFrontend {
|
class PPCFrontend {
|
||||||
|
@ -51,9 +50,8 @@ class PPCFrontend {
|
||||||
ContextInfo* context_info() const { return context_info_.get(); }
|
ContextInfo* context_info() const { return context_info_.get(); }
|
||||||
PPCBuiltins* builtins() { return &builtins_; }
|
PPCBuiltins* builtins() { return &builtins_; }
|
||||||
|
|
||||||
bool DeclareFunction(FunctionInfo* symbol_info);
|
bool DeclareFunction(GuestFunction* function);
|
||||||
bool DefineFunction(FunctionInfo* symbol_info, uint32_t debug_info_flags,
|
bool DefineFunction(GuestFunction* function, uint32_t debug_info_flags);
|
||||||
Function** out_function);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Processor* processor_;
|
Processor* processor_;
|
||||||
|
|
|
@ -40,27 +40,29 @@ PPCHIRBuilder::PPCHIRBuilder(PPCFrontend* frontend)
|
||||||
PPCHIRBuilder::~PPCHIRBuilder() = default;
|
PPCHIRBuilder::~PPCHIRBuilder() = default;
|
||||||
|
|
||||||
void PPCHIRBuilder::Reset() {
|
void PPCHIRBuilder::Reset() {
|
||||||
|
function_ = nullptr;
|
||||||
start_address_ = 0;
|
start_address_ = 0;
|
||||||
|
instr_count_ = 0;
|
||||||
instr_offset_list_ = NULL;
|
instr_offset_list_ = NULL;
|
||||||
label_list_ = NULL;
|
label_list_ = NULL;
|
||||||
with_debug_info_ = false;
|
with_debug_info_ = false;
|
||||||
HIRBuilder::Reset();
|
HIRBuilder::Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PPCHIRBuilder::Emit(FunctionInfo* symbol_info, uint32_t flags) {
|
bool PPCHIRBuilder::Emit(GuestFunction* function, uint32_t flags) {
|
||||||
SCOPE_profile_cpu_f("cpu");
|
SCOPE_profile_cpu_f("cpu");
|
||||||
|
|
||||||
Memory* memory = frontend_->memory();
|
Memory* memory = frontend_->memory();
|
||||||
|
|
||||||
symbol_info_ = symbol_info;
|
function_ = function;
|
||||||
start_address_ = symbol_info->address();
|
start_address_ = function_->address();
|
||||||
instr_count_ = (symbol_info->end_address() - symbol_info->address()) / 4 + 1;
|
instr_count_ = (function_->end_address() - function_->address()) / 4 + 1;
|
||||||
|
|
||||||
with_debug_info_ = (flags & EMIT_DEBUG_COMMENTS) == EMIT_DEBUG_COMMENTS;
|
with_debug_info_ = (flags & EMIT_DEBUG_COMMENTS) == EMIT_DEBUG_COMMENTS;
|
||||||
if (with_debug_info_) {
|
if (with_debug_info_) {
|
||||||
CommentFormat("%s fn %.8X-%.8X %s", symbol_info->module()->name().c_str(),
|
CommentFormat("%s fn %.8X-%.8X %s", function_->module()->name().c_str(),
|
||||||
symbol_info->address(), symbol_info->end_address(),
|
function_->address(), function_->end_address(),
|
||||||
symbol_info->name().c_str());
|
function_->name().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate offset list.
|
// Allocate offset list.
|
||||||
|
@ -78,8 +80,8 @@ bool PPCHIRBuilder::Emit(FunctionInfo* symbol_info, uint32_t flags) {
|
||||||
// Always mark entry with label.
|
// Always mark entry with label.
|
||||||
label_list_[0] = NewLabel();
|
label_list_[0] = NewLabel();
|
||||||
|
|
||||||
uint32_t start_address = symbol_info->address();
|
uint32_t start_address = function_->address();
|
||||||
uint32_t end_address = symbol_info->end_address();
|
uint32_t end_address = function_->end_address();
|
||||||
InstrData i;
|
InstrData i;
|
||||||
for (uint32_t address = start_address, offset = 0; address <= end_address;
|
for (uint32_t address = start_address, offset = 0; address <= end_address;
|
||||||
address += 4, offset++) {
|
address += 4, offset++) {
|
||||||
|
@ -165,13 +167,8 @@ void PPCHIRBuilder::AnnotateLabel(uint32_t address, Label* label) {
|
||||||
memcpy(label->name, name_buffer, sizeof(name_buffer));
|
memcpy(label->name, name_buffer, sizeof(name_buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionInfo* PPCHIRBuilder::LookupFunction(uint32_t address) {
|
Function* PPCHIRBuilder::LookupFunction(uint32_t address) {
|
||||||
Processor* processor = frontend_->processor();
|
return frontend_->processor()->LookupFunction(address);
|
||||||
FunctionInfo* symbol_info;
|
|
||||||
if (!processor->LookupFunctionInfo(address, &symbol_info)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return symbol_info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label* PPCHIRBuilder::LookupLabel(uint32_t address) {
|
Label* PPCHIRBuilder::LookupLabel(uint32_t address) {
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "xenia/base/string_buffer.h"
|
#include "xenia/base/string_buffer.h"
|
||||||
#include "xenia/cpu/hir/hir_builder.h"
|
#include "xenia/cpu/hir/hir_builder.h"
|
||||||
#include "xenia/cpu/function.h"
|
#include "xenia/cpu/function.h"
|
||||||
#include "xenia/cpu/symbol_info.h"
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
@ -28,18 +27,18 @@ class PPCHIRBuilder : public hir::HIRBuilder {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PPCHIRBuilder(PPCFrontend* frontend);
|
PPCHIRBuilder(PPCFrontend* frontend);
|
||||||
virtual ~PPCHIRBuilder();
|
~PPCHIRBuilder() override;
|
||||||
|
|
||||||
virtual void Reset();
|
void Reset() override;
|
||||||
|
|
||||||
enum EmitFlags {
|
enum EmitFlags {
|
||||||
// Emit comment nodes.
|
// Emit comment nodes.
|
||||||
EMIT_DEBUG_COMMENTS = 1 << 0,
|
EMIT_DEBUG_COMMENTS = 1 << 0,
|
||||||
};
|
};
|
||||||
bool Emit(FunctionInfo* symbol_info, uint32_t flags);
|
bool Emit(GuestFunction* function, uint32_t flags);
|
||||||
|
|
||||||
FunctionInfo* symbol_info() const { return symbol_info_; }
|
GuestFunction* function() const { return function_; }
|
||||||
FunctionInfo* LookupFunction(uint32_t address);
|
Function* LookupFunction(uint32_t address);
|
||||||
Label* LookupLabel(uint32_t address);
|
Label* LookupLabel(uint32_t address);
|
||||||
|
|
||||||
Value* LoadLR();
|
Value* LoadLR();
|
||||||
|
@ -83,7 +82,6 @@ class PPCHIRBuilder : public hir::HIRBuilder {
|
||||||
private:
|
private:
|
||||||
void AnnotateLabel(uint32_t address, Label* label);
|
void AnnotateLabel(uint32_t address, Label* label);
|
||||||
|
|
||||||
private:
|
|
||||||
PPCFrontend* frontend_;
|
PPCFrontend* frontend_;
|
||||||
|
|
||||||
// Reset whenever needed:
|
// Reset whenever needed:
|
||||||
|
@ -91,7 +89,7 @@ class PPCHIRBuilder : public hir::HIRBuilder {
|
||||||
|
|
||||||
// Reset each Emit:
|
// Reset each Emit:
|
||||||
bool with_debug_info_;
|
bool with_debug_info_;
|
||||||
FunctionInfo* symbol_info_;
|
GuestFunction* function_;
|
||||||
uint64_t start_address_;
|
uint64_t start_address_;
|
||||||
uint64_t instr_count_;
|
uint64_t instr_count_;
|
||||||
Instr** instr_offset_list_;
|
Instr** instr_offset_list_;
|
||||||
|
|
|
@ -35,11 +35,10 @@ PPCScanner::~PPCScanner() {}
|
||||||
|
|
||||||
bool PPCScanner::IsRestGprLr(uint32_t address) {
|
bool PPCScanner::IsRestGprLr(uint32_t address) {
|
||||||
auto function = frontend_->processor()->QueryFunction(address);
|
auto function = frontend_->processor()->QueryFunction(address);
|
||||||
return function &&
|
return function && function->behavior() == Function::Behavior::kEpilogReturn;
|
||||||
function->symbol_info()->behavior() == FunctionBehavior::kEpilogReturn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PPCScanner::Scan(FunctionInfo* symbol_info, DebugInfo* debug_info) {
|
bool PPCScanner::Scan(GuestFunction* function, DebugInfo* debug_info) {
|
||||||
// This is a simple basic block analyizer. It walks the start address to the
|
// This is a simple basic block analyizer. It walks the start address to the
|
||||||
// end address looking for branches. Each span of instructions between
|
// end address looking for branches. Each span of instructions between
|
||||||
// branches is considered a basic block. When the last blr (that has no
|
// branches is considered a basic block. When the last blr (that has no
|
||||||
|
@ -49,14 +48,14 @@ bool PPCScanner::Scan(FunctionInfo* symbol_info, DebugInfo* debug_info) {
|
||||||
|
|
||||||
Memory* memory = frontend_->memory();
|
Memory* memory = frontend_->memory();
|
||||||
|
|
||||||
LOGPPC("Analyzing function %.8X...", symbol_info->address());
|
LOGPPC("Analyzing function %.8X...", function->address());
|
||||||
|
|
||||||
// For debug info, only if needed.
|
// For debug info, only if needed.
|
||||||
uint32_t address_reference_count = 0;
|
uint32_t address_reference_count = 0;
|
||||||
uint32_t instruction_result_count = 0;
|
uint32_t instruction_result_count = 0;
|
||||||
|
|
||||||
uint32_t start_address = static_cast<uint32_t>(symbol_info->address());
|
uint32_t start_address = static_cast<uint32_t>(function->address());
|
||||||
uint32_t end_address = static_cast<uint32_t>(symbol_info->end_address());
|
uint32_t end_address = static_cast<uint32_t>(function->end_address());
|
||||||
uint32_t address = start_address;
|
uint32_t address = start_address;
|
||||||
uint32_t furthest_target = start_address;
|
uint32_t furthest_target = start_address;
|
||||||
size_t blocks_found = 0;
|
size_t blocks_found = 0;
|
||||||
|
@ -270,7 +269,7 @@ bool PPCScanner::Scan(FunctionInfo* symbol_info, DebugInfo* debug_info) {
|
||||||
LOGPPC("Function ran under: %.8X-%.8X ended at %.8X", start_address,
|
LOGPPC("Function ran under: %.8X-%.8X ended at %.8X", start_address,
|
||||||
end_address, address + 4);
|
end_address, address + 4);
|
||||||
}
|
}
|
||||||
symbol_info->set_end_address(address);
|
function->set_end_address(address);
|
||||||
|
|
||||||
// If there's spare bits at the end, split the function.
|
// If there's spare bits at the end, split the function.
|
||||||
// TODO(benvanik): splitting?
|
// TODO(benvanik): splitting?
|
||||||
|
@ -289,13 +288,13 @@ bool PPCScanner::Scan(FunctionInfo* symbol_info, DebugInfo* debug_info) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<BlockInfo> PPCScanner::FindBlocks(FunctionInfo* symbol_info) {
|
std::vector<BlockInfo> PPCScanner::FindBlocks(GuestFunction* function) {
|
||||||
Memory* memory = frontend_->memory();
|
Memory* memory = frontend_->memory();
|
||||||
|
|
||||||
std::map<uint32_t, BlockInfo> block_map;
|
std::map<uint32_t, BlockInfo> block_map;
|
||||||
|
|
||||||
uint32_t start_address = symbol_info->address();
|
uint32_t start_address = function->address();
|
||||||
uint32_t end_address = symbol_info->end_address();
|
uint32_t end_address = function->end_address();
|
||||||
bool in_block = false;
|
bool in_block = false;
|
||||||
uint32_t block_start = 0;
|
uint32_t block_start = 0;
|
||||||
InstrData i;
|
InstrData i;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/cpu/debug_info.h"
|
#include "xenia/cpu/debug_info.h"
|
||||||
#include "xenia/cpu/symbol_info.h"
|
#include "xenia/cpu/function.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
@ -21,25 +21,24 @@ namespace frontend {
|
||||||
|
|
||||||
class PPCFrontend;
|
class PPCFrontend;
|
||||||
|
|
||||||
typedef struct BlockInfo_t {
|
struct BlockInfo {
|
||||||
uint32_t start_address;
|
uint32_t start_address;
|
||||||
uint32_t end_address;
|
uint32_t end_address;
|
||||||
} BlockInfo;
|
};
|
||||||
|
|
||||||
class PPCScanner {
|
class PPCScanner {
|
||||||
public:
|
public:
|
||||||
PPCScanner(PPCFrontend* frontend);
|
PPCScanner(PPCFrontend* frontend);
|
||||||
~PPCScanner();
|
~PPCScanner();
|
||||||
|
|
||||||
bool Scan(FunctionInfo* symbol_info, DebugInfo* debug_info);
|
bool Scan(GuestFunction* function, DebugInfo* debug_info);
|
||||||
|
|
||||||
std::vector<BlockInfo> FindBlocks(FunctionInfo* symbol_info);
|
std::vector<BlockInfo> FindBlocks(GuestFunction* function);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsRestGprLr(uint32_t address);
|
bool IsRestGprLr(uint32_t address);
|
||||||
|
|
||||||
private:
|
PPCFrontend* frontend_ = nullptr;
|
||||||
PPCFrontend* frontend_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace frontend
|
} // namespace frontend
|
||||||
|
|
|
@ -99,9 +99,8 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) {
|
||||||
|
|
||||||
PPCTranslator::~PPCTranslator() = default;
|
PPCTranslator::~PPCTranslator() = default;
|
||||||
|
|
||||||
bool PPCTranslator::Translate(FunctionInfo* symbol_info,
|
bool PPCTranslator::Translate(GuestFunction* function,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags) {
|
||||||
Function** out_function) {
|
|
||||||
SCOPE_profile_cpu_f("cpu");
|
SCOPE_profile_cpu_f("cpu");
|
||||||
|
|
||||||
// Reset() all caching when we leave.
|
// Reset() all caching when we leave.
|
||||||
|
@ -137,7 +136,7 @@ bool PPCTranslator::Translate(FunctionInfo* symbol_info,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan the function to find its extents and gather debug data.
|
// Scan the function to find its extents and gather debug data.
|
||||||
if (!scanner_->Scan(symbol_info, debug_info.get())) {
|
if (!scanner_->Scan(function, debug_info.get())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,19 +152,19 @@ bool PPCTranslator::Translate(FunctionInfo* symbol_info,
|
||||||
if (debug_info_flags & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) {
|
if (debug_info_flags & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) {
|
||||||
// Additional space for instruction coverage counts.
|
// Additional space for instruction coverage counts.
|
||||||
trace_data_size += debug::FunctionTraceData::SizeOfInstructionCounts(
|
trace_data_size += debug::FunctionTraceData::SizeOfInstructionCounts(
|
||||||
symbol_info->address(), symbol_info->end_address());
|
function->address(), function->end_address());
|
||||||
}
|
}
|
||||||
uint8_t* trace_data = debugger->AllocateFunctionTraceData(trace_data_size);
|
uint8_t* trace_data = debugger->AllocateFunctionTraceData(trace_data_size);
|
||||||
if (trace_data) {
|
if (trace_data) {
|
||||||
debug_info->trace_data().Reset(trace_data, trace_data_size,
|
function->trace_data().Reset(trace_data, trace_data_size,
|
||||||
symbol_info->address(),
|
function->address(),
|
||||||
symbol_info->end_address());
|
function->end_address());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stash source.
|
// Stash source.
|
||||||
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmSource) {
|
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmSource) {
|
||||||
DumpSource(symbol_info, &string_buffer_);
|
DumpSource(function, &string_buffer_);
|
||||||
debug_info->set_source_disasm(string_buffer_.ToString());
|
debug_info->set_source_disasm(string_buffer_.ToString());
|
||||||
string_buffer_.Reset();
|
string_buffer_.Reset();
|
||||||
}
|
}
|
||||||
|
@ -179,7 +178,7 @@ bool PPCTranslator::Translate(FunctionInfo* symbol_info,
|
||||||
if (debug_info) {
|
if (debug_info) {
|
||||||
emit_flags |= PPCHIRBuilder::EMIT_DEBUG_COMMENTS;
|
emit_flags |= PPCHIRBuilder::EMIT_DEBUG_COMMENTS;
|
||||||
}
|
}
|
||||||
if (!builder_->Emit(symbol_info, emit_flags)) {
|
if (!builder_->Emit(function, emit_flags)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,27 +202,26 @@ bool PPCTranslator::Translate(FunctionInfo* symbol_info,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble to backend machine code.
|
// Assemble to backend machine code.
|
||||||
if (!assembler_->Assemble(symbol_info, builder_.get(), debug_info_flags,
|
if (!assembler_->Assemble(function, builder_.get(), debug_info_flags,
|
||||||
std::move(debug_info), out_function)) {
|
std::move(debug_info))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
void PPCTranslator::DumpSource(FunctionInfo* symbol_info,
|
void PPCTranslator::DumpSource(GuestFunction* function,
|
||||||
StringBuffer* string_buffer) {
|
StringBuffer* string_buffer) {
|
||||||
Memory* memory = frontend_->memory();
|
Memory* memory = frontend_->memory();
|
||||||
|
|
||||||
string_buffer->AppendFormat(
|
string_buffer->AppendFormat(
|
||||||
"%s fn %.8X-%.8X %s\n", symbol_info->module()->name().c_str(),
|
"%s fn %.8X-%.8X %s\n", function->module()->name().c_str(),
|
||||||
symbol_info->address(), symbol_info->end_address(),
|
function->address(), function->end_address(), function->name().c_str());
|
||||||
symbol_info->name().c_str());
|
|
||||||
|
|
||||||
auto blocks = scanner_->FindBlocks(symbol_info);
|
auto blocks = scanner_->FindBlocks(function);
|
||||||
|
|
||||||
uint32_t start_address = symbol_info->address();
|
uint32_t start_address = function->address();
|
||||||
uint32_t end_address = symbol_info->end_address();
|
uint32_t end_address = function->end_address();
|
||||||
InstrData i;
|
InstrData i;
|
||||||
auto block_it = blocks.begin();
|
auto block_it = blocks.begin();
|
||||||
for (uint32_t address = start_address, offset = 0; address <= end_address;
|
for (uint32_t address = start_address, offset = 0; address <= end_address;
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include "xenia/base/string_buffer.h"
|
#include "xenia/base/string_buffer.h"
|
||||||
#include "xenia/cpu/backend/assembler.h"
|
#include "xenia/cpu/backend/assembler.h"
|
||||||
#include "xenia/cpu/compiler/compiler.h"
|
#include "xenia/cpu/compiler/compiler.h"
|
||||||
#include "xenia/cpu/symbol_info.h"
|
#include "xenia/cpu/function.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
@ -30,13 +30,11 @@ class PPCTranslator {
|
||||||
PPCTranslator(PPCFrontend* frontend);
|
PPCTranslator(PPCFrontend* frontend);
|
||||||
~PPCTranslator();
|
~PPCTranslator();
|
||||||
|
|
||||||
bool Translate(FunctionInfo* symbol_info, uint32_t debug_info_flags,
|
bool Translate(GuestFunction* function, uint32_t debug_info_flags);
|
||||||
Function** out_function);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DumpSource(FunctionInfo* symbol_info, StringBuffer* string_buffer);
|
void DumpSource(GuestFunction* function, StringBuffer* string_buffer);
|
||||||
|
|
||||||
private:
|
|
||||||
PPCFrontend* frontend_;
|
PPCFrontend* frontend_;
|
||||||
std::unique_ptr<PPCScanner> scanner_;
|
std::unique_ptr<PPCScanner> scanner_;
|
||||||
std::unique_ptr<PPCHIRBuilder> builder_;
|
std::unique_ptr<PPCHIRBuilder> builder_;
|
||||||
|
|
|
@ -224,8 +224,8 @@ class TestRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute test.
|
// Execute test.
|
||||||
xe::cpu::Function* fn = nullptr;
|
auto fn = processor->ResolveFunction(test_case.address);
|
||||||
if (!processor->ResolveFunction(test_case.address, &fn)) {
|
if (!fn) {
|
||||||
XELOGE("Entry function not found");
|
XELOGE("Entry function not found");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -238,7 +238,9 @@ class TestRunner {
|
||||||
bool result = CheckTestResults(test_case);
|
bool result = CheckTestResults(test_case);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
// Also dump all disasm/etc.
|
// Also dump all disasm/etc.
|
||||||
fn->debug_info()->Dump();
|
if (fn->is_guest()) {
|
||||||
|
static_cast<xe::cpu::GuestFunction*>(fn)->debug_info()->Dump();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "xenia/cpu/function.h"
|
#include "xenia/cpu/function.h"
|
||||||
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/cpu/symbol_info.h"
|
#include "xenia/cpu/symbol.h"
|
||||||
#include "xenia/cpu/thread_state.h"
|
#include "xenia/cpu/thread_state.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -18,12 +18,56 @@ namespace cpu {
|
||||||
|
|
||||||
using xe::debug::Breakpoint;
|
using xe::debug::Breakpoint;
|
||||||
|
|
||||||
Function::Function(FunctionInfo* symbol_info)
|
Function::Function(Module* module, uint32_t address)
|
||||||
: address_(symbol_info->address()), symbol_info_(symbol_info) {}
|
: Symbol(Symbol::Type::kFunction, module, address) {}
|
||||||
|
|
||||||
Function::~Function() = default;
|
Function::~Function() = default;
|
||||||
|
|
||||||
const SourceMapEntry* Function::LookupSourceOffset(uint32_t offset) const {
|
BuiltinFunction::BuiltinFunction(Module* module, uint32_t address)
|
||||||
|
: Function(module, address) {
|
||||||
|
behavior_ = Behavior::kBuiltin;
|
||||||
|
}
|
||||||
|
|
||||||
|
BuiltinFunction::~BuiltinFunction() = default;
|
||||||
|
|
||||||
|
void BuiltinFunction::SetupBuiltin(Handler handler, void* arg0, void* arg1) {
|
||||||
|
behavior_ = Behavior::kBuiltin;
|
||||||
|
handler_ = handler;
|
||||||
|
arg0_ = arg0;
|
||||||
|
arg1_ = arg1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuiltinFunction::Call(ThreadState* thread_state, uint32_t return_address) {
|
||||||
|
// SCOPE_profile_cpu_f("cpu");
|
||||||
|
|
||||||
|
ThreadState* original_thread_state = ThreadState::Get();
|
||||||
|
if (original_thread_state != thread_state) {
|
||||||
|
ThreadState::Bind(thread_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_not_null(handler_);
|
||||||
|
handler_(thread_state->context(), arg0_, arg1_);
|
||||||
|
|
||||||
|
if (original_thread_state != thread_state) {
|
||||||
|
ThreadState::Bind(original_thread_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuestFunction::GuestFunction(Module* module, uint32_t address)
|
||||||
|
: Function(module, address) {
|
||||||
|
behavior_ = Behavior::kDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuestFunction::~GuestFunction() = default;
|
||||||
|
|
||||||
|
void GuestFunction::SetupExtern(ExternHandler handler) {
|
||||||
|
behavior_ = Behavior::kExtern;
|
||||||
|
extern_handler_ = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SourceMapEntry* GuestFunction::LookupSourceOffset(uint32_t offset) const {
|
||||||
// TODO(benvanik): binary search? We know the list is sorted by code order.
|
// TODO(benvanik): binary search? We know the list is sorted by code order.
|
||||||
for (size_t i = 0; i < source_map_.size(); ++i) {
|
for (size_t i = 0; i < source_map_.size(); ++i) {
|
||||||
const auto& entry = source_map_[i];
|
const auto& entry = source_map_[i];
|
||||||
|
@ -34,7 +78,7 @@ const SourceMapEntry* Function::LookupSourceOffset(uint32_t offset) const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SourceMapEntry* Function::LookupHIROffset(uint32_t offset) const {
|
const SourceMapEntry* GuestFunction::LookupHIROffset(uint32_t offset) const {
|
||||||
// TODO(benvanik): binary search? We know the list is sorted by code order.
|
// TODO(benvanik): binary search? We know the list is sorted by code order.
|
||||||
for (size_t i = 0; i < source_map_.size(); ++i) {
|
for (size_t i = 0; i < source_map_.size(); ++i) {
|
||||||
const auto& entry = source_map_[i];
|
const auto& entry = source_map_[i];
|
||||||
|
@ -45,7 +89,7 @@ const SourceMapEntry* Function::LookupHIROffset(uint32_t offset) const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SourceMapEntry* Function::LookupCodeOffset(uint32_t offset) const {
|
const SourceMapEntry* GuestFunction::LookupCodeOffset(uint32_t offset) const {
|
||||||
// TODO(benvanik): binary search? We know the list is sorted by code order.
|
// TODO(benvanik): binary search? We know the list is sorted by code order.
|
||||||
for (int64_t i = source_map_.size() - 1; i >= 0; --i) {
|
for (int64_t i = source_map_.size() - 1; i >= 0; --i) {
|
||||||
const auto& entry = source_map_[i];
|
const auto& entry = source_map_[i];
|
||||||
|
@ -56,49 +100,7 @@ const SourceMapEntry* Function::LookupCodeOffset(uint32_t offset) const {
|
||||||
return source_map_.empty() ? nullptr : &source_map_[0];
|
return source_map_.empty() ? nullptr : &source_map_[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Function::AddBreakpoint(Breakpoint* breakpoint) {
|
bool GuestFunction::Call(ThreadState* thread_state, uint32_t return_address) {
|
||||||
std::lock_guard<xe::mutex> guard(lock_);
|
|
||||||
bool found = false;
|
|
||||||
for (auto other : breakpoints_) {
|
|
||||||
if (other == breakpoint) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
breakpoints_.push_back(breakpoint);
|
|
||||||
return AddBreakpointImpl(breakpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Function::RemoveBreakpoint(Breakpoint* breakpoint) {
|
|
||||||
std::lock_guard<xe::mutex> guard(lock_);
|
|
||||||
for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) {
|
|
||||||
if (*it == breakpoint) {
|
|
||||||
if (!RemoveBreakpointImpl(breakpoint)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
breakpoints_.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Breakpoint* Function::FindBreakpoint(uint32_t address) {
|
|
||||||
std::lock_guard<xe::mutex> guard(lock_);
|
|
||||||
Breakpoint* result = nullptr;
|
|
||||||
for (auto breakpoint : breakpoints_) {
|
|
||||||
if (breakpoint->address() == address) {
|
|
||||||
result = breakpoint;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Function::Call(ThreadState* thread_state, uint32_t return_address) {
|
|
||||||
// SCOPE_profile_cpu_f("cpu");
|
// SCOPE_profile_cpu_f("cpu");
|
||||||
|
|
||||||
ThreadState* original_thread_state = ThreadState::Get();
|
ThreadState* original_thread_state = ThreadState::Get();
|
||||||
|
@ -106,29 +108,25 @@ bool Function::Call(ThreadState* thread_state, uint32_t return_address) {
|
||||||
ThreadState::Bind(thread_state);
|
ThreadState::Bind(thread_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool result = true;
|
bool result = false;
|
||||||
|
if (behavior_ == Behavior::kExtern) {
|
||||||
if (symbol_info_->behavior() == FunctionBehavior::kBuiltin) {
|
// Special handling for extern functions to speed things up (we don't
|
||||||
auto handler = symbol_info_->builtin_handler();
|
// trampoline into guest code only to trampoline back out).
|
||||||
assert_not_null(handler);
|
if (extern_handler_) {
|
||||||
handler(thread_state->context(), symbol_info_->builtin_arg0(),
|
extern_handler_(thread_state->context(),
|
||||||
symbol_info_->builtin_arg1());
|
thread_state->context()->kernel_state);
|
||||||
} else if (symbol_info_->behavior() == FunctionBehavior::kExtern) {
|
|
||||||
auto handler = symbol_info_->extern_handler();
|
|
||||||
if (handler) {
|
|
||||||
handler(thread_state->context(), thread_state->context()->kernel_state);
|
|
||||||
} else {
|
} else {
|
||||||
XELOGW("undefined extern call to %.8X %s", symbol_info_->address(),
|
XELOGW("undefined extern call to %.8X %s", address(), name().c_str());
|
||||||
symbol_info_->name().c_str());
|
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CallImpl(thread_state, return_address);
|
result = CallImpl(thread_state, return_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (original_thread_state != thread_state) {
|
if (original_thread_state != thread_state) {
|
||||||
ThreadState::Bind(original_thread_state);
|
ThreadState::Bind(original_thread_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,32 +11,89 @@
|
||||||
#define XENIA_CPU_FUNCTION_H_
|
#define XENIA_CPU_FUNCTION_H_
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/base/mutex.h"
|
#include "xenia/base/mutex.h"
|
||||||
#include "xenia/cpu/debug_info.h"
|
#include "xenia/cpu/debug_info.h"
|
||||||
|
#include "xenia/cpu/frontend/ppc_context.h"
|
||||||
|
#include "xenia/cpu/symbol.h"
|
||||||
#include "xenia/cpu/thread_state.h"
|
#include "xenia/cpu/thread_state.h"
|
||||||
#include "xenia/debug/breakpoint.h"
|
#include "xenia/debug/breakpoint.h"
|
||||||
|
#include "xenia/debug/function_trace_data.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
|
||||||
class FunctionInfo;
|
|
||||||
|
|
||||||
struct SourceMapEntry {
|
struct SourceMapEntry {
|
||||||
uint32_t source_offset; // Original source address/offset.
|
uint32_t source_offset; // Original source address/offset.
|
||||||
uint32_t hir_offset; // Block ordinal (16b) | Instr ordinal (16b)
|
uint32_t hir_offset; // Block ordinal (16b) | Instr ordinal (16b)
|
||||||
uint32_t code_offset; // Offset from emitted code start.
|
uint32_t code_offset; // Offset from emitted code start.
|
||||||
};
|
};
|
||||||
|
|
||||||
class Function {
|
class Function : public Symbol {
|
||||||
public:
|
public:
|
||||||
Function(FunctionInfo* symbol_info);
|
enum class Behavior {
|
||||||
virtual ~Function();
|
kDefault = 0,
|
||||||
|
kProlog,
|
||||||
|
kEpilog,
|
||||||
|
kEpilogReturn,
|
||||||
|
kBuiltin,
|
||||||
|
kExtern,
|
||||||
|
};
|
||||||
|
|
||||||
|
~Function() override;
|
||||||
|
|
||||||
uint32_t address() const { return address_; }
|
uint32_t address() const { return address_; }
|
||||||
FunctionInfo* symbol_info() const { return symbol_info_; }
|
bool has_end_address() const { return end_address_ > 0; }
|
||||||
|
uint32_t end_address() const { return end_address_; }
|
||||||
|
void set_end_address(uint32_t value) { end_address_ = value; }
|
||||||
|
Behavior behavior() const { return behavior_; }
|
||||||
|
void set_behavior(Behavior value) { behavior_ = value; }
|
||||||
|
bool is_guest() const { return behavior_ != Behavior::kBuiltin; }
|
||||||
|
|
||||||
|
virtual bool Call(ThreadState* thread_state, uint32_t return_address) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Function(Module* module, uint32_t address);
|
||||||
|
|
||||||
|
uint32_t end_address_ = 0;
|
||||||
|
Behavior behavior_ = Behavior::kDefault;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BuiltinFunction : public Function {
|
||||||
|
public:
|
||||||
|
typedef void (*Handler)(frontend::PPCContext* ppc_context, void* arg0,
|
||||||
|
void* arg1);
|
||||||
|
|
||||||
|
BuiltinFunction(Module* module, uint32_t address);
|
||||||
|
~BuiltinFunction() override;
|
||||||
|
|
||||||
|
void SetupBuiltin(Handler handler, void* arg0, void* arg1);
|
||||||
|
|
||||||
|
Handler handler() const { return handler_; }
|
||||||
|
void* arg0() const { return arg0_; }
|
||||||
|
void* arg1() const { return arg1_; }
|
||||||
|
|
||||||
|
bool Call(ThreadState* thread_state, uint32_t return_address) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Handler handler_ = nullptr;
|
||||||
|
void* arg0_ = nullptr;
|
||||||
|
void* arg1_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GuestFunction : public Function {
|
||||||
|
public:
|
||||||
|
typedef void (*ExternHandler)(frontend::PPCContext* ppc_context,
|
||||||
|
kernel::KernelState* kernel_state);
|
||||||
|
|
||||||
|
GuestFunction(Module* module, uint32_t address);
|
||||||
|
~GuestFunction() override;
|
||||||
|
|
||||||
|
uint32_t address() const { return address_; }
|
||||||
|
bool has_end_address() const { return end_address_ > 0; }
|
||||||
|
uint32_t end_address() const { return end_address_; }
|
||||||
|
void set_end_address(uint32_t value) { end_address_ = value; }
|
||||||
|
|
||||||
virtual uint8_t* machine_code() const = 0;
|
virtual uint8_t* machine_code() const = 0;
|
||||||
virtual size_t machine_code_length() const = 0;
|
virtual size_t machine_code_length() const = 0;
|
||||||
|
@ -45,32 +102,26 @@ class Function {
|
||||||
void set_debug_info(std::unique_ptr<DebugInfo> debug_info) {
|
void set_debug_info(std::unique_ptr<DebugInfo> debug_info) {
|
||||||
debug_info_ = std::move(debug_info);
|
debug_info_ = std::move(debug_info);
|
||||||
}
|
}
|
||||||
|
debug::FunctionTraceData& trace_data() { return trace_data_; }
|
||||||
std::vector<SourceMapEntry>& source_map() { return source_map_; }
|
std::vector<SourceMapEntry>& source_map() { return source_map_; }
|
||||||
|
|
||||||
|
ExternHandler extern_handler() const { return extern_handler_; }
|
||||||
|
void SetupExtern(ExternHandler handler);
|
||||||
|
|
||||||
const SourceMapEntry* LookupSourceOffset(uint32_t offset) const;
|
const SourceMapEntry* LookupSourceOffset(uint32_t offset) const;
|
||||||
const SourceMapEntry* LookupHIROffset(uint32_t offset) const;
|
const SourceMapEntry* LookupHIROffset(uint32_t offset) const;
|
||||||
const SourceMapEntry* LookupCodeOffset(uint32_t offset) const;
|
const SourceMapEntry* LookupCodeOffset(uint32_t offset) const;
|
||||||
|
|
||||||
bool AddBreakpoint(debug::Breakpoint* breakpoint);
|
bool Call(ThreadState* thread_state, uint32_t return_address) override;
|
||||||
bool RemoveBreakpoint(debug::Breakpoint* breakpoint);
|
|
||||||
|
|
||||||
bool Call(ThreadState* thread_state, uint32_t return_address);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
debug::Breakpoint* FindBreakpoint(uint32_t address);
|
|
||||||
virtual bool AddBreakpointImpl(debug::Breakpoint* breakpoint) { return 0; }
|
|
||||||
virtual bool RemoveBreakpointImpl(debug::Breakpoint* breakpoint) { return 0; }
|
|
||||||
virtual bool CallImpl(ThreadState* thread_state, uint32_t return_address) = 0;
|
virtual bool CallImpl(ThreadState* thread_state, uint32_t return_address) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t address_;
|
|
||||||
FunctionInfo* symbol_info_;
|
|
||||||
std::unique_ptr<DebugInfo> debug_info_;
|
std::unique_ptr<DebugInfo> debug_info_;
|
||||||
|
debug::FunctionTraceData trace_data_;
|
||||||
std::vector<SourceMapEntry> source_map_;
|
std::vector<SourceMapEntry> source_map_;
|
||||||
|
ExternHandler extern_handler_ = nullptr;
|
||||||
// TODO(benvanik): move elsewhere? DebugData?
|
|
||||||
xe::mutex lock_;
|
|
||||||
std::vector<debug::Breakpoint*> breakpoints_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
|
|
|
@ -13,10 +13,11 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
|
#include "xenia/cpu/function.h"
|
||||||
#include "xenia/cpu/hir/block.h"
|
#include "xenia/cpu/hir/block.h"
|
||||||
#include "xenia/cpu/hir/instr.h"
|
#include "xenia/cpu/hir/instr.h"
|
||||||
#include "xenia/cpu/hir/label.h"
|
#include "xenia/cpu/hir/label.h"
|
||||||
#include "xenia/cpu/symbol_info.h"
|
#include "xenia/cpu/symbol.h"
|
||||||
#include "xenia/profiling.h"
|
#include "xenia/profiling.h"
|
||||||
|
|
||||||
// Will scribble arena memory to hopefully find use before clears.
|
// Will scribble arena memory to hopefully find use before clears.
|
||||||
|
@ -162,7 +163,7 @@ void HIRBuilder::DumpOp(StringBuffer* str, OpcodeSignatureType sig_type,
|
||||||
break;
|
break;
|
||||||
case OPCODE_SIG_TYPE_S:
|
case OPCODE_SIG_TYPE_S:
|
||||||
if (true) {
|
if (true) {
|
||||||
auto target = op->symbol_info;
|
auto target = op->symbol;
|
||||||
str->Append(!target->name().empty() ? target->name() : "<fn>");
|
str->Append(!target->name().empty() ? target->name() : "<fn>");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -820,25 +821,24 @@ void HIRBuilder::TrapTrue(Value* cond, uint16_t trap_code) {
|
||||||
EndBlock();
|
EndBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HIRBuilder::Call(FunctionInfo* symbol_info, uint16_t call_flags) {
|
void HIRBuilder::Call(Function* symbol, uint16_t call_flags) {
|
||||||
Instr* i = AppendInstr(OPCODE_CALL_info, call_flags);
|
Instr* i = AppendInstr(OPCODE_CALL_info, call_flags);
|
||||||
i->src1.symbol_info = symbol_info;
|
i->src1.symbol = symbol;
|
||||||
i->src2.value = i->src3.value = NULL;
|
i->src2.value = i->src3.value = NULL;
|
||||||
EndBlock();
|
EndBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HIRBuilder::CallTrue(Value* cond, FunctionInfo* symbol_info,
|
void HIRBuilder::CallTrue(Value* cond, Function* symbol, uint16_t call_flags) {
|
||||||
uint16_t call_flags) {
|
|
||||||
if (cond->IsConstant()) {
|
if (cond->IsConstant()) {
|
||||||
if (cond->IsConstantTrue()) {
|
if (cond->IsConstantTrue()) {
|
||||||
Call(symbol_info, call_flags);
|
Call(symbol, call_flags);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Instr* i = AppendInstr(OPCODE_CALL_TRUE_info, call_flags);
|
Instr* i = AppendInstr(OPCODE_CALL_TRUE_info, call_flags);
|
||||||
i->set_src1(cond);
|
i->set_src1(cond);
|
||||||
i->src2.symbol_info = symbol_info;
|
i->src2.symbol = symbol;
|
||||||
i->src3.value = NULL;
|
i->src3.value = NULL;
|
||||||
EndBlock();
|
EndBlock();
|
||||||
}
|
}
|
||||||
|
@ -868,9 +868,9 @@ void HIRBuilder::CallIndirectTrue(Value* cond, Value* value,
|
||||||
EndBlock();
|
EndBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HIRBuilder::CallExtern(FunctionInfo* symbol_info) {
|
void HIRBuilder::CallExtern(Function* symbol) {
|
||||||
Instr* i = AppendInstr(OPCODE_CALL_EXTERN_info, 0);
|
Instr* i = AppendInstr(OPCODE_CALL_EXTERN_info, 0);
|
||||||
i->src1.symbol_info = symbol_info;
|
i->src1.symbol = symbol;
|
||||||
i->src2.value = i->src3.value = NULL;
|
i->src2.value = i->src3.value = NULL;
|
||||||
EndBlock();
|
EndBlock();
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,12 +83,11 @@ class HIRBuilder {
|
||||||
void Trap(uint16_t trap_code = 0);
|
void Trap(uint16_t trap_code = 0);
|
||||||
void TrapTrue(Value* cond, uint16_t trap_code = 0);
|
void TrapTrue(Value* cond, uint16_t trap_code = 0);
|
||||||
|
|
||||||
void Call(FunctionInfo* symbol_info, uint16_t call_flags = 0);
|
void Call(Function* symbol, uint16_t call_flags = 0);
|
||||||
void CallTrue(Value* cond, FunctionInfo* symbol_info,
|
void CallTrue(Value* cond, Function* symbol, uint16_t call_flags = 0);
|
||||||
uint16_t call_flags = 0);
|
|
||||||
void CallIndirect(Value* value, uint16_t call_flags = 0);
|
void CallIndirect(Value* value, uint16_t call_flags = 0);
|
||||||
void CallIndirectTrue(Value* cond, Value* value, uint16_t call_flags = 0);
|
void CallIndirectTrue(Value* cond, Value* value, uint16_t call_flags = 0);
|
||||||
void CallExtern(FunctionInfo* symbol_info);
|
void CallExtern(Function* symbol);
|
||||||
void Return();
|
void Return();
|
||||||
void ReturnTrue(Value* cond);
|
void ReturnTrue(Value* cond);
|
||||||
void SetReturnAddress(Value* value);
|
void SetReturnAddress(Value* value);
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
class FunctionInfo;
|
class Function;
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class Instr {
|
||||||
uint32_t ordinal;
|
uint32_t ordinal;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
FunctionInfo* symbol_info;
|
Function* symbol;
|
||||||
Label* label;
|
Label* label;
|
||||||
Value* value;
|
Value* value;
|
||||||
uint64_t offset;
|
uint64_t offset;
|
||||||
|
|
|
@ -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
|
|
|
@ -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_
|
|
|
@ -26,12 +26,12 @@ Module::~Module() = default;
|
||||||
|
|
||||||
bool Module::ContainsAddress(uint32_t address) { return true; }
|
bool Module::ContainsAddress(uint32_t address) { return true; }
|
||||||
|
|
||||||
SymbolInfo* Module::LookupSymbol(uint32_t address, bool wait) {
|
Symbol* Module::LookupSymbol(uint32_t address, bool wait) {
|
||||||
lock_.lock();
|
lock_.lock();
|
||||||
const auto it = map_.find(address);
|
const auto it = map_.find(address);
|
||||||
SymbolInfo* symbol_info = it != map_.end() ? it->second : nullptr;
|
Symbol* symbol = it != map_.end() ? it->second : nullptr;
|
||||||
if (symbol_info) {
|
if (symbol) {
|
||||||
if (symbol_info->status() == SymbolStatus::kDeclaring) {
|
if (symbol->status() == Symbol::Status::kDeclaring) {
|
||||||
// Some other thread is declaring the symbol - wait.
|
// Some other thread is declaring the symbol - wait.
|
||||||
if (wait) {
|
if (wait) {
|
||||||
do {
|
do {
|
||||||
|
@ -39,133 +39,130 @@ SymbolInfo* Module::LookupSymbol(uint32_t address, bool wait) {
|
||||||
// TODO(benvanik): sleep for less time?
|
// TODO(benvanik): sleep for less time?
|
||||||
xe::threading::Sleep(std::chrono::microseconds(100));
|
xe::threading::Sleep(std::chrono::microseconds(100));
|
||||||
lock_.lock();
|
lock_.lock();
|
||||||
} while (symbol_info->status() == SymbolStatus::kDeclaring);
|
} while (symbol->status() == Symbol::Status::kDeclaring);
|
||||||
} else {
|
} else {
|
||||||
// Immediate request, just return.
|
// Immediate request, just return.
|
||||||
symbol_info = nullptr;
|
symbol = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lock_.unlock();
|
lock_.unlock();
|
||||||
return symbol_info;
|
return symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolStatus Module::DeclareSymbol(SymbolType type, uint32_t address,
|
Symbol::Status Module::DeclareSymbol(Symbol::Type type, uint32_t address,
|
||||||
SymbolInfo** out_symbol_info) {
|
Symbol** out_symbol) {
|
||||||
*out_symbol_info = nullptr;
|
*out_symbol = nullptr;
|
||||||
lock_.lock();
|
lock_.lock();
|
||||||
auto it = map_.find(address);
|
auto it = map_.find(address);
|
||||||
SymbolInfo* symbol_info = it != map_.end() ? it->second : nullptr;
|
Symbol* symbol = it != map_.end() ? it->second : nullptr;
|
||||||
SymbolStatus status;
|
Symbol::Status status;
|
||||||
if (symbol_info) {
|
if (symbol) {
|
||||||
// If we exist but are the wrong type, die.
|
// If we exist but are the wrong type, die.
|
||||||
if (symbol_info->type() != type) {
|
if (symbol->type() != type) {
|
||||||
lock_.unlock();
|
lock_.unlock();
|
||||||
return SymbolStatus::kFailed;
|
return Symbol::Status::kFailed;
|
||||||
}
|
}
|
||||||
// If we aren't ready yet spin and wait.
|
// If we aren't ready yet spin and wait.
|
||||||
if (symbol_info->status() == SymbolStatus::kDeclaring) {
|
if (symbol->status() == Symbol::Status::kDeclaring) {
|
||||||
// Still declaring, so spin.
|
// Still declaring, so spin.
|
||||||
do {
|
do {
|
||||||
lock_.unlock();
|
lock_.unlock();
|
||||||
// TODO(benvanik): sleep for less time?
|
// TODO(benvanik): sleep for less time?
|
||||||
xe::threading::Sleep(std::chrono::microseconds(100));
|
xe::threading::Sleep(std::chrono::microseconds(100));
|
||||||
lock_.lock();
|
lock_.lock();
|
||||||
} while (symbol_info->status() == SymbolStatus::kDeclaring);
|
} while (symbol->status() == Symbol::Status::kDeclaring);
|
||||||
}
|
}
|
||||||
status = symbol_info->status();
|
status = symbol->status();
|
||||||
} else {
|
} else {
|
||||||
// Create and return for initialization.
|
// Create and return for initialization.
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SymbolType::kFunction:
|
case Symbol::Type::kFunction:
|
||||||
symbol_info = new FunctionInfo(this, address);
|
symbol = CreateFunction(address).release();
|
||||||
break;
|
break;
|
||||||
case SymbolType::kVariable:
|
case Symbol::Type::kVariable:
|
||||||
symbol_info = new VariableInfo(this, address);
|
symbol = new Symbol(Symbol::Type::kVariable, this, address);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
map_[address] = symbol_info;
|
map_[address] = symbol;
|
||||||
list_.emplace_back(symbol_info);
|
list_.emplace_back(symbol);
|
||||||
status = SymbolStatus::kNew;
|
status = Symbol::Status::kNew;
|
||||||
}
|
}
|
||||||
lock_.unlock();
|
lock_.unlock();
|
||||||
*out_symbol_info = symbol_info;
|
*out_symbol = symbol;
|
||||||
|
|
||||||
// Get debug info from providers, if this is new.
|
// Get debug info from providers, if this is new.
|
||||||
if (status == SymbolStatus::kNew) {
|
if (status == Symbol::Status::kNew) {
|
||||||
// TODO(benvanik): lookup in map data/dwarf/etc?
|
// TODO(benvanik): lookup in map data/dwarf/etc?
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolStatus Module::DeclareFunction(uint32_t address,
|
Symbol::Status Module::DeclareFunction(uint32_t address,
|
||||||
FunctionInfo** out_symbol_info) {
|
Function** out_function) {
|
||||||
SymbolInfo* symbol_info;
|
Symbol* symbol;
|
||||||
SymbolStatus status =
|
Symbol::Status status =
|
||||||
DeclareSymbol(SymbolType::kFunction, address, &symbol_info);
|
DeclareSymbol(Symbol::Type::kFunction, address, &symbol);
|
||||||
*out_symbol_info = (FunctionInfo*)symbol_info;
|
*out_function = static_cast<Function*>(symbol);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolStatus Module::DeclareVariable(uint32_t address,
|
Symbol::Status Module::DeclareVariable(uint32_t address, Symbol** out_symbol) {
|
||||||
VariableInfo** out_symbol_info) {
|
Symbol::Status status =
|
||||||
SymbolInfo* symbol_info;
|
DeclareSymbol(Symbol::Type::kVariable, address, out_symbol);
|
||||||
SymbolStatus status =
|
|
||||||
DeclareSymbol(SymbolType::kVariable, address, &symbol_info);
|
|
||||||
*out_symbol_info = (VariableInfo*)symbol_info;
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolStatus Module::DefineSymbol(SymbolInfo* symbol_info) {
|
Symbol::Status Module::DefineSymbol(Symbol* symbol) {
|
||||||
lock_.lock();
|
lock_.lock();
|
||||||
SymbolStatus status;
|
Symbol::Status status;
|
||||||
if (symbol_info->status() == SymbolStatus::kDeclared) {
|
if (symbol->status() == Symbol::Status::kDeclared) {
|
||||||
// Declared but undefined, so request caller define it.
|
// Declared but undefined, so request caller define it.
|
||||||
symbol_info->set_status(SymbolStatus::kDefining);
|
symbol->set_status(Symbol::Status::kDefining);
|
||||||
status = SymbolStatus::kNew;
|
status = Symbol::Status::kNew;
|
||||||
} else if (symbol_info->status() == SymbolStatus::kDefining) {
|
} else if (symbol->status() == Symbol::Status::kDefining) {
|
||||||
// Still defining, so spin.
|
// Still defining, so spin.
|
||||||
do {
|
do {
|
||||||
lock_.unlock();
|
lock_.unlock();
|
||||||
// TODO(benvanik): sleep for less time?
|
// TODO(benvanik): sleep for less time?
|
||||||
xe::threading::Sleep(std::chrono::microseconds(100));
|
xe::threading::Sleep(std::chrono::microseconds(100));
|
||||||
lock_.lock();
|
lock_.lock();
|
||||||
} while (symbol_info->status() == SymbolStatus::kDefining);
|
} while (symbol->status() == Symbol::Status::kDefining);
|
||||||
status = symbol_info->status();
|
status = symbol->status();
|
||||||
} else {
|
} else {
|
||||||
status = symbol_info->status();
|
status = symbol->status();
|
||||||
}
|
}
|
||||||
lock_.unlock();
|
lock_.unlock();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolStatus Module::DefineFunction(FunctionInfo* symbol_info) {
|
Symbol::Status Module::DefineFunction(Function* symbol) {
|
||||||
return DefineSymbol((SymbolInfo*)symbol_info);
|
return DefineSymbol((Symbol*)symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolStatus Module::DefineVariable(VariableInfo* symbol_info) {
|
Symbol::Status Module::DefineVariable(Symbol* symbol) {
|
||||||
return DefineSymbol((SymbolInfo*)symbol_info);
|
return DefineSymbol((Symbol*)symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::ForEachFunction(std::function<void(FunctionInfo*)> callback) {
|
void Module::ForEachFunction(std::function<void(Function*)> callback) {
|
||||||
std::lock_guard<xe::mutex> guard(lock_);
|
std::lock_guard<xe::mutex> guard(lock_);
|
||||||
for (auto& symbol_info : list_) {
|
for (auto& symbol : list_) {
|
||||||
if (symbol_info->type() == SymbolType::kFunction) {
|
if (symbol->type() == Symbol::Type::kFunction) {
|
||||||
FunctionInfo* info = static_cast<FunctionInfo*>(symbol_info.get());
|
Function* info = static_cast<Function*>(symbol.get());
|
||||||
callback(info);
|
callback(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::ForEachSymbol(size_t start_index, size_t end_index,
|
void Module::ForEachSymbol(size_t start_index, size_t end_index,
|
||||||
std::function<void(SymbolInfo*)> callback) {
|
std::function<void(Symbol*)> callback) {
|
||||||
std::lock_guard<xe::mutex> guard(lock_);
|
std::lock_guard<xe::mutex> guard(lock_);
|
||||||
start_index = std::min(start_index, list_.size());
|
start_index = std::min(start_index, list_.size());
|
||||||
end_index = std::min(end_index, list_.size());
|
end_index = std::min(end_index, list_.size());
|
||||||
for (size_t i = start_index; i <= end_index; ++i) {
|
for (size_t i = start_index; i <= end_index; ++i) {
|
||||||
auto& symbol_info = list_[i];
|
auto& symbol = list_[i];
|
||||||
callback(symbol_info.get());
|
callback(symbol.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,19 +220,19 @@ bool Module::ReadMap(const char* file_name) {
|
||||||
|
|
||||||
if (type_str == "f") {
|
if (type_str == "f") {
|
||||||
// Function.
|
// Function.
|
||||||
FunctionInfo* fn_info;
|
auto function = processor_->LookupFunction(this, address);
|
||||||
if (!processor_->LookupFunctionInfo(this, address, &fn_info)) {
|
if (!function) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Don't overwrite names we've set elsewhere.
|
// Don't overwrite names we've set elsewhere.
|
||||||
if (fn_info->name().empty()) {
|
if (function->name().empty()) {
|
||||||
// If it's a mangled C++ name (?name@...) just use the name.
|
// If it's a mangled C++ name (?name@...) just use the name.
|
||||||
// TODO(benvanik): better demangling, or leave it to clients.
|
// TODO(benvanik): better demangling, or leave it to clients.
|
||||||
/*if (name[0] == '?') {
|
/*if (name[0] == '?') {
|
||||||
size_t at = name.find('@');
|
size_t at = name.find('@');
|
||||||
name = name.substr(1, at - 1);
|
name = name.substr(1, at - 1);
|
||||||
}*/
|
}*/
|
||||||
fn_info->set_name(name.c_str());
|
function->set_name(name.c_str());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Variable.
|
// Variable.
|
||||||
|
|
|
@ -17,13 +17,13 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/base/mutex.h"
|
#include "xenia/base/mutex.h"
|
||||||
#include "xenia/cpu/symbol_info.h"
|
#include "xenia/cpu/function.h"
|
||||||
|
#include "xenia/cpu/symbol.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
|
||||||
class Function;
|
|
||||||
class Processor;
|
class Processor;
|
||||||
|
|
||||||
class Module {
|
class Module {
|
||||||
|
@ -37,36 +37,36 @@ class Module {
|
||||||
|
|
||||||
virtual bool ContainsAddress(uint32_t address);
|
virtual bool ContainsAddress(uint32_t address);
|
||||||
|
|
||||||
SymbolInfo* LookupSymbol(uint32_t address, bool wait = true);
|
Symbol* LookupSymbol(uint32_t address, bool wait = true);
|
||||||
virtual SymbolStatus DeclareFunction(uint32_t address,
|
virtual Symbol::Status DeclareFunction(uint32_t address,
|
||||||
FunctionInfo** out_symbol_info);
|
Function** out_function);
|
||||||
virtual SymbolStatus DeclareVariable(uint32_t address,
|
virtual Symbol::Status DeclareVariable(uint32_t address, Symbol** out_symbol);
|
||||||
VariableInfo** out_symbol_info);
|
|
||||||
|
|
||||||
SymbolStatus DefineFunction(FunctionInfo* symbol_info);
|
Symbol::Status DefineFunction(Function* symbol);
|
||||||
SymbolStatus DefineVariable(VariableInfo* symbol_info);
|
Symbol::Status DefineVariable(Symbol* symbol);
|
||||||
|
|
||||||
void ForEachFunction(std::function<void(FunctionInfo*)> callback);
|
void ForEachFunction(std::function<void(Function*)> callback);
|
||||||
void ForEachSymbol(size_t start_index, size_t end_index,
|
void ForEachSymbol(size_t start_index, size_t end_index,
|
||||||
std::function<void(SymbolInfo*)> callback);
|
std::function<void(Symbol*)> callback);
|
||||||
size_t QuerySymbolCount();
|
size_t QuerySymbolCount();
|
||||||
|
|
||||||
bool ReadMap(const char* file_name);
|
bool ReadMap(const char* file_name);
|
||||||
|
|
||||||
private:
|
|
||||||
SymbolStatus DeclareSymbol(SymbolType type, uint32_t address,
|
|
||||||
SymbolInfo** out_symbol_info);
|
|
||||||
SymbolStatus DefineSymbol(SymbolInfo* symbol_info);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Processor* processor_;
|
virtual std::unique_ptr<Function> CreateFunction(uint32_t address) = 0;
|
||||||
Memory* memory_;
|
|
||||||
|
Processor* processor_ = nullptr;
|
||||||
|
Memory* memory_ = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Symbol::Status DeclareSymbol(Symbol::Type type, uint32_t address,
|
||||||
|
Symbol** out_symbol);
|
||||||
|
Symbol::Status DefineSymbol(Symbol* symbol);
|
||||||
|
|
||||||
// TODO(benvanik): replace with a better data structure.
|
// TODO(benvanik): replace with a better data structure.
|
||||||
xe::mutex lock_;
|
xe::mutex lock_;
|
||||||
std::unordered_map<uint32_t, SymbolInfo*> map_;
|
std::unordered_map<uint32_t, Symbol*> map_;
|
||||||
std::vector<std::unique_ptr<SymbolInfo>> list_;
|
std::vector<std::unique_ptr<Symbol>> list_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
|
|
|
@ -42,6 +42,11 @@ class BuiltinModule : public Module {
|
||||||
return (address & 0xFFFFFFF0) == 0xFFFFFFF0;
|
return (address & 0xFFFFFFF0) == 0xFFFFFFF0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<Function> CreateFunction(uint32_t address) override {
|
||||||
|
return std::unique_ptr<Function>(new BuiltinFunction(this, address));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string name_;
|
std::string name_;
|
||||||
};
|
};
|
||||||
|
@ -142,20 +147,22 @@ std::vector<Module*> Processor::GetModules() {
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionInfo* Processor::DefineBuiltin(const std::string& name,
|
Function* Processor::DefineBuiltin(const std::string& name,
|
||||||
FunctionInfo::BuiltinHandler handler,
|
BuiltinFunction::Handler handler, void* arg0,
|
||||||
void* arg0, void* arg1) {
|
void* arg1) {
|
||||||
uint32_t address = next_builtin_address_;
|
uint32_t address = next_builtin_address_;
|
||||||
next_builtin_address_ += 4;
|
next_builtin_address_ += 4;
|
||||||
|
|
||||||
FunctionInfo* fn_info;
|
Function* function;
|
||||||
builtin_module_->DeclareFunction(address, &fn_info);
|
builtin_module_->DeclareFunction(address, &function);
|
||||||
fn_info->set_end_address(address + 4);
|
function->set_end_address(address + 4);
|
||||||
fn_info->set_name(name);
|
function->set_name(name);
|
||||||
fn_info->SetupBuiltin(handler, arg0, arg1);
|
|
||||||
fn_info->set_status(SymbolStatus::kDeclared);
|
|
||||||
|
|
||||||
return fn_info;
|
auto builtin_function = static_cast<BuiltinFunction*>(function);
|
||||||
|
builtin_function->SetupBuiltin(handler, arg0, arg1);
|
||||||
|
|
||||||
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
Function* Processor::QueryFunction(uint32_t address) {
|
Function* Processor::QueryFunction(uint32_t address) {
|
||||||
|
@ -170,40 +177,36 @@ std::vector<Function*> Processor::FindFunctionsWithAddress(uint32_t address) {
|
||||||
return entry_table_.FindWithAddress(address);
|
return entry_table_.FindWithAddress(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Processor::ResolveFunction(uint32_t address, Function** out_function) {
|
Function* Processor::ResolveFunction(uint32_t address) {
|
||||||
*out_function = nullptr;
|
|
||||||
Entry* entry;
|
Entry* entry;
|
||||||
Entry::Status status = entry_table_.GetOrCreate(address, &entry);
|
Entry::Status status = entry_table_.GetOrCreate(address, &entry);
|
||||||
if (status == Entry::STATUS_NEW) {
|
if (status == Entry::STATUS_NEW) {
|
||||||
// Needs to be generated. We have the 'lock' on it and must do so now.
|
// Needs to be generated. We have the 'lock' on it and must do so now.
|
||||||
|
|
||||||
// Grab symbol declaration.
|
// Grab symbol declaration.
|
||||||
FunctionInfo* symbol_info;
|
auto function = LookupFunction(address);
|
||||||
if (!LookupFunctionInfo(address, &symbol_info)) {
|
if (!function) {
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DemandFunction(symbol_info, &entry->function)) {
|
if (!DemandFunction(function)) {
|
||||||
entry->status = Entry::STATUS_FAILED;
|
entry->status = Entry::STATUS_FAILED;
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
entry->end_address = symbol_info->end_address();
|
entry->function = function;
|
||||||
|
entry->end_address = function->end_address();
|
||||||
status = entry->status = Entry::STATUS_READY;
|
status = entry->status = Entry::STATUS_READY;
|
||||||
}
|
}
|
||||||
if (status == Entry::STATUS_READY) {
|
if (status == Entry::STATUS_READY) {
|
||||||
// Ready to use.
|
// Ready to use.
|
||||||
*out_function = entry->function;
|
return entry->function;
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
// Failed or bad state.
|
// Failed or bad state.
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Processor::LookupFunctionInfo(uint32_t address,
|
Function* Processor::LookupFunction(uint32_t address) {
|
||||||
FunctionInfo** out_symbol_info) {
|
|
||||||
*out_symbol_info = nullptr;
|
|
||||||
|
|
||||||
// TODO(benvanik): fast reject invalid addresses/log errors.
|
// TODO(benvanik): fast reject invalid addresses/log errors.
|
||||||
|
|
||||||
// Find the module that contains the address.
|
// Find the module that contains the address.
|
||||||
|
@ -221,64 +224,57 @@ bool Processor::LookupFunctionInfo(uint32_t address,
|
||||||
}
|
}
|
||||||
if (!code_module) {
|
if (!code_module) {
|
||||||
// No module found that could contain the address.
|
// No module found that could contain the address.
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return LookupFunctionInfo(code_module, address, out_symbol_info);
|
return LookupFunction(code_module, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Processor::LookupFunctionInfo(Module* module, uint32_t address,
|
Function* Processor::LookupFunction(Module* module, uint32_t address) {
|
||||||
FunctionInfo** out_symbol_info) {
|
|
||||||
// Atomic create/lookup symbol in module.
|
// Atomic create/lookup symbol in module.
|
||||||
// If we get back the NEW flag we must declare it now.
|
// If we get back the NEW flag we must declare it now.
|
||||||
FunctionInfo* symbol_info = nullptr;
|
Function* function = nullptr;
|
||||||
SymbolStatus symbol_status = module->DeclareFunction(address, &symbol_info);
|
auto symbol_status = module->DeclareFunction(address, &function);
|
||||||
if (symbol_status == SymbolStatus::kNew) {
|
if (symbol_status == Symbol::Status::kNew) {
|
||||||
// Symbol is undeclared, so declare now.
|
// Symbol is undeclared, so declare now.
|
||||||
if (!frontend_->DeclareFunction(symbol_info)) {
|
assert_true(function->is_guest());
|
||||||
symbol_info->set_status(SymbolStatus::kFailed);
|
if (!frontend_->DeclareFunction(static_cast<GuestFunction*>(function))) {
|
||||||
return false;
|
function->set_status(Symbol::Status::kFailed);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
symbol_info->set_status(SymbolStatus::kDeclared);
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
}
|
}
|
||||||
|
return function;
|
||||||
*out_symbol_info = symbol_info;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Processor::DemandFunction(FunctionInfo* symbol_info,
|
bool Processor::DemandFunction(Function* function) {
|
||||||
Function** out_function) {
|
|
||||||
*out_function = nullptr;
|
|
||||||
|
|
||||||
// Lock function for generation. If it's already being generated
|
// Lock function for generation. If it's already being generated
|
||||||
// by another thread this will block and return DECLARED.
|
// by another thread this will block and return DECLARED.
|
||||||
Module* module = symbol_info->module();
|
auto module = function->module();
|
||||||
SymbolStatus symbol_status = module->DefineFunction(symbol_info);
|
auto symbol_status = module->DefineFunction(function);
|
||||||
if (symbol_status == SymbolStatus::kNew) {
|
if (symbol_status == Symbol::Status::kNew) {
|
||||||
// Symbol is undefined, so define now.
|
// Symbol is undefined, so define now.
|
||||||
Function* function = nullptr;
|
assert_true(function->is_guest());
|
||||||
if (!frontend_->DefineFunction(symbol_info, debug_info_flags_, &function)) {
|
if (!frontend_->DefineFunction(static_cast<GuestFunction*>(function),
|
||||||
symbol_info->set_status(SymbolStatus::kFailed);
|
debug_info_flags_)) {
|
||||||
|
function->set_status(Symbol::Status::kFailed);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
symbol_info->set_function(function);
|
|
||||||
|
|
||||||
// Before we give the symbol back to the rest, let the debugger know.
|
// Before we give the symbol back to the rest, let the debugger know.
|
||||||
if (debugger_) {
|
if (debugger_) {
|
||||||
debugger_->OnFunctionDefined(symbol_info, function);
|
debugger_->OnFunctionDefined(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
symbol_info->set_status(SymbolStatus::kDefined);
|
function->set_status(Symbol::Status::kDefined);
|
||||||
symbol_status = symbol_info->status();
|
symbol_status = function->status();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symbol_status == SymbolStatus::kFailed) {
|
if (symbol_status == Symbol::Status::kFailed) {
|
||||||
// Symbol likely failed.
|
// Symbol likely failed.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_function = symbol_info->function();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,8 +282,8 @@ bool Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
||||||
SCOPE_profile_cpu_f("cpu");
|
SCOPE_profile_cpu_f("cpu");
|
||||||
|
|
||||||
// Attempt to get the function.
|
// Attempt to get the function.
|
||||||
Function* fn;
|
auto function = ResolveFunction(address);
|
||||||
if (!ResolveFunction(address, &fn)) {
|
if (!function) {
|
||||||
// Symbol not found in any module.
|
// Symbol not found in any module.
|
||||||
XELOGCPU("Execute(%.8X): failed to find function", address);
|
XELOGCPU("Execute(%.8X): failed to find function", address);
|
||||||
return false;
|
return false;
|
||||||
|
@ -305,7 +301,7 @@ bool Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
||||||
context->lr = 0xBCBCBCBC;
|
context->lr = 0xBCBCBCBC;
|
||||||
|
|
||||||
// Execute the function.
|
// Execute the function.
|
||||||
auto result = fn->Call(thread_state, uint32_t(context->lr));
|
auto result = function->Call(thread_state, uint32_t(context->lr));
|
||||||
|
|
||||||
context->lr = previous_lr;
|
context->lr = previous_lr;
|
||||||
context->r[1] += 64 + 112;
|
context->r[1] += 64 + 112;
|
||||||
|
|
|
@ -69,17 +69,16 @@ class Processor {
|
||||||
std::vector<Module*> GetModules();
|
std::vector<Module*> GetModules();
|
||||||
|
|
||||||
Module* builtin_module() const { return builtin_module_; }
|
Module* builtin_module() const { return builtin_module_; }
|
||||||
FunctionInfo* DefineBuiltin(const std::string& name,
|
Function* DefineBuiltin(const std::string& name,
|
||||||
FunctionInfo::BuiltinHandler handler, void* arg0,
|
BuiltinFunction::Handler handler, void* arg0,
|
||||||
void* arg1);
|
void* arg1);
|
||||||
|
|
||||||
Function* QueryFunction(uint32_t address);
|
Function* QueryFunction(uint32_t address);
|
||||||
std::vector<Function*> FindFunctionsWithAddress(uint32_t address);
|
std::vector<Function*> FindFunctionsWithAddress(uint32_t address);
|
||||||
|
|
||||||
bool LookupFunctionInfo(uint32_t address, FunctionInfo** out_symbol_info);
|
Function* LookupFunction(uint32_t address);
|
||||||
bool LookupFunctionInfo(Module* module, uint32_t address,
|
Function* LookupFunction(Module* module, uint32_t address);
|
||||||
FunctionInfo** out_symbol_info);
|
Function* ResolveFunction(uint32_t address);
|
||||||
bool ResolveFunction(uint32_t address, Function** out_function);
|
|
||||||
|
|
||||||
bool Execute(ThreadState* thread_state, uint32_t address);
|
bool Execute(ThreadState* thread_state, uint32_t address);
|
||||||
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t args[],
|
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t args[],
|
||||||
|
@ -89,7 +88,7 @@ class Processor {
|
||||||
void LowerIrql(Irql old_value);
|
void LowerIrql(Irql old_value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool DemandFunction(FunctionInfo* symbol_info, Function** out_function);
|
bool DemandFunction(Function* function);
|
||||||
|
|
||||||
Memory* memory_ = nullptr;
|
Memory* memory_ = nullptr;
|
||||||
debug::Debugger* debugger_ = nullptr;
|
debug::Debugger* debugger_ = nullptr;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include "xenia/base/filesystem.h"
|
#include "xenia/base/filesystem.h"
|
||||||
#include "xenia/base/platform.h"
|
#include "xenia/base/platform.h"
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
|
#include "xenia/cpu/function.h"
|
||||||
|
#include "xenia/cpu/processor.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
@ -60,5 +62,10 @@ bool RawModule::ContainsAddress(uint32_t address) {
|
||||||
return address >= low_address_ && address < high_address_;
|
return address >= low_address_ && address < high_address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Function> RawModule::CreateFunction(uint32_t address) {
|
||||||
|
return std::unique_ptr<Function>(
|
||||||
|
processor_->backend()->CreateGuestFunction(this, address));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -28,6 +28,9 @@ class RawModule : public Module {
|
||||||
|
|
||||||
bool ContainsAddress(uint32_t address) override;
|
bool ContainsAddress(uint32_t address) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<Function> CreateFunction(uint32_t address) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string name_;
|
std::string name_;
|
||||||
uint32_t base_address_;
|
uint32_t base_address_;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "xenia/cpu/symbol_info.h"
|
#include "xenia/cpu/function.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
@ -49,7 +49,7 @@ struct StackFrame {
|
||||||
} host_symbol;
|
} host_symbol;
|
||||||
// Contains symbol information for kGuest frames.
|
// Contains symbol information for kGuest frames.
|
||||||
struct {
|
struct {
|
||||||
FunctionInfo* function_info;
|
Function* function;
|
||||||
} guest_symbol;
|
} guest_symbol;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -205,19 +205,21 @@ class Win32StackWalker : public StackWalker {
|
||||||
if (frame.host_pc >= code_cache_min_ && frame.host_pc < code_cache_max_) {
|
if (frame.host_pc >= code_cache_min_ && frame.host_pc < code_cache_max_) {
|
||||||
// Guest symbol, so we can look it up quickly in the code cache.
|
// Guest symbol, so we can look it up quickly in the code cache.
|
||||||
frame.type = StackFrame::Type::kGuest;
|
frame.type = StackFrame::Type::kGuest;
|
||||||
auto function_info = code_cache_->LookupFunction(frame.host_pc);
|
auto function = code_cache_->LookupFunction(frame.host_pc);
|
||||||
if (function_info) {
|
if (function) {
|
||||||
frame.guest_symbol.function_info = function_info;
|
frame.guest_symbol.function = function;
|
||||||
// Figure out where in guest code we are by looking up the
|
// Figure out where in guest code we are by looking up the
|
||||||
// displacement in x64 from the JIT'ed code start to the PC.
|
// displacement in x64 from the JIT'ed code start to the PC.
|
||||||
uint32_t host_displacement =
|
if (function->is_guest()) {
|
||||||
uint32_t(frame.host_pc) -
|
auto guest_function = static_cast<GuestFunction*>(function);
|
||||||
uint32_t(uint64_t(function_info->function()->machine_code()));
|
uint32_t host_displacement =
|
||||||
auto entry =
|
uint32_t(frame.host_pc) -
|
||||||
function_info->function()->LookupCodeOffset(host_displacement);
|
uint32_t(uint64_t(guest_function->machine_code()));
|
||||||
frame.guest_pc = entry->source_offset;
|
auto entry = guest_function->LookupCodeOffset(host_displacement);
|
||||||
|
frame.guest_pc = entry->source_offset;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
frame.guest_symbol.function_info = nullptr;
|
frame.guest_symbol.function = nullptr;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Host symbol, which means either emulator or system.
|
// Host symbol, which means either emulator or system.
|
||||||
|
|
|
@ -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_
|
|
@ -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
|
|
|
@ -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_
|
|
|
@ -71,29 +71,34 @@ bool TestModule::ContainsAddress(uint32_t address) {
|
||||||
return contains_address_(address);
|
return contains_address_(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolStatus TestModule::DeclareFunction(uint32_t address,
|
std::unique_ptr<Function> TestModule::CreateFunction(uint32_t address) {
|
||||||
FunctionInfo** out_symbol_info) {
|
return std::unique_ptr<Function>(
|
||||||
SymbolStatus status = Module::DeclareFunction(address, out_symbol_info);
|
processor_->backend()->CreateGuestFunction(this, address));
|
||||||
if (status == SymbolStatus::kNew) {
|
}
|
||||||
auto symbol_info = *out_symbol_info;
|
|
||||||
|
Symbol::Status TestModule::DeclareFunction(uint32_t address,
|
||||||
|
Function** out_function) {
|
||||||
|
Symbol::Status status = Module::DeclareFunction(address, out_function);
|
||||||
|
if (status == Symbol::Status::kNew) {
|
||||||
|
auto function = static_cast<GuestFunction*>(*out_function);
|
||||||
|
|
||||||
// Reset() all caching when we leave.
|
// Reset() all caching when we leave.
|
||||||
xe::make_reset_scope(compiler_);
|
xe::make_reset_scope(compiler_);
|
||||||
xe::make_reset_scope(assembler_);
|
xe::make_reset_scope(assembler_);
|
||||||
|
|
||||||
if (!generate_(*builder_.get())) {
|
if (!generate_(*builder_.get())) {
|
||||||
symbol_info->set_status(SymbolStatus::kFailed);
|
function->set_status(Symbol::Status::kFailed);
|
||||||
return SymbolStatus::kFailed;
|
return Symbol::Status::kFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run optimization passes.
|
||||||
compiler_->Compile(builder_.get());
|
compiler_->Compile(builder_.get());
|
||||||
|
|
||||||
Function* fn = nullptr;
|
// Assemble the function.
|
||||||
assembler_->Assemble(symbol_info, builder_.get(), 0, nullptr, &fn);
|
assembler_->Assemble(function, builder_.get(), 0, nullptr);
|
||||||
|
|
||||||
symbol_info->set_function(fn);
|
status = Symbol::Status::kDefined;
|
||||||
status = SymbolStatus::kDefined;
|
function->set_status(status);
|
||||||
symbol_info->set_status(status);
|
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,11 @@ class TestModule : public Module {
|
||||||
|
|
||||||
bool ContainsAddress(uint32_t address) override;
|
bool ContainsAddress(uint32_t address) override;
|
||||||
|
|
||||||
SymbolStatus DeclareFunction(uint32_t address,
|
Symbol::Status DeclareFunction(uint32_t address,
|
||||||
FunctionInfo** out_symbol_info) override;
|
Function** out_symbol) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<Function> CreateFunction(uint32_t address) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string name_;
|
std::string name_;
|
||||||
|
|
|
@ -65,8 +65,7 @@ class TestFunction {
|
||||||
void Run(std::function<void(PPCContext*)> pre_call,
|
void Run(std::function<void(PPCContext*)> pre_call,
|
||||||
std::function<void(PPCContext*)> post_call) {
|
std::function<void(PPCContext*)> post_call) {
|
||||||
for (auto& processor : processors) {
|
for (auto& processor : processors) {
|
||||||
xe::cpu::Function* fn;
|
auto fn = processor->ResolveFunction(0x80000000);
|
||||||
processor->ResolveFunction(0x80000000, &fn);
|
|
||||||
|
|
||||||
uint32_t stack_size = 64 * 1024;
|
uint32_t stack_size = 64 * 1024;
|
||||||
uint32_t stack_address = memory_size - stack_size;
|
uint32_t stack_address = memory_size - stack_size;
|
||||||
|
|
|
@ -375,12 +375,12 @@ bool XexModule::SetupLibraryImports(const char* name,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup a variable and define it.
|
// Setup a variable and define it.
|
||||||
VariableInfo* var_info;
|
Symbol* var_info;
|
||||||
DeclareVariable(record_addr, &var_info);
|
DeclareVariable(record_addr, &var_info);
|
||||||
var_info->set_name(import_name.GetString());
|
var_info->set_name(import_name.GetString());
|
||||||
var_info->set_status(SymbolStatus::kDeclared);
|
var_info->set_status(Symbol::Status::kDeclared);
|
||||||
DefineVariable(var_info);
|
DefineVariable(var_info);
|
||||||
var_info->set_status(SymbolStatus::kDefined);
|
var_info->set_status(Symbol::Status::kDefined);
|
||||||
} else if (record_type == 1) {
|
} else if (record_type == 1) {
|
||||||
// Thunk.
|
// Thunk.
|
||||||
if (kernel_export) {
|
if (kernel_export) {
|
||||||
|
@ -389,10 +389,10 @@ bool XexModule::SetupLibraryImports(const char* name,
|
||||||
import_name.AppendFormat("__%s_%.3X", libbasename.c_str(), ordinal);
|
import_name.AppendFormat("__%s_%.3X", libbasename.c_str(), ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionInfo* fn_info;
|
Function* function;
|
||||||
DeclareFunction(record_addr, &fn_info);
|
DeclareFunction(record_addr, &function);
|
||||||
fn_info->set_end_address(record_addr + 16 - 4);
|
function->set_end_address(record_addr + 16 - 4);
|
||||||
fn_info->set_name(import_name.GetString());
|
function->set_name(import_name.GetString());
|
||||||
|
|
||||||
if (kernel_export) {
|
if (kernel_export) {
|
||||||
// On load we have something like this in memory:
|
// On load we have something like this in memory:
|
||||||
|
@ -415,14 +415,14 @@ bool XexModule::SetupLibraryImports(const char* name,
|
||||||
xe::store_and_swap<uint32_t>(p + 0x8, 0x60000000);
|
xe::store_and_swap<uint32_t>(p + 0x8, 0x60000000);
|
||||||
xe::store_and_swap<uint32_t>(p + 0xC, 0x60000000);
|
xe::store_and_swap<uint32_t>(p + 0xC, 0x60000000);
|
||||||
|
|
||||||
FunctionInfo::ExternHandler handler = 0;
|
GuestFunction::ExternHandler handler = nullptr;
|
||||||
if (kernel_export) {
|
if (kernel_export) {
|
||||||
if (kernel_export->function_data.trampoline) {
|
if (kernel_export->function_data.trampoline) {
|
||||||
handler = (FunctionInfo::ExternHandler)
|
handler = (GuestFunction::ExternHandler)
|
||||||
kernel_export->function_data.trampoline;
|
kernel_export->function_data.trampoline;
|
||||||
} else {
|
} else {
|
||||||
handler =
|
handler =
|
||||||
(FunctionInfo::ExternHandler)kernel_export->function_data.shim;
|
(GuestFunction::ExternHandler)kernel_export->function_data.shim;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
XELOGW("WARNING: Imported kernel function %s is unimplemented!",
|
XELOGW("WARNING: Imported kernel function %s is unimplemented!",
|
||||||
|
@ -430,7 +430,7 @@ bool XexModule::SetupLibraryImports(const char* name,
|
||||||
handler = UndefinedImport;
|
handler = UndefinedImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn_info->SetupExtern(handler);
|
static_cast<GuestFunction*>(function)->SetupExtern(handler);
|
||||||
} else if (user_export_addr) {
|
} else if (user_export_addr) {
|
||||||
// Rewrite PPC code to set r11 to the target address
|
// Rewrite PPC code to set r11 to the target address
|
||||||
// So we'll have:
|
// So we'll have:
|
||||||
|
@ -458,7 +458,7 @@ bool XexModule::SetupLibraryImports(const char* name,
|
||||||
xe::store_and_swap<uint32_t>(p + 0xC, 0x60000000);
|
xe::store_and_swap<uint32_t>(p + 0xC, 0x60000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn_info->set_status(SymbolStatus::kDeclared);
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
} else {
|
} else {
|
||||||
// Bad.
|
// Bad.
|
||||||
assert_always();
|
assert_always();
|
||||||
|
@ -472,6 +472,11 @@ bool XexModule::ContainsAddress(uint32_t address) {
|
||||||
return address >= low_address_ && address < high_address_;
|
return address >= low_address_ && address < high_address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Function> XexModule::CreateFunction(uint32_t address) {
|
||||||
|
return std::unique_ptr<Function>(
|
||||||
|
processor_->backend()->CreateGuestFunction(this, address));
|
||||||
|
}
|
||||||
|
|
||||||
bool XexModule::FindSaveRest() {
|
bool XexModule::FindSaveRest() {
|
||||||
// Special stack save/restore functions.
|
// Special stack save/restore functions.
|
||||||
// http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/md/ppc/xxx.s.htm
|
// http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/md/ppc/xxx.s.htm
|
||||||
|
@ -677,27 +682,27 @@ bool XexModule::FindSaveRest() {
|
||||||
uint32_t address = gplr_start;
|
uint32_t address = gplr_start;
|
||||||
for (int n = 14; n <= 31; n++) {
|
for (int n = 14; n <= 31; n++) {
|
||||||
snprintf(name, xe::countof(name), "__savegprlr_%d", n);
|
snprintf(name, xe::countof(name), "__savegprlr_%d", n);
|
||||||
FunctionInfo* symbol_info;
|
Function* function;
|
||||||
DeclareFunction(address, &symbol_info);
|
DeclareFunction(address, &function);
|
||||||
symbol_info->set_end_address(address + (31 - n) * 4 + 2 * 4);
|
function->set_end_address(address + (31 - n) * 4 + 2 * 4);
|
||||||
symbol_info->set_name(name);
|
function->set_name(name);
|
||||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveGprLr;
|
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveGprLr;
|
||||||
symbol_info->set_behavior(FunctionBehavior::kProlog);
|
function->set_behavior(Function::Behavior::kProlog);
|
||||||
symbol_info->set_status(SymbolStatus::kDeclared);
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
address += 4;
|
address += 4;
|
||||||
}
|
}
|
||||||
address = gplr_start + 20 * 4;
|
address = gplr_start + 20 * 4;
|
||||||
for (int n = 14; n <= 31; n++) {
|
for (int n = 14; n <= 31; n++) {
|
||||||
snprintf(name, xe::countof(name), "__restgprlr_%d", n);
|
snprintf(name, xe::countof(name), "__restgprlr_%d", n);
|
||||||
FunctionInfo* symbol_info;
|
Function* function;
|
||||||
DeclareFunction(address, &symbol_info);
|
DeclareFunction(address, &function);
|
||||||
symbol_info->set_end_address(address + (31 - n) * 4 + 3 * 4);
|
function->set_end_address(address + (31 - n) * 4 + 3 * 4);
|
||||||
symbol_info->set_name(name);
|
function->set_name(name);
|
||||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestGprLr;
|
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestGprLr;
|
||||||
symbol_info->set_behavior(FunctionBehavior::kEpilogReturn);
|
function->set_behavior(Function::Behavior::kEpilogReturn);
|
||||||
symbol_info->set_status(SymbolStatus::kDeclared);
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
address += 4;
|
address += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -705,27 +710,27 @@ bool XexModule::FindSaveRest() {
|
||||||
uint32_t address = fpr_start;
|
uint32_t address = fpr_start;
|
||||||
for (int n = 14; n <= 31; n++) {
|
for (int n = 14; n <= 31; n++) {
|
||||||
snprintf(name, xe::countof(name), "__savefpr_%d", n);
|
snprintf(name, xe::countof(name), "__savefpr_%d", n);
|
||||||
FunctionInfo* symbol_info;
|
Function* function;
|
||||||
DeclareFunction(address, &symbol_info);
|
DeclareFunction(address, &function);
|
||||||
symbol_info->set_end_address(address + (31 - n) * 4 + 1 * 4);
|
function->set_end_address(address + (31 - n) * 4 + 1 * 4);
|
||||||
symbol_info->set_name(name);
|
function->set_name(name);
|
||||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveFpr;
|
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveFpr;
|
||||||
symbol_info->set_behavior(FunctionBehavior::kProlog);
|
function->set_behavior(Function::Behavior::kProlog);
|
||||||
symbol_info->set_status(SymbolStatus::kDeclared);
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
address += 4;
|
address += 4;
|
||||||
}
|
}
|
||||||
address = fpr_start + (18 * 4) + (1 * 4);
|
address = fpr_start + (18 * 4) + (1 * 4);
|
||||||
for (int n = 14; n <= 31; n++) {
|
for (int n = 14; n <= 31; n++) {
|
||||||
snprintf(name, xe::countof(name), "__restfpr_%d", n);
|
snprintf(name, xe::countof(name), "__restfpr_%d", n);
|
||||||
FunctionInfo* symbol_info;
|
Function* function;
|
||||||
DeclareFunction(address, &symbol_info);
|
DeclareFunction(address, &function);
|
||||||
symbol_info->set_end_address(address + (31 - n) * 4 + 1 * 4);
|
function->set_end_address(address + (31 - n) * 4 + 1 * 4);
|
||||||
symbol_info->set_name(name);
|
function->set_name(name);
|
||||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestFpr;
|
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestFpr;
|
||||||
symbol_info->set_behavior(FunctionBehavior::kEpilog);
|
function->set_behavior(Function::Behavior::kEpilog);
|
||||||
symbol_info->set_status(SymbolStatus::kDeclared);
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
address += 4;
|
address += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -738,49 +743,49 @@ bool XexModule::FindSaveRest() {
|
||||||
uint32_t address = vmx_start;
|
uint32_t address = vmx_start;
|
||||||
for (int n = 14; n <= 31; n++) {
|
for (int n = 14; n <= 31; n++) {
|
||||||
snprintf(name, xe::countof(name), "__savevmx_%d", n);
|
snprintf(name, xe::countof(name), "__savevmx_%d", n);
|
||||||
FunctionInfo* symbol_info;
|
Function* function;
|
||||||
DeclareFunction(address, &symbol_info);
|
DeclareFunction(address, &function);
|
||||||
symbol_info->set_name(name);
|
function->set_name(name);
|
||||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
|
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
|
||||||
symbol_info->set_behavior(FunctionBehavior::kProlog);
|
function->set_behavior(Function::Behavior::kProlog);
|
||||||
symbol_info->set_status(SymbolStatus::kDeclared);
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
address += 2 * 4;
|
address += 2 * 4;
|
||||||
}
|
}
|
||||||
address += 4;
|
address += 4;
|
||||||
for (int n = 64; n <= 127; n++) {
|
for (int n = 64; n <= 127; n++) {
|
||||||
snprintf(name, xe::countof(name), "__savevmx_%d", n);
|
snprintf(name, xe::countof(name), "__savevmx_%d", n);
|
||||||
FunctionInfo* symbol_info;
|
Function* function;
|
||||||
DeclareFunction(address, &symbol_info);
|
DeclareFunction(address, &function);
|
||||||
symbol_info->set_name(name);
|
function->set_name(name);
|
||||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
|
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
|
||||||
symbol_info->set_behavior(FunctionBehavior::kProlog);
|
function->set_behavior(Function::Behavior::kProlog);
|
||||||
symbol_info->set_status(SymbolStatus::kDeclared);
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
address += 2 * 4;
|
address += 2 * 4;
|
||||||
}
|
}
|
||||||
address = vmx_start + (18 * 2 * 4) + (1 * 4) + (64 * 2 * 4) + (1 * 4);
|
address = vmx_start + (18 * 2 * 4) + (1 * 4) + (64 * 2 * 4) + (1 * 4);
|
||||||
for (int n = 14; n <= 31; n++) {
|
for (int n = 14; n <= 31; n++) {
|
||||||
snprintf(name, xe::countof(name), "__restvmx_%d", n);
|
snprintf(name, xe::countof(name), "__restvmx_%d", n);
|
||||||
FunctionInfo* symbol_info;
|
Function* function;
|
||||||
DeclareFunction(address, &symbol_info);
|
DeclareFunction(address, &function);
|
||||||
symbol_info->set_name(name);
|
function->set_name(name);
|
||||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx;
|
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx;
|
||||||
symbol_info->set_behavior(FunctionBehavior::kEpilog);
|
function->set_behavior(Function::Behavior::kEpilog);
|
||||||
symbol_info->set_status(SymbolStatus::kDeclared);
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
address += 2 * 4;
|
address += 2 * 4;
|
||||||
}
|
}
|
||||||
address += 4;
|
address += 4;
|
||||||
for (int n = 64; n <= 127; n++) {
|
for (int n = 64; n <= 127; n++) {
|
||||||
snprintf(name, xe::countof(name), "__restvmx_%d", n);
|
snprintf(name, xe::countof(name), "__restvmx_%d", n);
|
||||||
FunctionInfo* symbol_info;
|
Function* function;
|
||||||
DeclareFunction(address, &symbol_info);
|
DeclareFunction(address, &function);
|
||||||
symbol_info->set_name(name);
|
function->set_name(name);
|
||||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx;
|
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx;
|
||||||
symbol_info->set_behavior(FunctionBehavior::kEpilog);
|
function->set_behavior(Function::Behavior::kEpilog);
|
||||||
symbol_info->set_status(SymbolStatus::kDeclared);
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
address += 2 * 4;
|
address += 2 * 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,12 +77,14 @@ class XexModule : public xe::cpu::Module {
|
||||||
|
|
||||||
bool ContainsAddress(uint32_t address) override;
|
bool ContainsAddress(uint32_t address) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<Function> CreateFunction(uint32_t address) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool SetupLibraryImports(const char* name,
|
bool SetupLibraryImports(const char* name,
|
||||||
const xex2_import_library* library);
|
const xex2_import_library* library);
|
||||||
bool FindSaveRest();
|
bool FindSaveRest();
|
||||||
|
|
||||||
private:
|
|
||||||
Processor* processor_ = nullptr;
|
Processor* processor_ = nullptr;
|
||||||
kernel::KernelState* kernel_state_ = nullptr;
|
kernel::KernelState* kernel_state_ = nullptr;
|
||||||
std::string name_;
|
std::string name_;
|
||||||
|
|
|
@ -308,10 +308,10 @@ bool DebugServer::ProcessPacket(const proto::Packet* packet) {
|
||||||
frame_body->host_function_address = frame.host_symbol.address;
|
frame_body->host_function_address = frame.host_symbol.address;
|
||||||
frame_body->guest_pc = frame.guest_pc;
|
frame_body->guest_pc = frame.guest_pc;
|
||||||
frame_body->guest_function_address = 0;
|
frame_body->guest_function_address = 0;
|
||||||
auto function_info = frame.guest_symbol.function_info;
|
auto function = frame.guest_symbol.function;
|
||||||
if (frame.type == cpu::StackFrame::Type::kGuest && function_info) {
|
if (frame.type == cpu::StackFrame::Type::kGuest && function) {
|
||||||
frame_body->guest_function_address = function_info->address();
|
frame_body->guest_function_address = function->address();
|
||||||
std::strncpy(frame_body->name, function_info->name().c_str(),
|
std::strncpy(frame_body->name, function->name().c_str(),
|
||||||
xe::countof(frame_body->name));
|
xe::countof(frame_body->name));
|
||||||
} else {
|
} else {
|
||||||
std::strncpy(frame_body->name, frame.host_symbol.name,
|
std::strncpy(frame_body->name, frame.host_symbol.name,
|
||||||
|
|
|
@ -169,10 +169,9 @@ void Debugger::DumpThreadStacks() {
|
||||||
XELOGI(" %.2lld %.16llX %s", count - i - 1, frame.host_pc,
|
XELOGI(" %.2lld %.16llX %s", count - i - 1, frame.host_pc,
|
||||||
frame.host_symbol.name);
|
frame.host_symbol.name);
|
||||||
} else {
|
} else {
|
||||||
auto function_info = frame.guest_symbol.function_info;
|
auto function = frame.guest_symbol.function;
|
||||||
XELOGI(" %.2lld %.16llX %.8X %s", count - i - 1, frame.host_pc,
|
XELOGI(" %.2lld %.16llX %.8X %s", count - i - 1, frame.host_pc,
|
||||||
frame.guest_pc,
|
frame.guest_pc, function ? function->name().c_str() : "?");
|
||||||
function_info ? function_info->name().c_str() : "?");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,12 +189,13 @@ int Debugger::AddBreakpoint(Breakpoint* breakpoint) {
|
||||||
auto fns =
|
auto fns =
|
||||||
emulator_->processor()->FindFunctionsWithAddress(breakpoint->address());
|
emulator_->processor()->FindFunctionsWithAddress(breakpoint->address());
|
||||||
|
|
||||||
|
// TODO(benvanik): breakpoints.
|
||||||
// Add.
|
// Add.
|
||||||
for (auto fn : fns) {
|
// for (auto fn : fns) {
|
||||||
if (fn->AddBreakpoint(breakpoint)) {
|
// if (fn->AddBreakpoint(breakpoint)) {
|
||||||
return 1;
|
// return 1;
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -226,9 +226,10 @@ int Debugger::RemoveBreakpoint(Breakpoint* breakpoint) {
|
||||||
emulator_->processor()->FindFunctionsWithAddress(breakpoint->address());
|
emulator_->processor()->FindFunctionsWithAddress(breakpoint->address());
|
||||||
|
|
||||||
// Remove.
|
// Remove.
|
||||||
for (auto fn : fns) {
|
// TODO(benvanik): breakpoints.
|
||||||
|
/*for (auto fn : fns) {
|
||||||
fn->RemoveBreakpoint(breakpoint);
|
fn->RemoveBreakpoint(breakpoint);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -322,31 +323,30 @@ void Debugger::OnThreadDestroyed(xe::kernel::XThread* thread) {
|
||||||
// TODO(benvanik): notify transports.
|
// TODO(benvanik): notify transports.
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::OnFunctionDefined(cpu::FunctionInfo* symbol_info,
|
void Debugger::OnFunctionDefined(cpu::Function* function) {
|
||||||
cpu::Function* function) {
|
// TODO(benvanik): breakpoints?
|
||||||
// Man, I'd love not to take this lock.
|
// Man, I'd love not to take this lock.
|
||||||
std::vector<Breakpoint*> breakpoints;
|
// std::vector<Breakpoint*> breakpoints;
|
||||||
{
|
//{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
// std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||||
for (uint32_t address = symbol_info->address();
|
// for (uint32_t address = function->address();
|
||||||
address <= symbol_info->end_address(); address += 4) {
|
// address <= function->end_address(); address += 4) {
|
||||||
auto range = breakpoints_.equal_range(address);
|
// auto range = breakpoints_.equal_range(address);
|
||||||
if (range.first == range.second) {
|
// if (range.first == range.second) {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
for (auto it = range.first; it != range.second; ++it) {
|
// for (auto it = range.first; it != range.second; ++it) {
|
||||||
Breakpoint* breakpoint = it->second;
|
// Breakpoint* breakpoint = it->second;
|
||||||
breakpoints.push_back(breakpoint);
|
// breakpoints.push_back(breakpoint);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
// if (breakpoints.size()) {
|
||||||
if (breakpoints.size()) {
|
// // Breakpoints to add!
|
||||||
// Breakpoints to add!
|
// for (auto breakpoint : breakpoints) {
|
||||||
for (auto breakpoint : breakpoints) {
|
// function->AddBreakpoint(breakpoint);
|
||||||
function->AddBreakpoint(breakpoint);
|
// }
|
||||||
}
|
//}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::OnBreakpointHit(xe::kernel::XThread* thread,
|
void Debugger::OnBreakpointHit(xe::kernel::XThread* thread,
|
||||||
|
|
|
@ -83,8 +83,7 @@ class Debugger {
|
||||||
void OnThreadExit(xe::kernel::XThread* thread);
|
void OnThreadExit(xe::kernel::XThread* thread);
|
||||||
void OnThreadDestroyed(xe::kernel::XThread* thread);
|
void OnThreadDestroyed(xe::kernel::XThread* thread);
|
||||||
|
|
||||||
void OnFunctionDefined(cpu::FunctionInfo* symbol_info,
|
void OnFunctionDefined(cpu::Function* function);
|
||||||
cpu::Function* function);
|
|
||||||
|
|
||||||
void OnBreakpointHit(xe::kernel::XThread* thread, Breakpoint* breakpoint);
|
void OnBreakpointHit(xe::kernel::XThread* thread, Breakpoint* breakpoint);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue