2013-04-17 22:43:11 -04:00
|
|
|
// Copyright 2013 Dolphin Emulator Project
|
|
|
|
// Licensed under GPLv2
|
|
|
|
// Refer to the license.txt file included.
|
2008-12-08 04:46:09 +00:00
|
|
|
|
2014-02-10 13:54:46 -05:00
|
|
|
#pragma once
|
2008-12-08 04:46:09 +00:00
|
|
|
|
2013-10-26 18:04:00 -07:00
|
|
|
#include <algorithm>
|
2013-10-17 00:32:18 -04:00
|
|
|
#include <cstdlib>
|
2014-02-17 05:18:15 -05:00
|
|
|
#include <map>
|
2008-12-08 04:46:09 +00:00
|
|
|
#include <string>
|
2014-02-17 05:18:15 -05:00
|
|
|
#include <vector>
|
2008-12-08 04:46:09 +00:00
|
|
|
|
2014-09-07 20:06:58 -05:00
|
|
|
#include "Common/CommonTypes.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/PowerPC/PPCTables.h"
|
2008-12-08 04:46:09 +00:00
|
|
|
|
2009-06-21 08:39:21 +00:00
|
|
|
class PPCSymbolDB;
|
2008-12-08 04:46:09 +00:00
|
|
|
struct Symbol;
|
|
|
|
|
|
|
|
namespace PPCAnalyst
|
|
|
|
{
|
|
|
|
|
|
|
|
struct CodeOp //16B
|
|
|
|
{
|
|
|
|
UGeckoInstruction inst;
|
2010-07-23 19:30:00 +00:00
|
|
|
GekkoOPInfo * opinfo;
|
2008-12-08 04:46:09 +00:00
|
|
|
u32 address;
|
|
|
|
u32 branchTo; //if 0, not a branch
|
|
|
|
int branchToIndex; //index of target block
|
|
|
|
s8 regsOut[2];
|
|
|
|
s8 regsIn[3];
|
|
|
|
s8 fregOut;
|
2014-09-13 05:34:38 -07:00
|
|
|
s8 fregsIn[4];
|
2008-12-08 04:46:09 +00:00
|
|
|
bool isBranchTarget;
|
|
|
|
bool wantsCR0;
|
|
|
|
bool wantsCR1;
|
2014-08-20 02:22:07 -07:00
|
|
|
bool wantsFPRF;
|
2014-08-21 13:56:18 -07:00
|
|
|
bool wantsCA;
|
2014-09-07 00:37:47 -07:00
|
|
|
bool wantsCAInFlags;
|
2008-12-08 04:46:09 +00:00
|
|
|
bool outputCR0;
|
|
|
|
bool outputCR1;
|
2014-08-20 02:22:07 -07:00
|
|
|
bool outputFPRF;
|
2014-08-21 13:56:18 -07:00
|
|
|
bool outputCA;
|
2014-08-20 02:22:07 -07:00
|
|
|
bool canEndBlock;
|
2008-12-20 11:20:47 +00:00
|
|
|
bool skip; // followed BL-s for example
|
2014-09-13 05:34:38 -07:00
|
|
|
// which registers are still needed after this instruction in this block
|
|
|
|
u32 fprInUse;
|
2014-10-09 16:11:10 -07:00
|
|
|
u32 gprInUse;
|
|
|
|
// just because a register is in use doesn't mean we actually need or want it in an x86 register.
|
|
|
|
u32 gprInReg;
|
2014-09-25 16:01:29 -07:00
|
|
|
// 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.
|
|
|
|
u32 fprInXmm;
|
2008-12-08 04:46:09 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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) {return numReads[reg] + numWrites[reg];}
|
2014-08-30 17:23:13 -04:00
|
|
|
int GetUseRange(int reg)
|
|
|
|
{
|
2013-10-29 01:23:17 -04:00
|
|
|
return std::max(lastRead[reg], lastWrite[reg]) -
|
2014-08-30 17:23:13 -04:00
|
|
|
std::min(firstRead[reg], firstWrite[reg]);
|
|
|
|
}
|
2010-07-25 19:01:32 +00:00
|
|
|
|
2014-06-05 20:33:35 -05:00
|
|
|
bool IsUsed(int reg)
|
|
|
|
{
|
|
|
|
return (numReads[reg] + numWrites[reg]) > 0;
|
|
|
|
}
|
|
|
|
|
2014-04-30 00:14:24 -05:00
|
|
|
inline void SetInputRegister(int reg, short opindex)
|
|
|
|
{
|
2010-07-25 19:01:32 +00:00
|
|
|
if (firstRead[reg] == -1)
|
2014-10-09 20:14:39 -04:00
|
|
|
firstRead[reg] = opindex;
|
|
|
|
lastRead[reg] = opindex;
|
2010-07-25 19:01:32 +00:00
|
|
|
numReads[reg]++;
|
|
|
|
}
|
|
|
|
|
2014-04-30 00:14:24 -05:00
|
|
|
inline void SetOutputRegister(int reg, short opindex)
|
|
|
|
{
|
2010-07-25 19:01:32 +00:00
|
|
|
if (firstWrite[reg] == -1)
|
2014-10-09 20:14:39 -04:00
|
|
|
firstWrite[reg] = opindex;
|
|
|
|
lastWrite[reg] = opindex;
|
2010-07-25 19:01:32 +00:00
|
|
|
numWrites[reg]++;
|
|
|
|
}
|
2014-04-30 00:14:24 -05:00
|
|
|
|
|
|
|
inline void Clear()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 32; ++i)
|
|
|
|
{
|
|
|
|
firstRead[i] = -1;
|
|
|
|
firstWrite[i] = -1;
|
|
|
|
numReads[i] = 0;
|
|
|
|
numWrites[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
2008-12-08 04:46:09 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2008-12-18 10:44:03 +00:00
|
|
|
class CodeBuffer
|
|
|
|
{
|
2008-12-20 17:22:30 +00:00
|
|
|
int size_;
|
2008-12-18 10:44:03 +00:00
|
|
|
public:
|
|
|
|
CodeBuffer(int size);
|
|
|
|
~CodeBuffer();
|
2008-12-08 04:46:09 +00:00
|
|
|
|
2008-12-20 17:22:30 +00:00
|
|
|
int GetSize() const { return size_; }
|
|
|
|
|
2008-12-18 10:44:03 +00:00
|
|
|
PPCAnalyst::CodeOp *codebuffer;
|
2008-12-20 17:22:30 +00:00
|
|
|
|
|
|
|
|
2008-12-18 10:44:03 +00:00
|
|
|
};
|
2008-12-08 04:46:09 +00:00
|
|
|
|
2014-04-30 00:14:24 -05:00
|
|
|
struct CodeBlock
|
|
|
|
{
|
|
|
|
// Beginning PPC address.
|
|
|
|
u32 m_address;
|
|
|
|
|
|
|
|
// Number of instructions
|
|
|
|
// Gives us the size of the block.
|
2014-04-30 03:49:17 -05:00
|
|
|
u32 m_num_instructions;
|
2014-04-30 00:14:24 -05:00
|
|
|
|
|
|
|
// 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;
|
2014-05-09 09:10:45 -05:00
|
|
|
|
|
|
|
// Did we have a memory_exception?
|
|
|
|
bool m_memory_exception;
|
2014-04-30 00:14:24 -05:00
|
|
|
};
|
|
|
|
|
2014-04-30 03:49:17 -05:00
|
|
|
class PPCAnalyzer
|
2014-04-30 00:14:24 -05:00
|
|
|
{
|
2014-04-30 03:49:17 -05:00
|
|
|
private:
|
2014-04-30 00:14:24 -05:00
|
|
|
|
2014-09-07 08:30:11 -07:00
|
|
|
enum ReorderType
|
|
|
|
{
|
|
|
|
REORDER_CARRY,
|
|
|
|
REORDER_CMP
|
|
|
|
};
|
|
|
|
|
|
|
|
void ReorderInstructionsCore(u32 instructions, CodeOp* code, bool reverse, ReorderType type);
|
2014-04-30 00:14:24 -05:00
|
|
|
void ReorderInstructions(u32 instructions, CodeOp *code);
|
|
|
|
void SetInstructionStats(CodeBlock *block, CodeOp *code, GekkoOPInfo *opinfo, u32 index);
|
|
|
|
|
|
|
|
// Options
|
|
|
|
u32 m_options;
|
2014-04-30 03:49:17 -05:00
|
|
|
public:
|
2014-04-30 00:14:24 -05:00
|
|
|
|
|
|
|
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),
|
2014-09-11 03:59:40 -07:00
|
|
|
|
|
|
|
// 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),
|
2014-04-30 00:14:24 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2014-04-30 03:49:17 -05:00
|
|
|
PPCAnalyzer() : m_options(0) {}
|
2014-04-30 00:14:24 -05:00
|
|
|
|
|
|
|
// Option setting/getting
|
|
|
|
void SetOption(AnalystOption option) { m_options |= option; }
|
|
|
|
void ClearOption(AnalystOption option) { m_options &= ~(option); }
|
|
|
|
bool HasOption(AnalystOption option) { return !!(m_options & option); }
|
|
|
|
|
2014-04-30 03:49:17 -05:00
|
|
|
u32 Analyze(u32 address, CodeBlock *block, CodeBuffer *buffer, u32 blockSize);
|
2014-04-30 00:14:24 -05:00
|
|
|
};
|
|
|
|
|
2008-12-18 10:44:03 +00:00
|
|
|
void LogFunctionCall(u32 addr);
|
2009-06-21 08:39:21 +00:00
|
|
|
void FindFunctions(u32 startAddr, u32 endAddr, PPCSymbolDB *func_db);
|
2008-12-08 04:46:09 +00:00
|
|
|
bool AnalyzeFunction(u32 startAddr, Symbol &func, int max_size = 0);
|
|
|
|
|
|
|
|
} // namespace
|