Adding --trace_branches.
Simple output right now, can always be enhanced.
This commit is contained in:
parent
4495637616
commit
d6f2a0b3f0
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
|
|
||||||
DECLARE_bool(trace_instructions);
|
DECLARE_bool(trace_instructions);
|
||||||
|
DECLARE_bool(trace_branches);
|
||||||
DECLARE_bool(trace_user_calls);
|
DECLARE_bool(trace_user_calls);
|
||||||
DECLARE_bool(trace_kernel_calls);
|
DECLARE_bool(trace_kernel_calls);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
// Tracing:
|
// Tracing:
|
||||||
DEFINE_bool(trace_instructions, false,
|
DEFINE_bool(trace_instructions, false,
|
||||||
"Trace all instructions.");
|
"Trace all instructions.");
|
||||||
|
DEFINE_bool(trace_branches, false,
|
||||||
|
"Trace all branches.");
|
||||||
DEFINE_bool(trace_user_calls, false,
|
DEFINE_bool(trace_user_calls, false,
|
||||||
"Trace all user function calls.");
|
"Trace all user function calls.");
|
||||||
DEFINE_bool(trace_kernel_calls, false,
|
DEFINE_bool(trace_kernel_calls, false,
|
||||||
|
|
|
@ -82,6 +82,20 @@ void _cdecl XeTraceUserCall(
|
||||||
(uint32_t)call_ia - 4, (uint32_t)cia, fn->name());
|
(uint32_t)call_ia - 4, (uint32_t)cia, fn->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _cdecl XeTraceBranch(
|
||||||
|
xe_ppc_state_t* state, uint64_t cia, uint64_t target_ia) {
|
||||||
|
switch (target_ia) {
|
||||||
|
case kXEPPCRegLR:
|
||||||
|
target_ia = state->lr;
|
||||||
|
break;
|
||||||
|
case kXEPPCRegCTR:
|
||||||
|
target_ia = state->ctr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
XELOGCPU("TRACE: %.8X -> b.%.8X",
|
||||||
|
(uint32_t)cia, (uint32_t)target_ia);
|
||||||
|
}
|
||||||
|
|
||||||
void _cdecl XeTraceInstruction(
|
void _cdecl XeTraceInstruction(
|
||||||
xe_ppc_state_t* state, uint64_t cia, uint64_t data) {
|
xe_ppc_state_t* state, uint64_t cia, uint64_t data) {
|
||||||
ppc::InstrType* type = ppc::GetInstrType((uint32_t)data);
|
ppc::InstrType* type = ppc::GetInstrType((uint32_t)data);
|
||||||
|
@ -108,5 +122,6 @@ void xe::cpu::GetGlobalExports(GlobalExports* global_exports) {
|
||||||
global_exports->XeAccessViolation = XeAccessViolation;
|
global_exports->XeAccessViolation = XeAccessViolation;
|
||||||
global_exports->XeTraceKernelCall = XeTraceKernelCall;
|
global_exports->XeTraceKernelCall = XeTraceKernelCall;
|
||||||
global_exports->XeTraceUserCall = XeTraceUserCall;
|
global_exports->XeTraceUserCall = XeTraceUserCall;
|
||||||
|
global_exports->XeTraceBranch = XeTraceBranch;
|
||||||
global_exports->XeTraceInstruction = XeTraceInstruction;
|
global_exports->XeTraceInstruction = XeTraceInstruction;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,8 @@ typedef struct {
|
||||||
void (_cdecl *XeTraceUserCall)(
|
void (_cdecl *XeTraceUserCall)(
|
||||||
xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||||
sdb::FunctionSymbol* fn);
|
sdb::FunctionSymbol* fn);
|
||||||
|
void (_cdecl *XeTraceBranch)(
|
||||||
|
xe_ppc_state_t* state, uint64_t cia, uint64_t target_ia);
|
||||||
void (_cdecl *XeTraceInstruction)(
|
void (_cdecl *XeTraceInstruction)(
|
||||||
xe_ppc_state_t* state, uint64_t cia, uint64_t data);
|
xe_ppc_state_t* state, uint64_t cia, uint64_t data);
|
||||||
} GlobalExports;
|
} GlobalExports;
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#include <xenia/cpu/libjit/libjit_emit.h>
|
#include <xenia/cpu/libjit/libjit_emit.h>
|
||||||
|
|
||||||
|
#include <xenia/cpu/cpu-private.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace xe::cpu;
|
using namespace xe::cpu;
|
||||||
using namespace xe::cpu::ppc;
|
using namespace xe::cpu::ppc;
|
||||||
|
@ -61,15 +63,37 @@ int XeEmitIndirectBranchTo(
|
||||||
|
|
||||||
int XeEmitBranchTo(
|
int XeEmitBranchTo(
|
||||||
LibjitEmitter& e, jit_function_t f, const char* src, uint32_t cia,
|
LibjitEmitter& e, jit_function_t f, const char* src, uint32_t cia,
|
||||||
bool lk) {
|
bool lk, jit_value_t condition) {
|
||||||
// Get the basic block and switch behavior based on outgoing type.
|
|
||||||
FunctionBlock* fn_block = e.fn_block();
|
FunctionBlock* fn_block = e.fn_block();
|
||||||
|
|
||||||
|
// Fast-path for branches to other blocks.
|
||||||
|
// Only valid when not tracing branches.
|
||||||
|
if (!FLAGS_trace_branches &&
|
||||||
|
fn_block->outgoing_type == FunctionBlock::kTargetBlock) {
|
||||||
|
e.branch_to_block_if(fn_block->outgoing_address, condition);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only branch of conditionals when we have one.
|
||||||
|
jit_label_t post_jump_label = jit_label_undefined;
|
||||||
|
if (condition) {
|
||||||
|
// TODO(benvanik): add debug info for this?
|
||||||
|
// char name[32];
|
||||||
|
// xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address);
|
||||||
|
jit_insn_branch_if_not(f, condition, &post_jump_label);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FLAGS_trace_branches) {
|
||||||
|
e.TraceBranch(cia);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the basic block and switch behavior based on outgoing type.
|
||||||
|
int result = 0;
|
||||||
switch (fn_block->outgoing_type) {
|
switch (fn_block->outgoing_type) {
|
||||||
case FunctionBlock::kTargetBlock:
|
case FunctionBlock::kTargetBlock:
|
||||||
{
|
// Taken care of above usually.
|
||||||
e.branch_to_block(fn_block->outgoing_address);
|
e.branch_to_block(fn_block->outgoing_address);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case FunctionBlock::kTargetFunction:
|
case FunctionBlock::kTargetFunction:
|
||||||
{
|
{
|
||||||
// Spill all registers to memory.
|
// Spill all registers to memory.
|
||||||
|
@ -103,20 +127,28 @@ int XeEmitBranchTo(
|
||||||
{
|
{
|
||||||
// An indirect jump.
|
// An indirect jump.
|
||||||
printf("INDIRECT JUMP VIA LR: %.8X\n", cia);
|
printf("INDIRECT JUMP VIA LR: %.8X\n", cia);
|
||||||
return XeEmitIndirectBranchTo(e, f, src, cia, lk, kXEPPCRegLR);
|
result = XeEmitIndirectBranchTo(e, f, src, cia, lk, kXEPPCRegLR);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case FunctionBlock::kTargetCTR:
|
case FunctionBlock::kTargetCTR:
|
||||||
{
|
{
|
||||||
// An indirect jump.
|
// An indirect jump.
|
||||||
printf("INDIRECT JUMP VIA CTR: %.8X\n", cia);
|
printf("INDIRECT JUMP VIA CTR: %.8X\n", cia);
|
||||||
return XeEmitIndirectBranchTo(e, f, src, cia, lk, kXEPPCRegCTR);
|
result = XeEmitIndirectBranchTo(e, f, src, cia, lk, kXEPPCRegCTR);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
case FunctionBlock::kTargetNone:
|
case FunctionBlock::kTargetNone:
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
return 1;
|
result = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
if (condition) {
|
||||||
|
jit_insn_label(f, &post_jump_label);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,7 +170,7 @@ XEEMITTER(bx, 0x48000000, I )(LibjitEmitter& e, jit_function_t f, Ins
|
||||||
e.update_lr_value(e.get_uint64(i.address + 4));
|
e.update_lr_value(e.get_uint64(i.address + 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
return XeEmitBranchTo(e, f, "bx", i.address, i.I.LK);
|
return XeEmitBranchTo(e, f, "bx", i.address, i.I.LK, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(bcx, 0x40000000, B )(LibjitEmitter& e, jit_function_t f, InstrData& i) {
|
XEEMITTER(bcx, 0x40000000, B )(LibjitEmitter& e, jit_function_t f, InstrData& i) {
|
||||||
|
@ -204,34 +236,14 @@ XEEMITTER(bcx, 0x40000000, B )(LibjitEmitter& e, jit_function_t f, Ins
|
||||||
ok = cond_ok;
|
ok = cond_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization: if a branch target, inline the conditional branch here.
|
uint32_t nia;
|
||||||
FunctionBlock* fn_block = e.fn_block();
|
if (i.B.AA) {
|
||||||
if (fn_block->outgoing_type == FunctionBlock::kTargetBlock) {
|
nia = XEEXTS26(i.B.BD << 2);
|
||||||
e.branch_to_block_if(fn_block->outgoing_address, ok);
|
|
||||||
} else {
|
} else {
|
||||||
// Only branch of conditionals when we have one.
|
nia = i.address + XEEXTS26(i.B.BD << 2);
|
||||||
jit_label_t post_jump_label = jit_label_undefined;
|
}
|
||||||
if (ok) {
|
if (XeEmitBranchTo(e, f, "bcx", i.address, i.B.LK, ok)) {
|
||||||
// TODO(benvanik): add debug info for this?
|
return 1;
|
||||||
// char name[32];
|
|
||||||
// xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address);
|
|
||||||
jit_insn_branch_if_not(f, ok, &post_jump_label);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that this occurs entirely within the branch true block.
|
|
||||||
uint32_t nia;
|
|
||||||
if (i.B.AA) {
|
|
||||||
nia = XEEXTS26(i.B.BD << 2);
|
|
||||||
} else {
|
|
||||||
nia = i.address + XEEXTS26(i.B.BD << 2);
|
|
||||||
}
|
|
||||||
if (XeEmitBranchTo(e, f, "bcx", i.address, i.B.LK)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
jit_insn_label(f, &post_jump_label);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -273,28 +285,8 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(LibjitEmitter& e, jit_function_t f, Ins
|
||||||
ok = cond_ok;
|
ok = cond_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization: if a branch target, inline the conditional branch here.
|
if (XeEmitBranchTo(e, f, "bcctrx", i.address, i.XL.LK, ok)) {
|
||||||
FunctionBlock* fn_block = e.fn_block();
|
return 1;
|
||||||
if (fn_block->outgoing_type == FunctionBlock::kTargetBlock) {
|
|
||||||
e.branch_to_block_if(fn_block->outgoing_address, ok);
|
|
||||||
} else {
|
|
||||||
// Only branch of conditionals when we have one.
|
|
||||||
jit_label_t post_jump_label = jit_label_undefined;
|
|
||||||
if (ok) {
|
|
||||||
// TODO(benvanik): add debug info for this?
|
|
||||||
// char name[32];
|
|
||||||
// xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address);
|
|
||||||
jit_insn_branch_if_not(f, ok, &post_jump_label);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that this occurs entirely within the branch true block.
|
|
||||||
if (XeEmitBranchTo(e, f, "bcctrx", i.address, i.XL.LK)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
jit_insn_label(f, &post_jump_label);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -359,28 +351,8 @@ XEEMITTER(bclrx, 0x4C000020, XL )(LibjitEmitter& e, jit_function_t f, Ins
|
||||||
ok = cond_ok;
|
ok = cond_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization: if a branch target, inline the conditional branch here.
|
if (XeEmitBranchTo(e, f, "bclrx", i.address, i.XL.LK, ok)) {
|
||||||
FunctionBlock* fn_block = e.fn_block();
|
return 1;
|
||||||
if (fn_block->outgoing_type == FunctionBlock::kTargetBlock) {
|
|
||||||
e.branch_to_block_if(fn_block->outgoing_address, ok);
|
|
||||||
} else {
|
|
||||||
// Only branch of conditionals when we have one.
|
|
||||||
jit_label_t post_jump_label = jit_label_undefined;
|
|
||||||
if (ok) {
|
|
||||||
// TODO(benvanik): add debug info for this?
|
|
||||||
// char name[32];
|
|
||||||
// xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address);
|
|
||||||
jit_insn_branch_if_not(f, ok, &post_jump_label);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that this occurs entirely within the branch true block.
|
|
||||||
if (XeEmitBranchTo(e, f, "bclrx", i.address, i.XL.LK)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
jit_insn_label(f, &post_jump_label);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -751,6 +751,44 @@ int LibjitEmitter::call_function(FunctionSymbol* target_symbol,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LibjitEmitter::TraceBranch(uint32_t cia) {
|
||||||
|
SpillRegisters();
|
||||||
|
|
||||||
|
// Pick target. If it's an indirection the tracing function will handle it.
|
||||||
|
uint64_t target = 0;
|
||||||
|
switch (fn_block_->outgoing_type) {
|
||||||
|
case FunctionBlock::kTargetBlock:
|
||||||
|
target = fn_block_->outgoing_address;
|
||||||
|
break;
|
||||||
|
case FunctionBlock::kTargetFunction:
|
||||||
|
target = fn_block_->outgoing_function->start_address;
|
||||||
|
break;
|
||||||
|
case FunctionBlock::kTargetLR:
|
||||||
|
target = kXEPPCRegLR;
|
||||||
|
break;
|
||||||
|
case FunctionBlock::kTargetCTR:
|
||||||
|
target = kXEPPCRegCTR;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case FunctionBlock::kTargetNone:
|
||||||
|
XEASSERTALWAYS();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
jit_value_t trace_args[] = {
|
||||||
|
jit_value_get_param(fn_, 0),
|
||||||
|
jit_value_create_long_constant(fn_, jit_type_ulong, cia),
|
||||||
|
jit_value_create_long_constant(fn_, jit_type_ulong, target),
|
||||||
|
};
|
||||||
|
jit_insn_call_native(
|
||||||
|
fn_,
|
||||||
|
"XeTraceBranch",
|
||||||
|
global_exports_.XeTraceBranch,
|
||||||
|
global_export_signature_3_,
|
||||||
|
trace_args, XECOUNT(trace_args),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
int LibjitEmitter::GenerateIndirectionBranch(uint32_t cia, jit_value_t target,
|
int LibjitEmitter::GenerateIndirectionBranch(uint32_t cia, jit_value_t target,
|
||||||
bool lk, bool likely_local) {
|
bool lk, bool likely_local) {
|
||||||
// This function is called by the control emitters when they know that an
|
// This function is called by the control emitters when they know that an
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
int call_function(sdb::FunctionSymbol* target_symbol, jit_value_t lr,
|
int call_function(sdb::FunctionSymbol* target_symbol, jit_value_t lr,
|
||||||
bool tail);
|
bool tail);
|
||||||
|
|
||||||
|
void TraceBranch(uint32_t cia);
|
||||||
int GenerateIndirectionBranch(uint32_t cia, jit_value_t target,
|
int GenerateIndirectionBranch(uint32_t cia, jit_value_t target,
|
||||||
bool lk, bool likely_local);
|
bool lk, bool likely_local);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue