diff --git a/src/xenia/cpu/cpu-private.h b/src/xenia/cpu/cpu-private.h index d9966c5ec..5ca558aa7 100644 --- a/src/xenia/cpu/cpu-private.h +++ b/src/xenia/cpu/cpu-private.h @@ -14,6 +14,7 @@ DECLARE_bool(trace_instructions); +DECLARE_bool(trace_branches); DECLARE_bool(trace_user_calls); DECLARE_bool(trace_kernel_calls); diff --git a/src/xenia/cpu/cpu.cc b/src/xenia/cpu/cpu.cc index a633e7eb6..9e234e626 100644 --- a/src/xenia/cpu/cpu.cc +++ b/src/xenia/cpu/cpu.cc @@ -13,6 +13,8 @@ // Tracing: DEFINE_bool(trace_instructions, false, "Trace all instructions."); +DEFINE_bool(trace_branches, false, + "Trace all branches."); DEFINE_bool(trace_user_calls, false, "Trace all user function calls."); DEFINE_bool(trace_kernel_calls, false, diff --git a/src/xenia/cpu/global_exports.cc b/src/xenia/cpu/global_exports.cc index f494f45a5..b673dc953 100644 --- a/src/xenia/cpu/global_exports.cc +++ b/src/xenia/cpu/global_exports.cc @@ -82,6 +82,20 @@ void _cdecl XeTraceUserCall( (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( xe_ppc_state_t* state, uint64_t cia, uint64_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->XeTraceKernelCall = XeTraceKernelCall; global_exports->XeTraceUserCall = XeTraceUserCall; + global_exports->XeTraceBranch = XeTraceBranch; global_exports->XeTraceInstruction = XeTraceInstruction; } diff --git a/src/xenia/cpu/global_exports.h b/src/xenia/cpu/global_exports.h index 0a449df0c..561adec4b 100644 --- a/src/xenia/cpu/global_exports.h +++ b/src/xenia/cpu/global_exports.h @@ -37,6 +37,8 @@ typedef struct { void (_cdecl *XeTraceUserCall)( xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia, sdb::FunctionSymbol* fn); + void (_cdecl *XeTraceBranch)( + xe_ppc_state_t* state, uint64_t cia, uint64_t target_ia); void (_cdecl *XeTraceInstruction)( xe_ppc_state_t* state, uint64_t cia, uint64_t data); } GlobalExports; diff --git a/src/xenia/cpu/libjit/libjit_emit_control.cc b/src/xenia/cpu/libjit/libjit_emit_control.cc index 8e6924a00..4bb3333f7 100644 --- a/src/xenia/cpu/libjit/libjit_emit_control.cc +++ b/src/xenia/cpu/libjit/libjit_emit_control.cc @@ -9,6 +9,8 @@ #include +#include + using namespace xe::cpu; using namespace xe::cpu::ppc; @@ -61,15 +63,37 @@ int XeEmitIndirectBranchTo( int XeEmitBranchTo( LibjitEmitter& e, jit_function_t f, const char* src, uint32_t cia, - bool lk) { - // Get the basic block and switch behavior based on outgoing type. + bool lk, jit_value_t condition) { 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) { case FunctionBlock::kTargetBlock: - { + // Taken care of above usually. e.branch_to_block(fn_block->outgoing_address); break; - } case FunctionBlock::kTargetFunction: { // Spill all registers to memory. @@ -103,20 +127,28 @@ int XeEmitBranchTo( { // An indirect jump. 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: { // An indirect jump. 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: case FunctionBlock::kTargetNone: 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)); } - 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) { @@ -204,34 +236,14 @@ XEEMITTER(bcx, 0x40000000, B )(LibjitEmitter& e, jit_function_t f, Ins ok = cond_ok; } - // Optimization: if a branch target, inline the conditional branch here. - FunctionBlock* fn_block = e.fn_block(); - if (fn_block->outgoing_type == FunctionBlock::kTargetBlock) { - e.branch_to_block_if(fn_block->outgoing_address, ok); + uint32_t nia; + if (i.B.AA) { + nia = XEEXTS26(i.B.BD << 2); } 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. - 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); - } + nia = i.address + XEEXTS26(i.B.BD << 2); + } + if (XeEmitBranchTo(e, f, "bcx", i.address, i.B.LK, ok)) { + return 1; } return 0; @@ -273,28 +285,8 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(LibjitEmitter& e, jit_function_t f, Ins ok = cond_ok; } - // Optimization: if a branch target, inline the conditional branch here. - FunctionBlock* fn_block = e.fn_block(); - 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); - } + if (XeEmitBranchTo(e, f, "bcctrx", i.address, i.XL.LK, ok)) { + return 1; } return 0; @@ -359,28 +351,8 @@ XEEMITTER(bclrx, 0x4C000020, XL )(LibjitEmitter& e, jit_function_t f, Ins ok = cond_ok; } - // Optimization: if a branch target, inline the conditional branch here. - FunctionBlock* fn_block = e.fn_block(); - 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); - } + if (XeEmitBranchTo(e, f, "bclrx", i.address, i.XL.LK, ok)) { + return 1; } return 0; diff --git a/src/xenia/cpu/libjit/libjit_emitter.cc b/src/xenia/cpu/libjit/libjit_emitter.cc index 6dc540976..4932dc1ad 100644 --- a/src/xenia/cpu/libjit/libjit_emitter.cc +++ b/src/xenia/cpu/libjit/libjit_emitter.cc @@ -751,6 +751,44 @@ int LibjitEmitter::call_function(FunctionSymbol* target_symbol, 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, bool lk, bool likely_local) { // This function is called by the control emitters when they know that an diff --git a/src/xenia/cpu/libjit/libjit_emitter.h b/src/xenia/cpu/libjit/libjit_emitter.h index be94c273d..61a60578d 100644 --- a/src/xenia/cpu/libjit/libjit_emitter.h +++ b/src/xenia/cpu/libjit/libjit_emitter.h @@ -58,6 +58,7 @@ public: int call_function(sdb::FunctionSymbol* target_symbol, jit_value_t lr, bool tail); + void TraceBranch(uint32_t cia); int GenerateIndirectionBranch(uint32_t cia, jit_value_t target, bool lk, bool likely_local);