Refactoring SymbolInfo/FunctionInfo/Function into Symbol/Function.
This commit is contained in:
@@ -15,8 +15,7 @@
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class DebugInfo;
|
||||
class Function;
|
||||
class FunctionInfo;
|
||||
class GuestFunction;
|
||||
namespace hir {
|
||||
class HIRBuilder;
|
||||
} // namespace hir
|
||||
@@ -38,10 +37,9 @@ class Assembler {
|
||||
|
||||
virtual void Reset();
|
||||
|
||||
virtual bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
||||
virtual bool Assemble(GuestFunction* function, hir::HIRBuilder* builder,
|
||||
uint32_t debug_info_flags,
|
||||
std::unique_ptr<DebugInfo> debug_info,
|
||||
Function** out_function) = 0;
|
||||
std::unique_ptr<DebugInfo> debug_info) = 0;
|
||||
|
||||
protected:
|
||||
Backend* backend_;
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class GuestFunction;
|
||||
class Module;
|
||||
class Processor;
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
@@ -46,6 +48,9 @@ class Backend {
|
||||
|
||||
virtual std::unique_ptr<Assembler> CreateAssembler() = 0;
|
||||
|
||||
virtual std::unique_ptr<GuestFunction> CreateGuestFunction(
|
||||
Module* module, uint32_t address) = 0;
|
||||
|
||||
protected:
|
||||
Processor* processor_;
|
||||
MachineInfo machine_info_;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "xenia/cpu/symbol_info.h"
|
||||
#include "xenia/cpu/function.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
@@ -29,7 +29,7 @@ class CodeCache {
|
||||
|
||||
// Finds a function based on the given host PC (that may be within a
|
||||
// function).
|
||||
virtual FunctionInfo* LookupFunction(uint64_t host_pc) = 0;
|
||||
virtual GuestFunction* LookupFunction(uint64_t host_pc) = 0;
|
||||
|
||||
// Finds platform-specific function unwind info for the given host PC.
|
||||
virtual void* LookupUnwindInfo(uint64_t host_pc) = 0;
|
||||
|
||||
@@ -66,30 +66,26 @@ void X64Assembler::Reset() {
|
||||
Assembler::Reset();
|
||||
}
|
||||
|
||||
bool X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
|
||||
bool X64Assembler::Assemble(GuestFunction* function, HIRBuilder* builder,
|
||||
uint32_t debug_info_flags,
|
||||
std::unique_ptr<DebugInfo> debug_info,
|
||||
Function** out_function) {
|
||||
std::unique_ptr<DebugInfo> debug_info) {
|
||||
SCOPE_profile_cpu_f("cpu");
|
||||
|
||||
// Reset when we leave.
|
||||
xe::make_reset_scope(this);
|
||||
|
||||
// Create now, and populate as we go.
|
||||
// We may throw it away if we fail.
|
||||
auto fn = std::make_unique<X64Function>(symbol_info);
|
||||
|
||||
// Lower HIR -> x64.
|
||||
void* machine_code = nullptr;
|
||||
size_t code_size = 0;
|
||||
if (!emitter_->Emit(symbol_info, builder, debug_info_flags, debug_info.get(),
|
||||
machine_code, code_size, fn->source_map())) {
|
||||
if (!emitter_->Emit(function, builder, debug_info_flags, debug_info.get(),
|
||||
machine_code, code_size, function->source_map())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stash generated machine code.
|
||||
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmMachineCode) {
|
||||
DumpMachineCode(machine_code, code_size, fn->source_map(), &string_buffer_);
|
||||
DumpMachineCode(machine_code, code_size, function->source_map(),
|
||||
&string_buffer_);
|
||||
debug_info->set_machine_code_disasm(string_buffer_.ToString());
|
||||
string_buffer_.Reset();
|
||||
}
|
||||
@@ -102,11 +98,10 @@ bool X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
|
||||
}
|
||||
}
|
||||
|
||||
fn->set_debug_info(std::move(debug_info));
|
||||
fn->Setup(reinterpret_cast<uint8_t*>(machine_code), code_size);
|
||||
function->set_debug_info(std::move(debug_info));
|
||||
static_cast<X64Function*>(function)
|
||||
->Setup(reinterpret_cast<uint8_t*>(machine_code), code_size);
|
||||
|
||||
// Pass back ownership.
|
||||
*out_function = fn.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,10 +35,9 @@ class X64Assembler : public Assembler {
|
||||
|
||||
void Reset() override;
|
||||
|
||||
bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
||||
bool Assemble(GuestFunction* function, hir::HIRBuilder* builder,
|
||||
uint32_t debug_info_flags,
|
||||
std::unique_ptr<DebugInfo> debug_info,
|
||||
Function** out_function) override;
|
||||
std::unique_ptr<DebugInfo> debug_info) override;
|
||||
|
||||
private:
|
||||
void DumpMachineCode(void* machine_code, size_t code_size,
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
#include "xenia/cpu/backend/x64/x64_backend.h"
|
||||
|
||||
#include "xenia/cpu/backend/x64/x64_assembler.h"
|
||||
#include "xenia/cpu/backend/x64/x64_emitter.h"
|
||||
#include "xenia/cpu/backend/x64/x64_code_cache.h"
|
||||
#include "xenia/cpu/backend/x64/x64_emitter.h"
|
||||
#include "xenia/cpu/backend/x64/x64_function.h"
|
||||
#include "xenia/cpu/backend/x64/x64_sequences.h"
|
||||
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
@@ -106,6 +107,11 @@ std::unique_ptr<Assembler> X64Backend::CreateAssembler() {
|
||||
return std::make_unique<X64Assembler>(this);
|
||||
}
|
||||
|
||||
std::unique_ptr<GuestFunction> X64Backend::CreateGuestFunction(
|
||||
Module* module, uint32_t address) {
|
||||
return std::make_unique<X64Function>(module, address);
|
||||
}
|
||||
|
||||
using namespace Xbyak;
|
||||
|
||||
X64ThunkEmitter::X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator)
|
||||
|
||||
@@ -56,6 +56,9 @@ class X64Backend : public Backend {
|
||||
|
||||
std::unique_ptr<Assembler> CreateAssembler() override;
|
||||
|
||||
std::unique_ptr<GuestFunction> CreateGuestFunction(Module* module,
|
||||
uint32_t address) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<X64CodeCache> code_cache_;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/cpu/function.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
@@ -120,7 +121,7 @@ void* X64CodeCache::PlaceHostCode(uint32_t guest_address, void* machine_code,
|
||||
|
||||
void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size,
|
||||
FunctionInfo* function_info) {
|
||||
GuestFunction* function_info) {
|
||||
// Hold a lock while we bump the pointers up. This is important as the
|
||||
// unwind table requires entries AND code to be sorted in order.
|
||||
size_t low_mark;
|
||||
@@ -220,15 +221,15 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
||||
return uint32_t(uintptr_t(data_address));
|
||||
}
|
||||
|
||||
FunctionInfo* X64CodeCache::LookupFunction(uint64_t host_pc) {
|
||||
GuestFunction* X64CodeCache::LookupFunction(uint64_t host_pc) {
|
||||
uint32_t key = uint32_t(host_pc - kGeneratedCodeBase);
|
||||
void* fn_entry = std::bsearch(
|
||||
&key, generated_code_map_.data(), generated_code_map_.size() + 1,
|
||||
sizeof(std::pair<uint32_t, FunctionInfo*>),
|
||||
sizeof(std::pair<uint32_t, Function*>),
|
||||
[](const void* key_ptr, const void* element_ptr) {
|
||||
auto key = *reinterpret_cast<const uint32_t*>(key_ptr);
|
||||
auto element =
|
||||
reinterpret_cast<const std::pair<uint64_t, FunctionInfo*>*>(
|
||||
reinterpret_cast<const std::pair<uint64_t, GuestFunction*>*>(
|
||||
element_ptr);
|
||||
if (key < (element->first >> 32)) {
|
||||
return -1;
|
||||
@@ -239,7 +240,8 @@ FunctionInfo* X64CodeCache::LookupFunction(uint64_t host_pc) {
|
||||
}
|
||||
});
|
||||
if (fn_entry) {
|
||||
return reinterpret_cast<const std::pair<uint64_t, FunctionInfo*>*>(fn_entry)
|
||||
return reinterpret_cast<const std::pair<uint64_t, GuestFunction*>*>(
|
||||
fn_entry)
|
||||
->second;
|
||||
} else {
|
||||
return nullptr;
|
||||
|
||||
@@ -50,10 +50,10 @@ class X64CodeCache : public CodeCache {
|
||||
size_t code_size, size_t stack_size);
|
||||
void* PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size,
|
||||
FunctionInfo* function_info);
|
||||
GuestFunction* function_info);
|
||||
uint32_t PlaceData(const void* data, size_t length);
|
||||
|
||||
FunctionInfo* LookupFunction(uint64_t host_pc) override;
|
||||
GuestFunction* LookupFunction(uint64_t host_pc) override;
|
||||
|
||||
protected:
|
||||
// All executable code falls within 0x80000000 to 0x9FFFFFFF, so we can
|
||||
@@ -111,7 +111,7 @@ class X64CodeCache : public CodeCache {
|
||||
// Sorted map by host PC base offsets to source function info.
|
||||
// This can be used to bsearch on host PC to find the guest function.
|
||||
// The key is [start address | end address].
|
||||
std::vector<std::pair<uint64_t, FunctionInfo*>> generated_code_map_;
|
||||
std::vector<std::pair<uint64_t, GuestFunction*>> generated_code_map_;
|
||||
};
|
||||
|
||||
} // namespace x64
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/cpu/function.h"
|
||||
|
||||
// When enabled, this will use Windows 8 APIs to get unwind info.
|
||||
// TODO(benvanik): figure out why the callback variant doesn't work.
|
||||
|
||||
@@ -27,8 +27,9 @@
|
||||
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
|
||||
#include "xenia/cpu/cpu_flags.h"
|
||||
#include "xenia/cpu/debug_info.h"
|
||||
#include "xenia/cpu/function.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
#include "xenia/cpu/symbol_info.h"
|
||||
#include "xenia/cpu/symbol.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
#include "xenia/debug/debugger.h"
|
||||
#include "xenia/profiling.h"
|
||||
@@ -87,7 +88,7 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
|
||||
|
||||
X64Emitter::~X64Emitter() = default;
|
||||
|
||||
bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
|
||||
bool X64Emitter::Emit(GuestFunction* function, HIRBuilder* builder,
|
||||
uint32_t debug_info_flags, DebugInfo* debug_info,
|
||||
void*& out_code_address, size_t& out_code_size,
|
||||
std::vector<SourceMapEntry>& out_source_map) {
|
||||
@@ -96,6 +97,7 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
|
||||
// Reset.
|
||||
debug_info_ = debug_info;
|
||||
debug_info_flags_ = debug_info_flags;
|
||||
trace_data_ = &function->trace_data();
|
||||
source_map_arena_.Reset();
|
||||
|
||||
// Fill the generator with code.
|
||||
@@ -106,7 +108,7 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
|
||||
|
||||
// Copy the final code to the cache and relocate it.
|
||||
out_code_size = getSize();
|
||||
out_code_address = Emplace(stack_size, function_info);
|
||||
out_code_address = Emplace(stack_size, function);
|
||||
|
||||
// Stash source map.
|
||||
source_map_arena_.CloneContents(out_source_map);
|
||||
@@ -114,16 +116,16 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
|
||||
return true;
|
||||
}
|
||||
|
||||
void* X64Emitter::Emplace(size_t stack_size, FunctionInfo* function_info) {
|
||||
void* X64Emitter::Emplace(size_t stack_size, GuestFunction* function) {
|
||||
// To avoid changing xbyak, we do a switcharoo here.
|
||||
// top_ points to the Xbyak buffer, and since we are in AutoGrow mode
|
||||
// it has pending relocations. We copy the top_ to our buffer, swap the
|
||||
// pointer, relocate, then return the original scratch pointer for use.
|
||||
uint8_t* old_address = top_;
|
||||
void* new_address;
|
||||
if (function_info) {
|
||||
new_address = code_cache_->PlaceGuestCode(function_info->address(), top_,
|
||||
size_, stack_size, function_info);
|
||||
if (function) {
|
||||
new_address = code_cache_->PlaceGuestCode(function->address(), top_, size_,
|
||||
stack_size, function);
|
||||
} else {
|
||||
new_address = code_cache_->PlaceHostCode(0, top_, size_, stack_size);
|
||||
}
|
||||
@@ -177,8 +179,8 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
||||
// Safe now to do some tracing.
|
||||
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctions) {
|
||||
// We require 32-bit addresses.
|
||||
assert_true(uint64_t(debug_info_->trace_data().header()) < UINT_MAX);
|
||||
auto trace_header = debug_info_->trace_data().header();
|
||||
assert_true(uint64_t(trace_data_->header()) < UINT_MAX);
|
||||
auto trace_header = trace_data_->header();
|
||||
|
||||
// Call count.
|
||||
lock();
|
||||
@@ -265,11 +267,10 @@ void X64Emitter::MarkSourceOffset(const Instr* i) {
|
||||
}
|
||||
|
||||
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) {
|
||||
auto trace_data = debug_info_->trace_data();
|
||||
uint32_t instruction_index =
|
||||
(entry->source_offset - trace_data.start_address()) / 4;
|
||||
(entry->source_offset - trace_data_->start_address()) / 4;
|
||||
lock();
|
||||
inc(qword[low_address(trace_data.instruction_execute_counts() +
|
||||
inc(qword[low_address(trace_data_->instruction_execute_counts() +
|
||||
instruction_index * 8)]);
|
||||
}
|
||||
}
|
||||
@@ -341,8 +342,7 @@ extern "C" uint64_t ResolveFunction(void* raw_context,
|
||||
// TODO(benvanik): required?
|
||||
assert_not_zero(target_address);
|
||||
|
||||
Function* fn = NULL;
|
||||
thread_state->processor()->ResolveFunction(target_address, &fn);
|
||||
auto fn = thread_state->processor()->ResolveFunction(target_address);
|
||||
assert_not_null(fn);
|
||||
auto x64_fn = static_cast<X64Function*>(fn);
|
||||
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
|
||||
@@ -350,11 +350,11 @@ extern "C" uint64_t ResolveFunction(void* raw_context,
|
||||
return addr;
|
||||
}
|
||||
|
||||
void X64Emitter::Call(const hir::Instr* instr, FunctionInfo* symbol_info) {
|
||||
assert_not_null(symbol_info);
|
||||
auto fn = reinterpret_cast<X64Function*>(symbol_info->function());
|
||||
void X64Emitter::Call(const hir::Instr* instr, GuestFunction* function) {
|
||||
assert_not_null(function);
|
||||
auto fn = static_cast<X64Function*>(function);
|
||||
// Resolve address to the function to call and store in rax.
|
||||
if (fn) {
|
||||
if (fn->machine_code()) {
|
||||
// TODO(benvanik): is it worth it to do this? It removes the need for
|
||||
// a ResolveFunction call, but makes the table less useful.
|
||||
assert_zero(uint64_t(fn->machine_code()) & 0xFFFFFFFF00000000);
|
||||
@@ -363,7 +363,7 @@ void X64Emitter::Call(const hir::Instr* instr, FunctionInfo* symbol_info) {
|
||||
// Load the pointer to the indirection table maintained in X64CodeCache.
|
||||
// The target dword will either contain the address of the generated code
|
||||
// or a thunk to ResolveAddress.
|
||||
mov(ebx, symbol_info->address());
|
||||
mov(ebx, function->address());
|
||||
mov(eax, dword[ebx]);
|
||||
}
|
||||
|
||||
@@ -418,43 +418,50 @@ void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t UndefinedCallExtern(void* raw_context, uint64_t symbol_info_ptr) {
|
||||
auto symbol_info = reinterpret_cast<FunctionInfo*>(symbol_info_ptr);
|
||||
XELOGW("undefined extern call to %.8X %s", symbol_info->address(),
|
||||
symbol_info->name().c_str());
|
||||
uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) {
|
||||
auto function = reinterpret_cast<Function*>(function_ptr);
|
||||
XELOGW("undefined extern call to %.8X %s", function->address(),
|
||||
function->name().c_str());
|
||||
return 0;
|
||||
}
|
||||
void X64Emitter::CallExtern(const hir::Instr* instr,
|
||||
const FunctionInfo* symbol_info) {
|
||||
if (symbol_info->behavior() == FunctionBehavior::kBuiltin &&
|
||||
symbol_info->builtin_handler()) {
|
||||
// rcx = context
|
||||
// rdx = target host function
|
||||
// r8 = arg0
|
||||
// r9 = arg1
|
||||
mov(rdx, reinterpret_cast<uint64_t>(symbol_info->builtin_handler()));
|
||||
mov(r8, reinterpret_cast<uint64_t>(symbol_info->builtin_arg0()));
|
||||
mov(r9, reinterpret_cast<uint64_t>(symbol_info->builtin_arg1()));
|
||||
auto thunk = backend()->guest_to_host_thunk();
|
||||
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
||||
call(rax);
|
||||
ReloadECX();
|
||||
ReloadEDX();
|
||||
// rax = host return
|
||||
} else if (symbol_info->behavior() == FunctionBehavior::kExtern &&
|
||||
symbol_info->extern_handler()) {
|
||||
// rcx = context
|
||||
// rdx = target host function
|
||||
mov(rdx, reinterpret_cast<uint64_t>(symbol_info->extern_handler()));
|
||||
mov(r8, qword[rcx + offsetof(cpu::frontend::PPCContext, kernel_state)]);
|
||||
auto thunk = backend()->guest_to_host_thunk();
|
||||
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
||||
call(rax);
|
||||
ReloadECX();
|
||||
ReloadEDX();
|
||||
// rax = host return
|
||||
} else {
|
||||
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(symbol_info));
|
||||
void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
|
||||
bool undefined = true;
|
||||
if (function->behavior() == Function::Behavior::kBuiltin) {
|
||||
auto builtin_function = static_cast<const BuiltinFunction*>(function);
|
||||
if (builtin_function->handler()) {
|
||||
undefined = false;
|
||||
// rcx = context
|
||||
// rdx = target host function
|
||||
// r8 = arg0
|
||||
// r9 = arg1
|
||||
mov(rdx, reinterpret_cast<uint64_t>(builtin_function->handler()));
|
||||
mov(r8, reinterpret_cast<uint64_t>(builtin_function->arg0()));
|
||||
mov(r9, reinterpret_cast<uint64_t>(builtin_function->arg1()));
|
||||
auto thunk = backend()->guest_to_host_thunk();
|
||||
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
||||
call(rax);
|
||||
ReloadECX();
|
||||
ReloadEDX();
|
||||
// rax = host return
|
||||
}
|
||||
} else if (function->behavior() == Function::Behavior::kExtern) {
|
||||
auto extern_function = static_cast<const GuestFunction*>(function);
|
||||
if (extern_function->extern_handler()) {
|
||||
undefined = false;
|
||||
// rcx = context
|
||||
// rdx = target host function
|
||||
mov(rdx, reinterpret_cast<uint64_t>(extern_function->extern_handler()));
|
||||
mov(r8, qword[rcx + offsetof(cpu::frontend::PPCContext, kernel_state)]);
|
||||
auto thunk = backend()->guest_to_host_thunk();
|
||||
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
||||
call(rax);
|
||||
ReloadECX();
|
||||
ReloadEDX();
|
||||
// rax = host return
|
||||
}
|
||||
}
|
||||
if (undefined) {
|
||||
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(function));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "xenia/cpu/hir/hir_builder.h"
|
||||
#include "xenia/cpu/hir/instr.h"
|
||||
#include "xenia/cpu/hir/value.h"
|
||||
#include "xenia/cpu/symbol_info.h"
|
||||
#include "xenia/debug/function_trace_data.h"
|
||||
#include "xenia/memory.h"
|
||||
|
||||
@@ -27,10 +26,7 @@
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class DebugInfo;
|
||||
class FunctionInfo;
|
||||
class Processor;
|
||||
class SymbolInfo;
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
@@ -118,7 +114,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
Processor* processor() const { return processor_; }
|
||||
X64Backend* backend() const { return backend_; }
|
||||
|
||||
bool Emit(FunctionInfo* function_info, hir::HIRBuilder* builder,
|
||||
bool Emit(GuestFunction* function, hir::HIRBuilder* builder,
|
||||
uint32_t debug_info_flags, DebugInfo* debug_info,
|
||||
void*& out_code_address, size_t& out_code_size,
|
||||
std::vector<SourceMapEntry>& out_source_map);
|
||||
@@ -163,9 +159,9 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
void Trap(uint16_t trap_type = 0);
|
||||
void UnimplementedInstr(const hir::Instr* i);
|
||||
|
||||
void Call(const hir::Instr* instr, FunctionInfo* symbol_info);
|
||||
void Call(const hir::Instr* instr, GuestFunction* function);
|
||||
void CallIndirect(const hir::Instr* instr, const Xbyak::Reg64& reg);
|
||||
void CallExtern(const hir::Instr* instr, const FunctionInfo* symbol_info);
|
||||
void CallExtern(const hir::Instr* instr, const Function* function);
|
||||
void CallNative(void* fn);
|
||||
void CallNative(uint64_t (*fn)(void* raw_context));
|
||||
void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0));
|
||||
@@ -199,7 +195,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
size_t stack_size() const { return stack_size_; }
|
||||
|
||||
protected:
|
||||
void* Emplace(size_t stack_size, FunctionInfo* function_info = nullptr);
|
||||
void* Emplace(size_t stack_size, GuestFunction* function = nullptr);
|
||||
bool Emit(hir::HIRBuilder* builder, size_t& out_stack_size);
|
||||
void EmitGetCurrentThreadId();
|
||||
void EmitTraceUserCallReturn();
|
||||
@@ -218,6 +214,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
|
||||
DebugInfo* debug_info_ = nullptr;
|
||||
uint32_t debug_info_flags_ = 0;
|
||||
debug::FunctionTraceData* trace_data_ = nullptr;
|
||||
Arena source_map_arena_;
|
||||
|
||||
size_t stack_size_ = 0;
|
||||
|
||||
@@ -18,8 +18,8 @@ namespace cpu {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
X64Function::X64Function(FunctionInfo* symbol_info)
|
||||
: Function(symbol_info), machine_code_(nullptr), machine_code_length_(0) {}
|
||||
X64Function::X64Function(Module* module, uint32_t address)
|
||||
: GuestFunction(module, address) {}
|
||||
|
||||
X64Function::~X64Function() {
|
||||
// machine_code_ is freed by code cache.
|
||||
@@ -30,14 +30,6 @@ void X64Function::Setup(uint8_t* machine_code, size_t machine_code_length) {
|
||||
machine_code_length_ = machine_code_length;
|
||||
}
|
||||
|
||||
bool X64Function::AddBreakpointImpl(debug::Breakpoint* breakpoint) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool X64Function::RemoveBreakpointImpl(debug::Breakpoint* breakpoint) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool X64Function::CallImpl(ThreadState* thread_state, uint32_t return_address) {
|
||||
auto backend =
|
||||
reinterpret_cast<X64Backend*>(thread_state->processor()->backend());
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#define XENIA_BACKEND_X64_X64_FUNCTION_H_
|
||||
|
||||
#include "xenia/cpu/function.h"
|
||||
#include "xenia/cpu/symbol_info.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
|
||||
namespace xe {
|
||||
@@ -19,10 +18,10 @@ namespace cpu {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
class X64Function : public Function {
|
||||
class X64Function : public GuestFunction {
|
||||
public:
|
||||
X64Function(FunctionInfo* symbol_info);
|
||||
virtual ~X64Function();
|
||||
X64Function(Module* module, uint32_t address);
|
||||
~X64Function() override;
|
||||
|
||||
uint8_t* machine_code() const override { return machine_code_; }
|
||||
size_t machine_code_length() const override { return machine_code_length_; }
|
||||
@@ -30,13 +29,11 @@ class X64Function : public Function {
|
||||
void Setup(uint8_t* machine_code, size_t machine_code_length);
|
||||
|
||||
protected:
|
||||
bool AddBreakpointImpl(debug::Breakpoint* breakpoint) override;
|
||||
bool RemoveBreakpointImpl(debug::Breakpoint* breakpoint) override;
|
||||
bool CallImpl(ThreadState* thread_state, uint32_t return_address) override;
|
||||
|
||||
private:
|
||||
uint8_t* machine_code_;
|
||||
size_t machine_code_length_;
|
||||
uint8_t* machine_code_ = nullptr;
|
||||
size_t machine_code_length_ = 0;
|
||||
};
|
||||
|
||||
} // namespace x64
|
||||
|
||||
@@ -154,7 +154,7 @@ struct OffsetOp : Op<OffsetOp, KEY_TYPE_O> {
|
||||
};
|
||||
|
||||
struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
|
||||
FunctionInfo* value;
|
||||
Function* value;
|
||||
|
||||
protected:
|
||||
template <typename T, KeyType KEY_TYPE>
|
||||
@@ -162,7 +162,7 @@ struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
|
||||
template <hir::Opcode OPCODE, typename... Ts>
|
||||
friend struct I;
|
||||
bool Load(const Instr::Op& op) {
|
||||
this->value = op.symbol_info;
|
||||
this->value = op.symbol;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -856,7 +856,8 @@ EMITTER_OPCODE_TABLE(OPCODE_TRAP_TRUE, TRAP_TRUE_I8, TRAP_TRUE_I16,
|
||||
// ============================================================================
|
||||
struct CALL : Sequence<CALL, I<OPCODE_CALL, VoidOp, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.Call(i.instr, i.src1.value);
|
||||
assert_true(i.src1.value->is_guest());
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src1.value));
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
|
||||
@@ -867,60 +868,66 @@ EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
|
||||
struct CALL_TRUE_I8
|
||||
: Sequence<CALL_TRUE_I8, I<OPCODE_CALL_TRUE, VoidOp, I8Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, i.src2.value);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_TRUE_I16
|
||||
: Sequence<CALL_TRUE_I16, I<OPCODE_CALL_TRUE, VoidOp, I16Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, i.src2.value);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_TRUE_I32
|
||||
: Sequence<CALL_TRUE_I32, I<OPCODE_CALL_TRUE, VoidOp, I32Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, i.src2.value);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_TRUE_I64
|
||||
: Sequence<CALL_TRUE_I64, I<OPCODE_CALL_TRUE, VoidOp, I64Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, i.src2.value);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_TRUE_F32
|
||||
: Sequence<CALL_TRUE_F32, I<OPCODE_CALL_TRUE, VoidOp, F32Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.vptest(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, i.src2.value);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_TRUE_F64
|
||||
: Sequence<CALL_TRUE_F64, I<OPCODE_CALL_TRUE, VoidOp, F64Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.vptest(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, i.src2.value);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user