Adding --trace_branches.

Simple output right now, can always be enhanced.
This commit is contained in:
Ben Vanik 2013-05-23 01:31:41 -07:00
parent 4495637616
commit d6f2a0b3f0
7 changed files with 111 additions and 80 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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;
} }

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);