Reconcile debugger and save state stuff into a single implementation.
Fixes #497 and fixes #496. Still rough edges, but at least less duplication.
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class DebugInfo;
|
||||
class FunctionDebugInfo;
|
||||
class GuestFunction;
|
||||
namespace hir {
|
||||
class HIRBuilder;
|
||||
@@ -39,7 +39,7 @@ class Assembler {
|
||||
|
||||
virtual bool Assemble(GuestFunction* function, hir::HIRBuilder* builder,
|
||||
uint32_t debug_info_flags,
|
||||
std::unique_ptr<DebugInfo> debug_info) = 0;
|
||||
std::unique_ptr<FunctionDebugInfo> debug_info) = 0;
|
||||
|
||||
protected:
|
||||
Backend* backend_;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/cpu/backend/machine_info.h"
|
||||
#include "xenia/cpu/thread_debug_info.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
@@ -53,11 +54,15 @@ class Backend {
|
||||
virtual std::unique_ptr<GuestFunction> CreateGuestFunction(
|
||||
Module* module, uint32_t address) = 0;
|
||||
|
||||
virtual bool InstallBreakpoint(Breakpoint* bp) { return false; }
|
||||
virtual bool InstallBreakpoint(Breakpoint* bp, Function* func) {
|
||||
return false;
|
||||
}
|
||||
virtual bool UninstallBreakpoint(Breakpoint* bp) { return false; }
|
||||
// Calculates the next host instruction based on the current thread state and
|
||||
// current PC. This will look for branches and other control flow
|
||||
// instructions.
|
||||
virtual uint64_t CalculateNextHostInstruction(ThreadDebugInfo* thread_info,
|
||||
uint64_t current_pc) = 0;
|
||||
|
||||
virtual void InstallBreakpoint(Breakpoint* breakpoint) {}
|
||||
virtual void InstallBreakpoint(Breakpoint* breakpoint, Function* fn) {}
|
||||
virtual void UninstallBreakpoint(Breakpoint* breakpoint) {}
|
||||
|
||||
protected:
|
||||
Processor* processor_;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/base/reset_scope.h"
|
||||
#include "xenia/cpu/backend/x64/x64_backend.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/cpu_flags.h"
|
||||
@@ -67,7 +68,7 @@ void X64Assembler::Reset() {
|
||||
|
||||
bool X64Assembler::Assemble(GuestFunction* function, HIRBuilder* builder,
|
||||
uint32_t debug_info_flags,
|
||||
std::unique_ptr<DebugInfo> debug_info) {
|
||||
std::unique_ptr<FunctionDebugInfo> debug_info) {
|
||||
SCOPE_profile_cpu_f("cpu");
|
||||
|
||||
// Reset when we leave.
|
||||
@@ -89,18 +90,17 @@ bool X64Assembler::Assemble(GuestFunction* function, HIRBuilder* builder,
|
||||
string_buffer_.Reset();
|
||||
}
|
||||
|
||||
// Dump debug data.
|
||||
if (FLAGS_disassemble_functions) {
|
||||
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmSource) {
|
||||
// auto fn_data = backend_->processor()->debugger()->AllocateFunctionData(
|
||||
// xe::debug::FunctionDisasmData::SizeOfHeader());
|
||||
}
|
||||
}
|
||||
|
||||
function->set_debug_info(std::move(debug_info));
|
||||
static_cast<X64Function*>(function)->Setup(
|
||||
reinterpret_cast<uint8_t*>(machine_code), code_size);
|
||||
|
||||
// Install into indirection table.
|
||||
uint64_t host_address = reinterpret_cast<uint64_t>(machine_code);
|
||||
assert_true((host_address >> 32) == 0);
|
||||
reinterpret_cast<X64CodeCache*>(backend_->code_cache())
|
||||
->AddIndirection(function->address(),
|
||||
static_cast<uint32_t>(host_address));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class X64Assembler : public Assembler {
|
||||
|
||||
bool Assemble(GuestFunction* function, hir::HIRBuilder* builder,
|
||||
uint32_t debug_info_flags,
|
||||
std::unique_ptr<DebugInfo> debug_info) override;
|
||||
std::unique_ptr<FunctionDebugInfo> debug_info) override;
|
||||
|
||||
private:
|
||||
void DumpMachineCode(void* machine_code, size_t code_size,
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "xenia/cpu/backend/x64/x64_backend.h"
|
||||
|
||||
#include "third_party/capstone/include/capstone.h"
|
||||
#include "third_party/capstone/include/x86.h"
|
||||
#include "xenia/base/exception_handler.h"
|
||||
#include "xenia/cpu/backend/x64/x64_assembler.h"
|
||||
#include "xenia/cpu/backend/x64/x64_code_cache.h"
|
||||
@@ -39,13 +41,23 @@ class X64ThunkEmitter : public X64Emitter {
|
||||
};
|
||||
|
||||
X64Backend::X64Backend(Processor* processor)
|
||||
: Backend(processor), code_cache_(nullptr), emitter_data_(0) {}
|
||||
: Backend(processor), code_cache_(nullptr), emitter_data_(0) {
|
||||
if (cs_open(CS_ARCH_X86, CS_MODE_64, &capstone_handle_) != CS_ERR_OK) {
|
||||
assert_always("Failed to initialize capstone");
|
||||
}
|
||||
cs_option(capstone_handle_, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL);
|
||||
cs_option(capstone_handle_, CS_OPT_DETAIL, CS_OPT_ON);
|
||||
cs_option(capstone_handle_, CS_OPT_SKIPDATA, CS_OPT_OFF);
|
||||
}
|
||||
|
||||
X64Backend::~X64Backend() {
|
||||
if (emitter_data_) {
|
||||
processor()->memory()->SystemHeapFree(emitter_data_);
|
||||
emitter_data_ = 0;
|
||||
}
|
||||
if (capstone_handle_) {
|
||||
cs_close(&capstone_handle_);
|
||||
}
|
||||
|
||||
ExceptionHandler::Uninstall(&ExceptionCallbackThunk, this);
|
||||
}
|
||||
@@ -124,59 +136,227 @@ std::unique_ptr<GuestFunction> X64Backend::CreateGuestFunction(
|
||||
return std::make_unique<X64Function>(module, address);
|
||||
}
|
||||
|
||||
bool X64Backend::InstallBreakpoint(Breakpoint* bp) {
|
||||
auto functions = processor()->FindFunctionsWithAddress(bp->address());
|
||||
if (functions.empty()) {
|
||||
// Go ahead and fail - let the caller handle this.
|
||||
return false;
|
||||
uint64_t ReadCapstoneReg(X64Context* context, x86_reg reg) {
|
||||
switch (reg) {
|
||||
case X86_REG_RAX:
|
||||
return context->rax;
|
||||
case X86_REG_RCX:
|
||||
return context->rcx;
|
||||
case X86_REG_RDX:
|
||||
return context->rdx;
|
||||
case X86_REG_RBX:
|
||||
return context->rbx;
|
||||
case X86_REG_RSP:
|
||||
return context->rsp;
|
||||
case X86_REG_RBP:
|
||||
return context->rbp;
|
||||
case X86_REG_RSI:
|
||||
return context->rsi;
|
||||
case X86_REG_RDI:
|
||||
return context->rdi;
|
||||
case X86_REG_R8:
|
||||
return context->r8;
|
||||
case X86_REG_R9:
|
||||
return context->r9;
|
||||
case X86_REG_R10:
|
||||
return context->r10;
|
||||
case X86_REG_R11:
|
||||
return context->r11;
|
||||
case X86_REG_R12:
|
||||
return context->r12;
|
||||
case X86_REG_R13:
|
||||
return context->r13;
|
||||
case X86_REG_R14:
|
||||
return context->r14;
|
||||
case X86_REG_R15:
|
||||
return context->r15;
|
||||
default:
|
||||
assert_unhandled_case(reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (auto function : functions) {
|
||||
assert_true(function->is_guest());
|
||||
auto guest_function = reinterpret_cast<cpu::GuestFunction*>(function);
|
||||
auto code = guest_function->MapGuestAddressToMachineCode(bp->address());
|
||||
if (!code) {
|
||||
// This should not happen.
|
||||
assert_always();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto orig_bytes =
|
||||
xe::load_and_swap<uint16_t>(reinterpret_cast<void*>(code + 0x0));
|
||||
bp->backend_data().push_back({code, orig_bytes});
|
||||
|
||||
xe::store_and_swap<uint16_t>(reinterpret_cast<void*>(code + 0x0), 0x0F0C);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool X64Backend::InstallBreakpoint(Breakpoint* bp, Function* func) {
|
||||
assert_true(func->is_guest());
|
||||
auto guest_function = reinterpret_cast<cpu::GuestFunction*>(func);
|
||||
auto code = guest_function->MapGuestAddressToMachineCode(bp->address());
|
||||
if (!code) {
|
||||
#define X86_EFLAGS_CF 0x00000001 // Carry Flag
|
||||
#define X86_EFLAGS_PF 0x00000004 // Parity Flag
|
||||
#define X86_EFLAGS_ZF 0x00000040 // Zero Flag
|
||||
#define X86_EFLAGS_SF 0x00000080 // Sign Flag
|
||||
#define X86_EFLAGS_OF 0x00000800 // Overflow Flag
|
||||
bool TestCapstoneEflags(uint32_t eflags, uint32_t insn) {
|
||||
// http://www.felixcloutier.com/x86/Jcc.html
|
||||
switch (insn) {
|
||||
case X86_INS_JAE:
|
||||
// CF=0 && ZF=0
|
||||
return ((eflags & X86_EFLAGS_CF) == 0) && ((eflags & X86_EFLAGS_ZF) == 0);
|
||||
case X86_INS_JA:
|
||||
// CF=0
|
||||
return (eflags & X86_EFLAGS_CF) == 0;
|
||||
case X86_INS_JBE:
|
||||
// CF=1 || ZF=1
|
||||
return ((eflags & X86_EFLAGS_CF) == X86_EFLAGS_CF) ||
|
||||
((eflags & X86_EFLAGS_ZF) == X86_EFLAGS_ZF);
|
||||
case X86_INS_JB:
|
||||
// CF=1
|
||||
return (eflags & X86_EFLAGS_CF) == X86_EFLAGS_CF;
|
||||
case X86_INS_JE:
|
||||
// ZF=1
|
||||
return (eflags & X86_EFLAGS_ZF) == X86_EFLAGS_ZF;
|
||||
case X86_INS_JGE:
|
||||
// SF=OF
|
||||
return (eflags & X86_EFLAGS_SF) == (eflags & X86_EFLAGS_OF);
|
||||
case X86_INS_JG:
|
||||
// ZF=0 && SF=OF
|
||||
return ((eflags & X86_EFLAGS_ZF) == 0) &&
|
||||
((eflags & X86_EFLAGS_SF) == (eflags & X86_EFLAGS_OF));
|
||||
case X86_INS_JLE:
|
||||
// ZF=1 || SF!=OF
|
||||
return ((eflags & X86_EFLAGS_ZF) == X86_EFLAGS_ZF) ||
|
||||
((eflags & X86_EFLAGS_SF) != X86_EFLAGS_OF);
|
||||
case X86_INS_JL:
|
||||
// SF!=OF
|
||||
return (eflags & X86_EFLAGS_SF) != (eflags & X86_EFLAGS_OF);
|
||||
case X86_INS_JNE:
|
||||
// ZF=0
|
||||
return (eflags & X86_EFLAGS_ZF) == 0;
|
||||
case X86_INS_JNO:
|
||||
// OF=0
|
||||
return (eflags & X86_EFLAGS_OF) == 0;
|
||||
case X86_INS_JNP:
|
||||
// PF=0
|
||||
return (eflags & X86_EFLAGS_PF) == 0;
|
||||
case X86_INS_JNS:
|
||||
// SF=0
|
||||
return (eflags & X86_EFLAGS_SF) == 0;
|
||||
case X86_INS_JO:
|
||||
// OF=1
|
||||
return (eflags & X86_EFLAGS_OF) == X86_EFLAGS_OF;
|
||||
case X86_INS_JP:
|
||||
// PF=1
|
||||
return (eflags & X86_EFLAGS_PF) == X86_EFLAGS_PF;
|
||||
case X86_INS_JS:
|
||||
// SF=1
|
||||
return (eflags & X86_EFLAGS_SF) == X86_EFLAGS_SF;
|
||||
default:
|
||||
assert_unhandled_case(insn);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t X64Backend::CalculateNextHostInstruction(ThreadDebugInfo* thread_info,
|
||||
uint64_t current_pc) {
|
||||
auto machine_code_ptr = reinterpret_cast<const uint8_t*>(current_pc);
|
||||
size_t remaining_machine_code_size = 64;
|
||||
uint64_t host_address = current_pc;
|
||||
cs_insn insn = {0};
|
||||
cs_detail all_detail = {0};
|
||||
insn.detail = &all_detail;
|
||||
cs_disasm_iter(capstone_handle_, &machine_code_ptr,
|
||||
&remaining_machine_code_size, &host_address, &insn);
|
||||
auto& detail = all_detail.x86;
|
||||
switch (insn.id) {
|
||||
default:
|
||||
// Not a branching instruction - just move over it.
|
||||
return current_pc + insn.size;
|
||||
case X86_INS_CALL: {
|
||||
assert_true(detail.op_count == 1);
|
||||
assert_true(detail.operands[0].type == X86_OP_REG);
|
||||
uint64_t target_pc =
|
||||
ReadCapstoneReg(&thread_info->host_context, detail.operands[0].reg);
|
||||
return target_pc;
|
||||
} break;
|
||||
case X86_INS_RET: {
|
||||
assert_zero(detail.op_count);
|
||||
auto stack_ptr =
|
||||
reinterpret_cast<uint64_t*>(thread_info->host_context.rsp);
|
||||
uint64_t target_pc = stack_ptr[0];
|
||||
return target_pc;
|
||||
} break;
|
||||
case X86_INS_JMP: {
|
||||
assert_true(detail.op_count == 1);
|
||||
if (detail.operands[0].type == X86_OP_IMM) {
|
||||
uint64_t target_pc = static_cast<uint64_t>(detail.operands[0].imm);
|
||||
return target_pc;
|
||||
} else if (detail.operands[0].type == X86_OP_REG) {
|
||||
uint64_t target_pc =
|
||||
ReadCapstoneReg(&thread_info->host_context, detail.operands[0].reg);
|
||||
return target_pc;
|
||||
} else {
|
||||
// TODO(benvanik): find some more uses of this.
|
||||
assert_always("jmp branch emulation not yet implemented");
|
||||
return current_pc + insn.size;
|
||||
}
|
||||
} break;
|
||||
case X86_INS_JCXZ:
|
||||
case X86_INS_JECXZ:
|
||||
case X86_INS_JRCXZ:
|
||||
assert_always("j*cxz branch emulation not yet implemented");
|
||||
return current_pc + insn.size;
|
||||
case X86_INS_JAE:
|
||||
case X86_INS_JA:
|
||||
case X86_INS_JBE:
|
||||
case X86_INS_JB:
|
||||
case X86_INS_JE:
|
||||
case X86_INS_JGE:
|
||||
case X86_INS_JG:
|
||||
case X86_INS_JLE:
|
||||
case X86_INS_JL:
|
||||
case X86_INS_JNE:
|
||||
case X86_INS_JNO:
|
||||
case X86_INS_JNP:
|
||||
case X86_INS_JNS:
|
||||
case X86_INS_JO:
|
||||
case X86_INS_JP:
|
||||
case X86_INS_JS: {
|
||||
assert_true(detail.op_count == 1);
|
||||
assert_true(detail.operands[0].type == X86_OP_IMM);
|
||||
uint64_t target_pc = static_cast<uint64_t>(detail.operands[0].imm);
|
||||
bool test_passed =
|
||||
TestCapstoneEflags(thread_info->host_context.eflags, insn.id);
|
||||
if (test_passed) {
|
||||
return target_pc;
|
||||
} else {
|
||||
return current_pc + insn.size;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void X64Backend::InstallBreakpoint(Breakpoint* breakpoint) {
|
||||
breakpoint->ForEachHostAddress([breakpoint](uint64_t host_address) {
|
||||
auto ptr = reinterpret_cast<void*>(host_address);
|
||||
auto original_bytes = xe::load_and_swap<uint16_t>(ptr);
|
||||
assert_true(original_bytes != 0x0F0B);
|
||||
xe::store_and_swap<uint16_t>(ptr, 0x0F0B);
|
||||
breakpoint->backend_data().emplace_back(host_address, original_bytes);
|
||||
});
|
||||
}
|
||||
|
||||
void X64Backend::InstallBreakpoint(Breakpoint* breakpoint, Function* fn) {
|
||||
assert_true(breakpoint->address_type() == Breakpoint::AddressType::kGuest);
|
||||
assert_true(fn->is_guest());
|
||||
auto guest_function = reinterpret_cast<cpu::GuestFunction*>(fn);
|
||||
auto host_address =
|
||||
guest_function->MapGuestAddressToMachineCode(breakpoint->guest_address());
|
||||
if (!host_address) {
|
||||
assert_always();
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Assume we haven't already installed a breakpoint in this spot.
|
||||
auto orig_bytes =
|
||||
xe::load_and_swap<uint16_t>(reinterpret_cast<void*>(code + 0x0));
|
||||
bp->backend_data().push_back({code, orig_bytes});
|
||||
|
||||
xe::store_and_swap<uint16_t>(reinterpret_cast<void*>(code + 0x0), 0x0F0C);
|
||||
return true;
|
||||
auto ptr = reinterpret_cast<void*>(host_address);
|
||||
auto original_bytes = xe::load_and_swap<uint16_t>(ptr);
|
||||
assert_true(original_bytes != 0x0F0B);
|
||||
xe::store_and_swap<uint16_t>(ptr, 0x0F0B);
|
||||
breakpoint->backend_data().emplace_back(host_address, original_bytes);
|
||||
}
|
||||
|
||||
bool X64Backend::UninstallBreakpoint(Breakpoint* bp) {
|
||||
for (auto& pair : bp->backend_data()) {
|
||||
xe::store_and_swap<uint16_t>(reinterpret_cast<void*>(pair.first),
|
||||
uint16_t(pair.second));
|
||||
void X64Backend::UninstallBreakpoint(Breakpoint* breakpoint) {
|
||||
for (auto& pair : breakpoint->backend_data()) {
|
||||
auto ptr = reinterpret_cast<uint8_t*>(pair.first);
|
||||
auto instruction_bytes = xe::load_and_swap<uint16_t>(ptr);
|
||||
assert_true(instruction_bytes == 0x0F0B);
|
||||
xe::store_and_swap<uint16_t>(ptr, static_cast<uint16_t>(pair.second));
|
||||
}
|
||||
|
||||
bp->backend_data().clear();
|
||||
return true;
|
||||
breakpoint->backend_data().clear();
|
||||
}
|
||||
|
||||
bool X64Backend::ExceptionCallbackThunk(Exception* ex, void* data) {
|
||||
@@ -186,39 +366,22 @@ bool X64Backend::ExceptionCallbackThunk(Exception* ex, void* data) {
|
||||
|
||||
bool X64Backend::ExceptionCallback(Exception* ex) {
|
||||
if (ex->code() != Exception::Code::kIllegalInstruction) {
|
||||
// Has nothing to do with breakpoints. Not ours.
|
||||
// We only care about illegal instructions. Other things will be handled by
|
||||
// other handlers (probably). If nothing else picks it up we'll be called
|
||||
// with OnUnhandledException to do real crash handling.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify an expected illegal instruction.
|
||||
auto instruction_bytes =
|
||||
xe::load_and_swap<uint16_t>(reinterpret_cast<void*>(ex->pc()));
|
||||
if (instruction_bytes != 0x0F0C) {
|
||||
// Not a BP instruction - not ours.
|
||||
if (instruction_bytes != 0x0F0B) {
|
||||
// Not our ud2 - not us.
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t host_pcs[64];
|
||||
cpu::StackFrame frames[64];
|
||||
size_t count = processor()->stack_walker()->CaptureStackTrace(
|
||||
host_pcs, 0, xe::countof(host_pcs));
|
||||
processor()->stack_walker()->ResolveStack(host_pcs, frames, count);
|
||||
if (count == 0) {
|
||||
// Stack resolve failed.
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (frames[i].type != cpu::StackFrame::Type::kGuest) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (processor()->BreakpointHit(frames[i].guest_pc, frames[i].host_pc)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No breakpoints found at this address.
|
||||
return false;
|
||||
// Let the processor handle things.
|
||||
return processor()->OnThreadBreakpointHit(ex);
|
||||
}
|
||||
|
||||
X64ThunkEmitter::X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator)
|
||||
|
||||
@@ -62,14 +62,19 @@ class X64Backend : public Backend {
|
||||
std::unique_ptr<GuestFunction> CreateGuestFunction(Module* module,
|
||||
uint32_t address) override;
|
||||
|
||||
bool InstallBreakpoint(Breakpoint* bp) override;
|
||||
bool InstallBreakpoint(Breakpoint* bp, Function* func) override;
|
||||
bool UninstallBreakpoint(Breakpoint* bp) override;
|
||||
uint64_t CalculateNextHostInstruction(ThreadDebugInfo* thread_info,
|
||||
uint64_t current_pc) override;
|
||||
|
||||
void InstallBreakpoint(Breakpoint* breakpoint) override;
|
||||
void InstallBreakpoint(Breakpoint* breakpoint, Function* fn) override;
|
||||
void UninstallBreakpoint(Breakpoint* breakpoint) override;
|
||||
|
||||
private:
|
||||
static bool ExceptionCallbackThunk(Exception* ex, void* data);
|
||||
bool ExceptionCallback(Exception* ex);
|
||||
|
||||
uintptr_t capstone_handle_ = 0;
|
||||
|
||||
std::unique_ptr<X64CodeCache> code_cache_;
|
||||
|
||||
uint32_t emitter_data_;
|
||||
|
||||
@@ -27,12 +27,11 @@
|
||||
#include "xenia/cpu/backend/x64/x64_sequences.h"
|
||||
#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/function_debug_info.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
#include "xenia/cpu/symbol.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
#include "xenia/debug/debugger.h"
|
||||
|
||||
DEFINE_bool(enable_debugprint_log, false,
|
||||
"Log debugprint traps to the active debugger");
|
||||
@@ -89,7 +88,7 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
|
||||
X64Emitter::~X64Emitter() = default;
|
||||
|
||||
bool X64Emitter::Emit(GuestFunction* function, HIRBuilder* builder,
|
||||
uint32_t debug_info_flags, DebugInfo* debug_info,
|
||||
uint32_t debug_info_flags, FunctionDebugInfo* debug_info,
|
||||
void** out_code_address, size_t* out_code_size,
|
||||
std::vector<SourceMapEntry>* out_source_map) {
|
||||
SCOPE_profile_cpu_f("cpu");
|
||||
@@ -187,7 +186,7 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
|
||||
inc(qword[low_address(&trace_header->function_call_count)]);
|
||||
|
||||
// Get call history slot.
|
||||
static_assert(debug::FunctionTraceData::kFunctionCallerHistoryCount == 4,
|
||||
static_assert(FunctionTraceData::kFunctionCallerHistoryCount == 4,
|
||||
"bitmask depends on count");
|
||||
mov(rax, qword[low_address(&trace_header->function_call_count)]);
|
||||
and_(rax, 0b00000011);
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
|
||||
#include "xenia/base/arena.h"
|
||||
#include "xenia/cpu/function.h"
|
||||
#include "xenia/cpu/function_trace_data.h"
|
||||
#include "xenia/cpu/hir/hir_builder.h"
|
||||
#include "xenia/cpu/hir/instr.h"
|
||||
#include "xenia/cpu/hir/value.h"
|
||||
#include "xenia/debug/function_trace_data.h"
|
||||
#include "xenia/memory.h"
|
||||
|
||||
// NOTE: must be included last as it expects windows.h to already be included.
|
||||
@@ -115,7 +115,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
X64Backend* backend() const { return backend_; }
|
||||
|
||||
bool Emit(GuestFunction* function, hir::HIRBuilder* builder,
|
||||
uint32_t debug_info_flags, DebugInfo* debug_info,
|
||||
uint32_t debug_info_flags, FunctionDebugInfo* debug_info,
|
||||
void** out_code_address, size_t* out_code_size,
|
||||
std::vector<SourceMapEntry>* out_source_map);
|
||||
|
||||
@@ -190,7 +190,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
return (feature_flags_ & feature_flag) != 0;
|
||||
}
|
||||
|
||||
DebugInfo* debug_info() const { return debug_info_; }
|
||||
FunctionDebugInfo* debug_info() const { return debug_info_; }
|
||||
|
||||
size_t stack_size() const { return stack_size_; }
|
||||
|
||||
@@ -212,9 +212,9 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
|
||||
hir::Instr* current_instr_ = nullptr;
|
||||
|
||||
DebugInfo* debug_info_ = nullptr;
|
||||
FunctionDebugInfo* debug_info_ = nullptr;
|
||||
uint32_t debug_info_flags_ = 0;
|
||||
debug::FunctionTraceData* trace_data_ = nullptr;
|
||||
FunctionTraceData* trace_data_ = nullptr;
|
||||
Arena source_map_arena_;
|
||||
|
||||
size_t stack_size_ = 0;
|
||||
|
||||
Reference in New Issue
Block a user