#include "IML.h" #include "IMLInstruction.h" #include "IMLSegment.h" #include "IMLRegisterAllocatorRanges.h" #include "util/helpers/StringBuf.h" #include "../PPCRecompiler.h" const char* IMLDebug_GetOpcodeName(const IMLInstruction* iml) { static char _tempOpcodename[32]; uint32 op = iml->operation; if (op == PPCREC_IML_OP_ASSIGN) return "MOV"; else if (op == PPCREC_IML_OP_ADD) return "ADD"; else if (op == PPCREC_IML_OP_ADD_WITH_CARRY) return "ADC"; else if (op == PPCREC_IML_OP_SUB) return "SUB"; else if (op == PPCREC_IML_OP_OR) return "OR"; else if (op == PPCREC_IML_OP_AND) return "AND"; else if (op == PPCREC_IML_OP_XOR) return "XOR"; else if (op == PPCREC_IML_OP_LEFT_SHIFT) return "LSH"; else if (op == PPCREC_IML_OP_RIGHT_SHIFT_U) return "RSH"; else if (op == PPCREC_IML_OP_RIGHT_SHIFT_S) return "ARSH"; else if (op == PPCREC_IML_OP_LEFT_ROTATE) return "LROT"; else if (op == PPCREC_IML_OP_MULTIPLY_SIGNED) return "MULS"; else if (op == PPCREC_IML_OP_DIVIDE_SIGNED) return "DIVS"; sprintf(_tempOpcodename, "OP0%02x_T%d", iml->operation, iml->type); return _tempOpcodename; } void IMLDebug_AppendRegisterParam(StringBuf& strOutput, sint32 virtualRegister, bool isLast = false) { if (isLast) { if (virtualRegister < 10) strOutput.addFmt("t{} ", virtualRegister); else strOutput.addFmt("t{}", virtualRegister); return; } if (virtualRegister < 10) strOutput.addFmt("t{} , ", virtualRegister); else strOutput.addFmt("t{}, ", virtualRegister); } void IMLDebug_AppendS32Param(StringBuf& strOutput, sint32 val, bool isLast = false) { if (isLast) { strOutput.addFmt("0x{:08x}", val); return; } strOutput.addFmt("0x{:08x}, ", val); } void IMLDebug_PrintLivenessRangeInfo(StringBuf& currentLineText, IMLSegment* imlSegment, sint32 offset) { // pad to 70 characters sint32 index = currentLineText.getLen(); while (index < 70) { debug_printf(" "); index++; } raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; while (subrangeItr) { if (offset == subrangeItr->start.index) { if (false)//subrange->isDirtied && i == subrange->becomesDirtyAtIndex.index) { debug_printf("*%-2d", subrangeItr->range->virtualRegister); } else { debug_printf("|%-2d", subrangeItr->range->virtualRegister); } } else if (false)//subrange->isDirtied && i == subrange->becomesDirtyAtIndex.index ) { debug_printf("* "); } else if (offset >= subrangeItr->start.index && offset < subrangeItr->end.index) { debug_printf("| "); } else { debug_printf(" "); } index += 3; // next subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; } } std::string IMLDebug_GetSegmentName(ppcImlGenContext_t* ctx, IMLSegment* seg) { if (!ctx) { return ""; } // find segment index for (size_t i = 0; i < ctx->segmentList2.size(); i++) { if (ctx->segmentList2[i] == seg) { return fmt::format("Seg{:04x}", i); } } return ""; } std::string IMLDebug_GetConditionName(IMLCondition cond) { switch (cond) { case IMLCondition::EQ: return "EQ"; case IMLCondition::NEQ: return "NEQ"; case IMLCondition::UNSIGNED_GT: return "UGT"; case IMLCondition::UNSIGNED_LT: return "ULT"; case IMLCondition::SIGNED_GT: return "SGT"; case IMLCondition::SIGNED_LT: return "SLT"; default: cemu_assert_unimplemented(); } return "ukn"; } void IMLDebug_DumpSegment(ppcImlGenContext_t* ctx, IMLSegment* imlSegment, bool printLivenessRangeInfo) { StringBuf strOutput(1024); strOutput.addFmt("SEGMENT {} | PPC=0x{:08x} Loop-depth {}", IMLDebug_GetSegmentName(ctx, imlSegment), imlSegment->ppcAddress, imlSegment->loopDepth); if (imlSegment->isEnterable) { strOutput.addFmt(" ENTERABLE (0x{:08x})", imlSegment->enterPPCAddress); } //else if (imlSegment->isJumpDestination) //{ // strOutput.addFmt(" JUMP-DEST (0x{:08x})", imlSegment->jumpDestinationPPCAddress); //} debug_printf("%s\n", strOutput.c_str()); //strOutput.reset(); //strOutput.addFmt("SEGMENT NAME 0x{:016x}", (uintptr_t)imlSegment); //debug_printf("%s", strOutput.c_str()); if (printLivenessRangeInfo) { strOutput.reset(); IMLDebug_PrintLivenessRangeInfo(strOutput, imlSegment, RA_INTER_RANGE_START); debug_printf("%s\n", strOutput.c_str()); } //debug_printf("\n"); strOutput.reset(); sint32 lineOffsetParameters = 18; for (sint32 i = 0; i < imlSegment->imlList.size(); i++) { const IMLInstruction& inst = imlSegment->imlList[i]; // don't log NOP instructions if (inst.type == PPCREC_IML_TYPE_NO_OP) continue; strOutput.reset(); strOutput.addFmt("{:02x} ", i); if (inst.type == PPCREC_IML_TYPE_R_NAME || inst.type == PPCREC_IML_TYPE_NAME_R) { if (inst.type == PPCREC_IML_TYPE_R_NAME) strOutput.add("LD_NAME"); else strOutput.add("ST_NAME"); while ((sint32)strOutput.getLen() < lineOffsetParameters) strOutput.add(" "); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_name.registerIndex); strOutput.addFmt("name_{} (", inst.op_r_name.registerIndex, inst.op_r_name.name); if (inst.op_r_name.name >= PPCREC_NAME_R0 && inst.op_r_name.name < (PPCREC_NAME_R0 + 999)) { strOutput.addFmt("r{}", inst.op_r_name.name - PPCREC_NAME_R0); } else if (inst.op_r_name.name >= PPCREC_NAME_SPR0 && inst.op_r_name.name < (PPCREC_NAME_SPR0 + 999)) { strOutput.addFmt("spr{}", inst.op_r_name.name - PPCREC_NAME_SPR0); } else strOutput.add("ukn"); strOutput.add(")"); } else if (inst.type == PPCREC_IML_TYPE_R_R) { strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); while ((sint32)strOutput.getLen() < lineOffsetParameters) strOutput.add(" "); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r.registerResult); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r.registerA, true); if (inst.crRegister != PPC_REC_INVALID_REGISTER) { strOutput.addFmt(" -> CR{}", inst.crRegister); } } else if (inst.type == PPCREC_IML_TYPE_R_R_R) { strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); while ((sint32)strOutput.getLen() < lineOffsetParameters) strOutput.add(" "); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r.registerResult); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r.registerA); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r.registerB, true); if (inst.crRegister != PPC_REC_INVALID_REGISTER) { strOutput.addFmt(" -> CR{}", inst.crRegister); } } else if (inst.type == PPCREC_IML_TYPE_R_R_R_CARRY) { strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); while ((sint32)strOutput.getLen() < lineOffsetParameters) strOutput.add(" "); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r_carry.regR); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r_carry.regA); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r_carry.regB); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r_carry.regCarry, true); } else if (inst.type == PPCREC_IML_TYPE_COMPARE) { strOutput.add("CMP "); while ((sint32)strOutput.getLen() < lineOffsetParameters) strOutput.add(" "); IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.registerOperandA); IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.registerOperandB); strOutput.addFmt(", {}", IMLDebug_GetConditionName(inst.op_compare.cond)); strOutput.add(" -> "); IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.registerResult, true); } else if (inst.type == PPCREC_IML_TYPE_COMPARE_S32) { strOutput.add("CMP "); while ((sint32)strOutput.getLen() < lineOffsetParameters) strOutput.add(" "); IMLDebug_AppendRegisterParam(strOutput, inst.op_compare_s32.registerOperandA); strOutput.addFmt("{}", inst.op_compare_s32.immS32); strOutput.addFmt(", {}", IMLDebug_GetConditionName(inst.op_compare_s32.cond)); strOutput.add(" -> "); IMLDebug_AppendRegisterParam(strOutput, inst.op_compare_s32.registerResult, true); } else if (inst.type == PPCREC_IML_TYPE_CONDITIONAL_JUMP) { strOutput.add("CJUMP2 "); while ((sint32)strOutput.getLen() < lineOffsetParameters) strOutput.add(" "); IMLDebug_AppendRegisterParam(strOutput, inst.op_conditionalJump2.registerBool, true); if(!inst.op_conditionalJump2.mustBeTrue) strOutput.add("(inverted)"); } else if (inst.type == PPCREC_IML_TYPE_R_R_S32) { strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); while ((sint32)strOutput.getLen() < lineOffsetParameters) strOutput.add(" "); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_s32.registerResult); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_s32.registerA); IMLDebug_AppendS32Param(strOutput, inst.op_r_r_s32.immS32, true); if (inst.crRegister != PPC_REC_INVALID_REGISTER) { strOutput.addFmt(" -> CR{}", inst.crRegister); } } else if (inst.type == PPCREC_IML_TYPE_R_R_S32_CARRY) { strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); while ((sint32)strOutput.getLen() < lineOffsetParameters) strOutput.add(" "); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_s32_carry.regR); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_s32_carry.regA); IMLDebug_AppendS32Param(strOutput, inst.op_r_r_s32_carry.immS32); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_s32_carry.regCarry, true); } else if (inst.type == PPCREC_IML_TYPE_R_S32) { strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); while ((sint32)strOutput.getLen() < lineOffsetParameters) strOutput.add(" "); IMLDebug_AppendRegisterParam(strOutput, inst.op_r_immS32.registerIndex); IMLDebug_AppendS32Param(strOutput, inst.op_r_immS32.immS32, true); if (inst.crRegister != PPC_REC_INVALID_REGISTER) { strOutput.addFmt(" -> CR{}", inst.crRegister); } } else if (inst.type == PPCREC_IML_TYPE_LOAD || inst.type == PPCREC_IML_TYPE_STORE || inst.type == PPCREC_IML_TYPE_LOAD_INDEXED || inst.type == PPCREC_IML_TYPE_STORE_INDEXED) { if (inst.type == PPCREC_IML_TYPE_LOAD || inst.type == PPCREC_IML_TYPE_LOAD_INDEXED) strOutput.add("LD_"); else strOutput.add("ST_"); if (inst.op_storeLoad.flags2.signExtend) strOutput.add("S"); else strOutput.add("U"); strOutput.addFmt("{}", inst.op_storeLoad.copyWidth); while ((sint32)strOutput.getLen() < lineOffsetParameters) strOutput.add(" "); IMLDebug_AppendRegisterParam(strOutput, inst.op_storeLoad.registerData); if (inst.type == PPCREC_IML_TYPE_LOAD_INDEXED || inst.type == PPCREC_IML_TYPE_STORE_INDEXED) strOutput.addFmt("[t{}+t{}]", inst.op_storeLoad.registerMem, inst.op_storeLoad.registerMem2); else strOutput.addFmt("[t{}+{}]", inst.op_storeLoad.registerMem, inst.op_storeLoad.immS32); } else if (inst.type == PPCREC_IML_TYPE_CJUMP) { if (inst.op_conditionalJump.condition == PPCREC_JUMP_CONDITION_E) strOutput.add("JE"); else if (inst.op_conditionalJump.condition == PPCREC_JUMP_CONDITION_NE) strOutput.add("JNE"); else if (inst.op_conditionalJump.condition == PPCREC_JUMP_CONDITION_G) strOutput.add("JG"); else if (inst.op_conditionalJump.condition == PPCREC_JUMP_CONDITION_GE) strOutput.add("JGE"); else if (inst.op_conditionalJump.condition == PPCREC_JUMP_CONDITION_L) strOutput.add("JL"); else if (inst.op_conditionalJump.condition == PPCREC_JUMP_CONDITION_LE) strOutput.add("JLE"); else if (inst.op_conditionalJump.condition == PPCREC_JUMP_CONDITION_NONE) strOutput.add("JALW"); // jump always else cemu_assert_unimplemented(); strOutput.addFmt(" (cr{})", inst.crRegister); } else if (inst.type == PPCREC_IML_TYPE_NO_OP) { strOutput.add("NOP"); } else if (inst.type == PPCREC_IML_TYPE_MACRO) { if (inst.operation == PPCREC_IML_MACRO_B_TO_REG) { strOutput.addFmt("MACRO B_TO_REG t{}", inst.op_macro.param); } else if (inst.operation == PPCREC_IML_MACRO_BL) { strOutput.addFmt("MACRO BL 0x{:08x} -> 0x{:08x} cycles (depr): {}", inst.op_macro.param, inst.op_macro.param2, (sint32)inst.op_macro.paramU16); } else if (inst.operation == PPCREC_IML_MACRO_B_FAR) { strOutput.addFmt("MACRO B_FAR 0x{:08x} -> 0x{:08x} cycles (depr): {}", inst.op_macro.param, inst.op_macro.param2, (sint32)inst.op_macro.paramU16); } else if (inst.operation == PPCREC_IML_MACRO_LEAVE) { strOutput.addFmt("MACRO LEAVE ppc: 0x{:08x}", inst.op_macro.param); } else if (inst.operation == PPCREC_IML_MACRO_HLE) { strOutput.addFmt("MACRO HLE ppcAddr: 0x{:08x} funcId: 0x{:08x}", inst.op_macro.param, inst.op_macro.param2); } else if (inst.operation == PPCREC_IML_MACRO_MFTB) { strOutput.addFmt("MACRO MFTB ppcAddr: 0x{:08x} sprId: 0x{:08x}", inst.op_macro.param, inst.op_macro.param2); } else if (inst.operation == PPCREC_IML_MACRO_COUNT_CYCLES) { strOutput.addFmt("MACRO COUNT_CYCLES cycles: {}", inst.op_macro.param); } else { strOutput.addFmt("MACRO ukn operation {}", inst.operation); } } else if (inst.type == PPCREC_IML_TYPE_FPR_R_NAME) { strOutput.addFmt("fpr_t{} = name_{} (", inst.op_r_name.registerIndex, inst.op_r_name.name); if (inst.op_r_name.name >= PPCREC_NAME_FPR0 && inst.op_r_name.name < (PPCREC_NAME_FPR0 + 999)) { strOutput.addFmt("fpr{}", inst.op_r_name.name - PPCREC_NAME_FPR0); } else if (inst.op_r_name.name >= PPCREC_NAME_TEMPORARY_FPR0 && inst.op_r_name.name < (PPCREC_NAME_TEMPORARY_FPR0 + 999)) { strOutput.addFmt("tempFpr{}", inst.op_r_name.name - PPCREC_NAME_TEMPORARY_FPR0); } else strOutput.add("ukn"); strOutput.add(")"); } else if (inst.type == PPCREC_IML_TYPE_FPR_NAME_R) { strOutput.addFmt("name_{} (", inst.op_r_name.name); if (inst.op_r_name.name >= PPCREC_NAME_FPR0 && inst.op_r_name.name < (PPCREC_NAME_FPR0 + 999)) { strOutput.addFmt("fpr{}", inst.op_r_name.name - PPCREC_NAME_FPR0); } else if (inst.op_r_name.name >= PPCREC_NAME_TEMPORARY_FPR0 && inst.op_r_name.name < (PPCREC_NAME_TEMPORARY_FPR0 + 999)) { strOutput.addFmt("tempFpr{}", inst.op_r_name.name - PPCREC_NAME_TEMPORARY_FPR0); } else strOutput.add("ukn"); strOutput.addFmt(") = fpr_t{}", inst.op_r_name.registerIndex); } else if (inst.type == PPCREC_IML_TYPE_FPR_LOAD) { strOutput.addFmt("fpr_t{} = ", inst.op_storeLoad.registerData); if (inst.op_storeLoad.flags2.signExtend) strOutput.add("S"); else strOutput.add("U"); strOutput.addFmt("{} [t{}+{}] mode {}", inst.op_storeLoad.copyWidth / 8, inst.op_storeLoad.registerMem, inst.op_storeLoad.immS32, inst.op_storeLoad.mode); if (inst.op_storeLoad.flags2.notExpanded) { strOutput.addFmt(" "); } } else if (inst.type == PPCREC_IML_TYPE_FPR_STORE) { if (inst.op_storeLoad.flags2.signExtend) strOutput.add("S"); else strOutput.add("U"); strOutput.addFmt("{} [t{}+{}]", inst.op_storeLoad.copyWidth / 8, inst.op_storeLoad.registerMem, inst.op_storeLoad.immS32); strOutput.addFmt(" = fpr_t{} mode {}", inst.op_storeLoad.registerData, inst.op_storeLoad.mode); } else if (inst.type == PPCREC_IML_TYPE_FPR_R_R) { strOutput.addFmt("{:>6} ", IMLDebug_GetOpcodeName(&inst)); strOutput.addFmt("fpr{:02}, fpr{:02}", inst.op_fpr_r_r.registerResult, inst.op_fpr_r_r.registerOperand); } else if (inst.type == PPCREC_IML_TYPE_FPR_R_R_R_R) { strOutput.addFmt("{:>6} ", IMLDebug_GetOpcodeName(&inst)); strOutput.addFmt("fpr{:02}, fpr{:02}, fpr{:02}, fpr{:02}", inst.op_fpr_r_r_r_r.registerResult, inst.op_fpr_r_r_r_r.registerOperandA, inst.op_fpr_r_r_r_r.registerOperandB, inst.op_fpr_r_r_r_r.registerOperandC); } else if (inst.type == PPCREC_IML_TYPE_FPR_R_R_R) { strOutput.addFmt("{:>6} ", IMLDebug_GetOpcodeName(&inst)); strOutput.addFmt("fpr{:02}, fpr{:02}, fpr{:02}", inst.op_fpr_r_r_r.registerResult, inst.op_fpr_r_r_r.registerOperandA, inst.op_fpr_r_r_r.registerOperandB); } else if (inst.type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) { strOutput.addFmt("CYCLE_CHECK"); } else if (inst.type == PPCREC_IML_TYPE_CONDITIONAL_R_S32) { strOutput.addFmt("t{} ", inst.op_conditional_r_s32.registerIndex); bool displayAsHex = false; if (inst.operation == PPCREC_IML_OP_ASSIGN) { displayAsHex = true; strOutput.add("="); } else strOutput.addFmt("(unknown operation CONDITIONAL_R_S32 {})", inst.operation); if (displayAsHex) strOutput.addFmt(" 0x{:x}", inst.op_conditional_r_s32.immS32); else strOutput.addFmt(" {}", inst.op_conditional_r_s32.immS32); strOutput.add(" (conditional)"); if (inst.crRegister != PPC_REC_INVALID_REGISTER) { strOutput.addFmt(" -> and update CR{}", inst.crRegister); } } else { strOutput.addFmt("Unknown iml type {}", inst.type); } debug_printf("%s", strOutput.c_str()); if (printLivenessRangeInfo) { IMLDebug_PrintLivenessRangeInfo(strOutput, imlSegment, i); } debug_printf("\n"); } // all ranges if (printLivenessRangeInfo) { debug_printf("Ranges-VirtReg "); raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; while (subrangeItr) { debug_printf("v%-2d", subrangeItr->range->virtualRegister); subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; } debug_printf("\n"); debug_printf("Ranges-PhysReg "); subrangeItr = imlSegment->raInfo.linkedList_allSubranges; while (subrangeItr) { debug_printf("p%-2d", subrangeItr->range->physicalRegister); subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; } debug_printf("\n"); } // branch info debug_printf("Links from: "); for (sint32 i = 0; i < imlSegment->list_prevSegments.size(); i++) { if (i) debug_printf(", "); debug_printf("%s", IMLDebug_GetSegmentName(ctx, imlSegment->list_prevSegments[i]).c_str()); } debug_printf("\n"); if (imlSegment->nextSegmentBranchNotTaken) debug_printf("BranchNotTaken: %s\n", IMLDebug_GetSegmentName(ctx, imlSegment->nextSegmentBranchNotTaken).c_str()); if (imlSegment->nextSegmentBranchTaken) debug_printf("BranchTaken: %s\n", IMLDebug_GetSegmentName(ctx, imlSegment->nextSegmentBranchTaken).c_str()); if (imlSegment->nextSegmentIsUncertain) debug_printf("Dynamic target\n"); debug_printf("\n"); } void IMLDebug_Dump(ppcImlGenContext_t* ppcImlGenContext, bool printLivenessRangeInfo) { for (size_t i = 0; i < ppcImlGenContext->segmentList2.size(); i++) { IMLDebug_DumpSegment(ppcImlGenContext, ppcImlGenContext->segmentList2[i], printLivenessRangeInfo); debug_printf("\n"); } }