// Copyright 2008 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #pragma once #include #include #include #include #include #include "Common/BitSet.h" #include "Common/CommonTypes.h" #include "Core/PowerPC/PPCTables.h" class PPCSymbolDB; struct Symbol; namespace PPCAnalyst { struct CodeOp //16B { UGeckoInstruction inst; GekkoOPInfo * opinfo; u32 address; u32 branchTo; //if 0, not a branch int branchToIndex; //index of target block BitSet32 regsOut; BitSet32 regsIn; BitSet32 fregsIn; s8 fregOut; bool isBranchTarget; bool wantsCR0; bool wantsCR1; bool wantsFPRF; bool wantsCA; bool wantsCAInFlags; bool outputCR0; bool outputCR1; bool outputFPRF; bool outputCA; bool canEndBlock; bool skip; // followed BL-s for example // which registers are still needed after this instruction in this block BitSet32 fprInUse; BitSet32 gprInUse; // just because a register is in use doesn't mean we actually need or want it in an x86 register. BitSet32 gprInReg; // we do double stores from GPRs, so we don't want to load a PowerPC floating point register into // an XMM only to move it again to a GPR afterwards. BitSet32 fprInXmm; // whether an fpr is known to be an actual single-precision value at this point in the block. BitSet32 fprIsSingle; // whether an fpr is known to have identical top and bottom halves (e.g. due to a single instruction) BitSet32 fprIsDuplicated; // whether an fpr is the output of a single-precision arithmetic instruction, i.e. whether we can safely // skip PPC_FP. BitSet32 fprIsStoreSafe; }; struct BlockStats { bool isFirstBlockOfFunction; bool isLastBlockOfFunction; int numCycles; }; struct BlockRegStats { short firstRead[32]; short firstWrite[32]; short lastRead[32]; short lastWrite[32]; short numReads[32]; short numWrites[32]; bool any; bool anyTimer; int GetTotalNumAccesses(int reg) const { return numReads[reg] + numWrites[reg]; } int GetUseRange(int reg) const { return std::max(lastRead[reg], lastWrite[reg]) - std::min(firstRead[reg], firstWrite[reg]); } bool IsUsed(int reg) const { return (numReads[reg] + numWrites[reg]) > 0; } void SetInputRegister(int reg, short opindex) { if (firstRead[reg] == -1) firstRead[reg] = opindex; lastRead[reg] = opindex; numReads[reg]++; } void SetOutputRegister(int reg, short opindex) { if (firstWrite[reg] == -1) firstWrite[reg] = opindex; lastWrite[reg] = opindex; numWrites[reg]++; } void Clear() { for (int i = 0; i < 32; ++i) { firstRead[i] = -1; firstWrite[i] = -1; numReads[i] = 0; numWrites[i] = 0; } } }; class CodeBuffer { public: CodeBuffer(int size); ~CodeBuffer(); int GetSize() const { return size_; } PPCAnalyst::CodeOp *codebuffer; private: int size_; }; struct CodeBlock { // Beginning PPC address. u32 m_address; // Number of instructions // Gives us the size of the block. u32 m_num_instructions; // Some basic statistics about the block. BlockStats *m_stats; // Register statistics about the block. BlockRegStats *m_gpa, *m_fpa; // Are we a broken block? bool m_broken; // Did we have a memory_exception? bool m_memory_exception; // Which GQRs this block uses, if any. BitSet8 m_gqr_used; // Which GQRs this block modifies, if any. BitSet8 m_gqr_modified; }; class PPCAnalyzer { private: enum ReorderType { REORDER_CARRY, REORDER_CMP, REORDER_CROR }; void ReorderInstructionsCore(u32 instructions, CodeOp* code, bool reverse, ReorderType type); void ReorderInstructions(u32 instructions, CodeOp *code); void SetInstructionStats(CodeBlock *block, CodeOp *code, GekkoOPInfo *opinfo, u32 index); // Options u32 m_options; public: enum AnalystOption { // Conditional branch continuing // If the JIT core supports conditional branches within the blocks // Block will end on unconditional branch or other ENDBLOCK flagged instruction. // Requires JIT support to be enabled. OPTION_CONDITIONAL_CONTINUE = (1 << 0), // If there is a unconditional branch that jumps to a leaf function then inline it. // Might require JIT intervention to support it correctly. // Requires JITBLock support for inlined code // XXX: NOT COMPLETE OPTION_LEAF_INLINE = (1 << 1), // Complex blocks support jumping backwards on to themselves. // Happens commonly in loops, pretty complex to support. // May require register caches to use register usage metrics. // XXX: NOT COMPLETE OPTION_COMPLEX_BLOCK = (1 << 2), // Similar to complex blocks. // Instead of jumping backwards, this jumps forwards within the block. // Requires JIT support to work. // XXX: NOT COMPLETE OPTION_FORWARD_JUMP = (1 << 3), // Reorder compare/Rc instructions next to their associated branches and // merge in the JIT (for common cases, anyway). OPTION_BRANCH_MERGE = (1 << 4), // Reorder carry instructions next to their associated branches and pass // carry flags in the x86 flags between them, instead of in XER. OPTION_CARRY_MERGE = (1 << 5), // Reorder cror instructions next to their associated fcmp. OPTION_CROR_MERGE = (1 << 6), }; PPCAnalyzer() : m_options(0) {} // Option setting/getting void SetOption(AnalystOption option) { m_options |= option; } void ClearOption(AnalystOption option) { m_options &= ~(option); } bool HasOption(AnalystOption option) const { return !!(m_options & option); } u32 Analyze(u32 address, CodeBlock *block, CodeBuffer *buffer, u32 blockSize); }; void LogFunctionCall(u32 addr); void FindFunctions(u32 startAddr, u32 endAddr, PPCSymbolDB *func_db); bool AnalyzeFunction(u32 startAddr, Symbol &func, int max_size = 0); } // namespace