Merge pull request #9857 from JosJuice/jitarm64-cr-analysis
PPCAnalyst: Allow more reordering of CR operations
This commit is contained in:
commit
a7216a3035
|
@ -205,6 +205,7 @@ void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
|
||||||
// we must mark them as no longer discarded
|
// we must mark them as no longer discarded
|
||||||
gpr.ResetRegisters(js.op->regsOut);
|
gpr.ResetRegisters(js.op->regsOut);
|
||||||
fpr.ResetRegisters(js.op->GetFregsOut());
|
fpr.ResetRegisters(js.op->GetFregsOut());
|
||||||
|
gpr.ResetCRRegisters(js.op->crOut);
|
||||||
|
|
||||||
if (js.op->opinfo->flags & FL_ENDBLOCK)
|
if (js.op->opinfo->flags & FL_ENDBLOCK)
|
||||||
{
|
{
|
||||||
|
@ -1222,9 +1223,11 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||||
{
|
{
|
||||||
gpr.DiscardRegisters(op.gprDiscardable);
|
gpr.DiscardRegisters(op.gprDiscardable);
|
||||||
fpr.DiscardRegisters(op.fprDiscardable);
|
fpr.DiscardRegisters(op.fprDiscardable);
|
||||||
|
gpr.DiscardCRRegisters(op.crDiscardable);
|
||||||
}
|
}
|
||||||
gpr.StoreRegisters(~op.gprInUse & (op.regsIn | op.regsOut));
|
gpr.StoreRegisters(~op.gprInUse & (op.regsIn | op.regsOut));
|
||||||
fpr.StoreRegisters(~op.fprInUse & (op.fregsIn | op.GetFregsOut()));
|
fpr.StoreRegisters(~op.fprInUse & (op.fregsIn | op.GetFregsOut()));
|
||||||
|
gpr.StoreCRRegisters(~op.crInUse & (op.crIn | op.crOut));
|
||||||
|
|
||||||
if (opinfo->flags & FL_LOADSTORE)
|
if (opinfo->flags & FL_LOADSTORE)
|
||||||
++js.numLoadStoreInst;
|
++js.numLoadStoreInst;
|
||||||
|
|
|
@ -129,6 +129,8 @@ void Arm64RegCache::DiscardRegister(size_t preg)
|
||||||
{
|
{
|
||||||
OpArg& reg = m_guest_registers[preg];
|
OpArg& reg = m_guest_registers[preg];
|
||||||
ARM64Reg host_reg = reg.GetReg();
|
ARM64Reg host_reg = reg.GetReg();
|
||||||
|
if (!IsVector(host_reg))
|
||||||
|
host_reg = EncodeRegTo32(host_reg);
|
||||||
|
|
||||||
reg.Discard();
|
reg.Discard();
|
||||||
if (host_reg != ARM64Reg::INVALID_REG)
|
if (host_reg != ARM64Reg::INVALID_REG)
|
||||||
|
@ -288,6 +290,25 @@ void Arm64GPRCache::FlushCRRegisters(BitSet8 regs, bool maintain_state, ARM64Reg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Arm64GPRCache::DiscardCRRegisters(BitSet8 regs)
|
||||||
|
{
|
||||||
|
for (int i : regs)
|
||||||
|
DiscardRegister(GUEST_CR_OFFSET + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arm64GPRCache::ResetCRRegisters(BitSet8 regs)
|
||||||
|
{
|
||||||
|
for (int i : regs)
|
||||||
|
{
|
||||||
|
OpArg& reg = m_guest_registers[GUEST_CR_OFFSET + i];
|
||||||
|
ARM64Reg host_reg = reg.GetReg();
|
||||||
|
|
||||||
|
ASSERT_MSG(DYNA_REC, host_reg == ARM64Reg::INVALID_REG,
|
||||||
|
"Attempted to reset a loaded register (did you mean to flush it?)");
|
||||||
|
reg.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Arm64GPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg)
|
void Arm64GPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg)
|
||||||
{
|
{
|
||||||
FlushRegisters(BitSet32(0xFFFFFFFF), mode == FlushMode::MaintainState, tmp_reg);
|
FlushRegisters(BitSet32(0xFFFFFFFF), mode == FlushMode::MaintainState, tmp_reg);
|
||||||
|
|
|
@ -335,6 +335,9 @@ public:
|
||||||
FlushCRRegisters(regs, false, tmp_reg);
|
FlushCRRegisters(regs, false, tmp_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DiscardCRRegisters(BitSet8 regs);
|
||||||
|
void ResetCRRegisters(BitSet8 regs);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Get the order of the host registers
|
// Get the order of the host registers
|
||||||
void GetAllocationOrder() override;
|
void GetAllocationOrder() override;
|
||||||
|
|
|
@ -222,26 +222,13 @@ bool PPCAnalyzer::CanSwapAdjacentOps(const CodeOp& a, const CodeOp& b) const
|
||||||
// [1] https://bugs.dolphin-emu.org/issues/5864#note-7
|
// [1] https://bugs.dolphin-emu.org/issues/5864#note-7
|
||||||
if (a.canCauseException || b.canCauseException)
|
if (a.canCauseException || b.canCauseException)
|
||||||
return false;
|
return false;
|
||||||
if (a_flags & FL_ENDBLOCK)
|
if (a_flags & (FL_ENDBLOCK | FL_TIMER | FL_NO_REORDER | FL_SET_OE))
|
||||||
return false;
|
return false;
|
||||||
if (b_flags & (FL_SET_CRx | FL_ENDBLOCK | FL_TIMER | FL_EVIL | FL_SET_OE))
|
if (b_flags & (FL_ENDBLOCK | FL_TIMER | FL_NO_REORDER | FL_SET_OE))
|
||||||
return false;
|
|
||||||
if ((b_flags & (FL_RC_BIT | FL_RC_BIT_F)) && (b.inst.Rc))
|
|
||||||
return false;
|
return false;
|
||||||
if ((a_flags & (FL_SET_CA | FL_READ_CA)) && (b_flags & (FL_SET_CA | FL_READ_CA)))
|
if ((a_flags & (FL_SET_CA | FL_READ_CA)) && (b_flags & (FL_SET_CA | FL_READ_CA)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
switch (b.inst.OPCD)
|
|
||||||
{
|
|
||||||
case 16:
|
|
||||||
case 18:
|
|
||||||
// branches. Do not swap.
|
|
||||||
case 17: // sc
|
|
||||||
case 46: // lmw
|
|
||||||
case 19: // table19 - lots of tricky stuff
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now, only integer ops are acceptable.
|
// For now, only integer ops are acceptable.
|
||||||
if (b_info->type != OpType::Integer)
|
if (b_info->type != OpType::Integer)
|
||||||
return false;
|
return false;
|
||||||
|
@ -249,16 +236,22 @@ bool PPCAnalyzer::CanSwapAdjacentOps(const CodeOp& a, const CodeOp& b) const
|
||||||
// Check that we have no register collisions.
|
// Check that we have no register collisions.
|
||||||
// That is, check that none of b's outputs matches any of a's inputs,
|
// That is, check that none of b's outputs matches any of a's inputs,
|
||||||
// and that none of a's outputs matches any of b's inputs.
|
// and that none of a's outputs matches any of b's inputs.
|
||||||
// The latter does not apply if a is a cmp, of course, but doesn't hurt to check.
|
|
||||||
// register collision: b outputs to one of a's inputs
|
// register collision: b outputs to one of a's inputs
|
||||||
if (b.regsOut & a.regsIn)
|
if (b.regsOut & a.regsIn)
|
||||||
return false;
|
return false;
|
||||||
|
if (b.crOut & a.crIn)
|
||||||
|
return false;
|
||||||
// register collision: a outputs to one of b's inputs
|
// register collision: a outputs to one of b's inputs
|
||||||
if (a.regsOut & b.regsIn)
|
if (a.regsOut & b.regsIn)
|
||||||
return false;
|
return false;
|
||||||
|
if (a.crOut & b.crIn)
|
||||||
|
return false;
|
||||||
// register collision: b outputs to one of a's outputs (overwriting it)
|
// register collision: b outputs to one of a's outputs (overwriting it)
|
||||||
if (b.regsOut & a.regsOut)
|
if (b.regsOut & a.regsOut)
|
||||||
return false;
|
return false;
|
||||||
|
if (b.crOut & a.crOut)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -451,12 +444,6 @@ void FindFunctions(const Core::CPUThreadGuard& guard, u32 startAddr, u32 endAddr
|
||||||
unniceSize);
|
unniceSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isCmp(const CodeOp& a)
|
|
||||||
{
|
|
||||||
return (a.inst.OPCD == 10 || a.inst.OPCD == 11) ||
|
|
||||||
(a.inst.OPCD == 31 && (a.inst.SUBOP10 == 0 || a.inst.SUBOP10 == 32));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isCarryOp(const CodeOp& a)
|
static bool isCarryOp(const CodeOp& a)
|
||||||
{
|
{
|
||||||
return (a.opinfo->flags & FL_SET_CA) && !(a.opinfo->flags & FL_SET_OE) &&
|
return (a.opinfo->flags & FL_SET_CA) && !(a.opinfo->flags & FL_SET_OE) &&
|
||||||
|
@ -506,7 +493,7 @@ void PPCAnalyzer::ReorderInstructionsCore(u32 instructions, CodeOp* code, bool r
|
||||||
// Reorder integer compares, rlwinm., and carry-affecting ops
|
// Reorder integer compares, rlwinm., and carry-affecting ops
|
||||||
// (if we add more merged branch instructions, add them here!)
|
// (if we add more merged branch instructions, add them here!)
|
||||||
if ((type == ReorderType::CROR && isCror(a)) || (type == ReorderType::Carry && isCarryOp(a)) ||
|
if ((type == ReorderType::CROR && isCror(a)) || (type == ReorderType::Carry && isCarryOp(a)) ||
|
||||||
(type == ReorderType::CMP && (isCmp(a) || a.outputCR[0])))
|
(type == ReorderType::CMP && (type == ReorderType::CMP && a.crOut)))
|
||||||
{
|
{
|
||||||
// once we're next to a carry instruction, don't move away!
|
// once we're next to a carry instruction, don't move away!
|
||||||
if (type == ReorderType::Carry && i != start)
|
if (type == ReorderType::Carry && i != start)
|
||||||
|
@ -544,11 +531,6 @@ void PPCAnalyzer::ReorderInstructionsCore(u32 instructions, CodeOp* code, bool r
|
||||||
|
|
||||||
void PPCAnalyzer::ReorderInstructions(u32 instructions, CodeOp* code) const
|
void PPCAnalyzer::ReorderInstructions(u32 instructions, CodeOp* code) const
|
||||||
{
|
{
|
||||||
// Reorder cror instructions upwards (e.g. towards an fcmp). Technically we should be more
|
|
||||||
// picky about this, but cror seems to almost solely be used for this purpose in real code.
|
|
||||||
// Additionally, the other boolean ops seem to almost never be used.
|
|
||||||
if (HasOption(OPTION_CROR_MERGE))
|
|
||||||
ReorderInstructionsCore(instructions, code, true, ReorderType::CROR);
|
|
||||||
// For carry, bubble instructions *towards* each other; one direction often isn't enough
|
// For carry, bubble instructions *towards* each other; one direction often isn't enough
|
||||||
// to get pairs like addc/adde next to each other.
|
// to get pairs like addc/adde next to each other.
|
||||||
if (HasOption(OPTION_CARRY_MERGE))
|
if (HasOption(OPTION_CARRY_MERGE))
|
||||||
|
@ -556,8 +538,16 @@ void PPCAnalyzer::ReorderInstructions(u32 instructions, CodeOp* code) const
|
||||||
ReorderInstructionsCore(instructions, code, false, ReorderType::Carry);
|
ReorderInstructionsCore(instructions, code, false, ReorderType::Carry);
|
||||||
ReorderInstructionsCore(instructions, code, true, ReorderType::Carry);
|
ReorderInstructionsCore(instructions, code, true, ReorderType::Carry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reorder instructions which write to CR (typically compare instructions) towards branches.
|
||||||
if (HasOption(OPTION_BRANCH_MERGE))
|
if (HasOption(OPTION_BRANCH_MERGE))
|
||||||
ReorderInstructionsCore(instructions, code, false, ReorderType::CMP);
|
ReorderInstructionsCore(instructions, code, false, ReorderType::CMP);
|
||||||
|
|
||||||
|
// Reorder cror instructions upwards (e.g. towards an fcmp). Technically we should be more
|
||||||
|
// picky about this, but cror seems to almost solely be used for this purpose in real code.
|
||||||
|
// Additionally, the other boolean ops seem to almost never be used.
|
||||||
|
if (HasOption(OPTION_CROR_MERGE))
|
||||||
|
ReorderInstructionsCore(instructions, code, true, ReorderType::CROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPCAnalyzer::SetInstructionStats(CodeBlock* block, CodeOp* code,
|
void PPCAnalyzer::SetInstructionStats(CodeBlock* block, CodeOp* code,
|
||||||
|
@ -570,23 +560,40 @@ void PPCAnalyzer::SetInstructionStats(CodeBlock* block, CodeOp* code,
|
||||||
block->m_fpa->any = true;
|
block->m_fpa->any = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
code->wantsCR = BitSet8(0);
|
code->crIn = BitSet8(0);
|
||||||
if (opinfo->flags & FL_READ_ALL_CR)
|
if (opinfo->flags & FL_READ_ALL_CR)
|
||||||
code->wantsCR = BitSet8(0xFF);
|
{
|
||||||
|
code->crIn = BitSet8(0xFF);
|
||||||
|
}
|
||||||
else if (opinfo->flags & FL_READ_CRn)
|
else if (opinfo->flags & FL_READ_CRn)
|
||||||
code->wantsCR[code->inst.CRFS] = true;
|
{
|
||||||
|
code->crIn[code->inst.CRFS] = true;
|
||||||
|
}
|
||||||
else if (opinfo->flags & FL_READ_CR_BI)
|
else if (opinfo->flags & FL_READ_CR_BI)
|
||||||
code->wantsCR[code->inst.BI] = true;
|
{
|
||||||
|
code->crIn[code->inst.BI] = true;
|
||||||
|
}
|
||||||
|
else if (opinfo->type == OpType::CR)
|
||||||
|
{
|
||||||
|
code->crIn[code->inst.CRBA >> 2] = true;
|
||||||
|
code->crIn[code->inst.CRBB >> 2] = true;
|
||||||
|
|
||||||
code->outputCR = BitSet8(0);
|
// CR instructions only write to one bit of the destination CR,
|
||||||
|
// so treat the other three bits of the destination as inputs
|
||||||
|
code->crIn[code->inst.CRBD >> 2] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
code->crOut = BitSet8(0);
|
||||||
if (opinfo->flags & FL_SET_ALL_CR)
|
if (opinfo->flags & FL_SET_ALL_CR)
|
||||||
code->outputCR = BitSet8(0xFF);
|
code->crOut = BitSet8(0xFF);
|
||||||
else if (opinfo->flags & FL_SET_CRn)
|
else if (opinfo->flags & FL_SET_CRn)
|
||||||
code->outputCR[code->inst.CRFD] = true;
|
code->crOut[code->inst.CRFD] = true;
|
||||||
else if ((opinfo->flags & FL_SET_CR0) || ((opinfo->flags & FL_RC_BIT) && code->inst.Rc))
|
else if ((opinfo->flags & FL_SET_CR0) || ((opinfo->flags & FL_RC_BIT) && code->inst.Rc))
|
||||||
code->outputCR[0] = true;
|
code->crOut[0] = true;
|
||||||
else if ((opinfo->flags & FL_SET_CR1) || ((opinfo->flags & FL_RC_BIT_F) && code->inst.Rc))
|
else if ((opinfo->flags & FL_SET_CR1) || ((opinfo->flags & FL_RC_BIT_F) && code->inst.Rc))
|
||||||
code->outputCR[1] = true;
|
code->crOut[1] = true;
|
||||||
|
else if (opinfo->type == OpType::CR)
|
||||||
|
code->crOut[code->inst.CRBD >> 2] = true;
|
||||||
|
|
||||||
code->wantsFPRF = (opinfo->flags & FL_READ_FPRF) != 0;
|
code->wantsFPRF = (opinfo->flags & FL_READ_FPRF) != 0;
|
||||||
code->outputFPRF = (opinfo->flags & FL_SET_FPRF) != 0;
|
code->outputFPRF = (opinfo->flags & FL_SET_FPRF) != 0;
|
||||||
|
@ -955,9 +962,9 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer,
|
||||||
|
|
||||||
// Scan for flag dependencies; assume the next block (or any branch that can leave the block)
|
// Scan for flag dependencies; assume the next block (or any branch that can leave the block)
|
||||||
// wants flags, to be safe.
|
// wants flags, to be safe.
|
||||||
BitSet8 wantsCR = BitSet8(0xFF);
|
|
||||||
bool wantsFPRF = true;
|
bool wantsFPRF = true;
|
||||||
bool wantsCA = true;
|
bool wantsCA = true;
|
||||||
|
BitSet8 crInUse, crDiscardable;
|
||||||
BitSet32 gprBlockInputs, gprInUse, fprInUse, gprDiscardable, fprDiscardable, fprInXmm;
|
BitSet32 gprBlockInputs, gprInUse, fprInUse, gprDiscardable, fprDiscardable, fprInXmm;
|
||||||
for (int i = block->m_num_instructions - 1; i >= 0; i--)
|
for (int i = block->m_num_instructions - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
|
@ -974,27 +981,26 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer,
|
||||||
const bool hle = !!HLE::TryReplaceFunction(op.address);
|
const bool hle = !!HLE::TryReplaceFunction(op.address);
|
||||||
const bool may_exit_block = hle || op.canEndBlock || op.canCauseException;
|
const bool may_exit_block = hle || op.canEndBlock || op.canCauseException;
|
||||||
|
|
||||||
const BitSet8 opWantsCR = op.wantsCR;
|
|
||||||
const bool opWantsFPRF = op.wantsFPRF;
|
const bool opWantsFPRF = op.wantsFPRF;
|
||||||
const bool opWantsCA = op.wantsCA;
|
const bool opWantsCA = op.wantsCA;
|
||||||
op.wantsCR = wantsCR | BitSet8(may_exit_block ? 0xFF : 0);
|
|
||||||
op.wantsFPRF = wantsFPRF || may_exit_block;
|
op.wantsFPRF = wantsFPRF || may_exit_block;
|
||||||
op.wantsCA = wantsCA || may_exit_block;
|
op.wantsCA = wantsCA || may_exit_block;
|
||||||
wantsCR |= opWantsCR | BitSet8(may_exit_block ? 0xFF : 0);
|
|
||||||
wantsFPRF |= opWantsFPRF || may_exit_block;
|
wantsFPRF |= opWantsFPRF || may_exit_block;
|
||||||
wantsCA |= opWantsCA || may_exit_block;
|
wantsCA |= opWantsCA || may_exit_block;
|
||||||
wantsCR &= ~op.outputCR | opWantsCR;
|
|
||||||
wantsFPRF &= !op.outputFPRF || opWantsFPRF;
|
wantsFPRF &= !op.outputFPRF || opWantsFPRF;
|
||||||
wantsCA &= !op.outputCA || opWantsCA;
|
wantsCA &= !op.outputCA || opWantsCA;
|
||||||
op.gprInUse = gprInUse;
|
op.gprInUse = gprInUse;
|
||||||
op.fprInUse = fprInUse;
|
op.fprInUse = fprInUse;
|
||||||
|
op.crInUse = crInUse;
|
||||||
op.gprDiscardable = gprDiscardable;
|
op.gprDiscardable = gprDiscardable;
|
||||||
op.fprDiscardable = fprDiscardable;
|
op.fprDiscardable = fprDiscardable;
|
||||||
|
op.crDiscardable = crDiscardable;
|
||||||
op.fprInXmm = fprInXmm;
|
op.fprInXmm = fprInXmm;
|
||||||
gprBlockInputs &= ~op.regsOut;
|
gprBlockInputs &= ~op.regsOut;
|
||||||
gprBlockInputs |= op.regsIn;
|
gprBlockInputs |= op.regsIn;
|
||||||
gprInUse |= op.regsIn | op.regsOut;
|
gprInUse |= op.regsIn | op.regsOut;
|
||||||
fprInUse |= op.fregsIn | op.GetFregsOut();
|
fprInUse |= op.fregsIn | op.GetFregsOut();
|
||||||
|
crInUse |= op.crIn | op.crOut;
|
||||||
|
|
||||||
if (strncmp(op.opinfo->opname, "stfd", 4))
|
if (strncmp(op.opinfo->opname, "stfd", 4))
|
||||||
fprInXmm |= op.fregsIn;
|
fprInXmm |= op.fregsIn;
|
||||||
|
@ -1006,11 +1012,13 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer,
|
||||||
fprInXmm = BitSet32{};
|
fprInXmm = BitSet32{};
|
||||||
gprDiscardable = BitSet32{};
|
gprDiscardable = BitSet32{};
|
||||||
fprDiscardable = BitSet32{};
|
fprDiscardable = BitSet32{};
|
||||||
|
crDiscardable = BitSet8{};
|
||||||
}
|
}
|
||||||
else if (op.canEndBlock || op.canCauseException)
|
else if (op.canEndBlock || op.canCauseException)
|
||||||
{
|
{
|
||||||
gprDiscardable = BitSet32{};
|
gprDiscardable = BitSet32{};
|
||||||
fprDiscardable = BitSet32{};
|
fprDiscardable = BitSet32{};
|
||||||
|
crDiscardable = BitSet8{};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1018,6 +1026,8 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer,
|
||||||
gprDiscardable &= ~op.regsIn;
|
gprDiscardable &= ~op.regsIn;
|
||||||
fprDiscardable |= op.GetFregsOut();
|
fprDiscardable |= op.GetFregsOut();
|
||||||
fprDiscardable &= ~op.fregsIn;
|
fprDiscardable &= ~op.fregsIn;
|
||||||
|
crDiscardable |= op.crOut;
|
||||||
|
crDiscardable &= ~op.crIn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,12 @@ struct CodeOp // 16B
|
||||||
const GekkoOPInfo* opinfo = nullptr;
|
const GekkoOPInfo* opinfo = nullptr;
|
||||||
u32 address = 0;
|
u32 address = 0;
|
||||||
u32 branchTo = 0; // if UINT32_MAX, not a branch
|
u32 branchTo = 0; // if UINT32_MAX, not a branch
|
||||||
BitSet32 regsOut;
|
|
||||||
BitSet32 regsIn;
|
BitSet32 regsIn;
|
||||||
|
BitSet32 regsOut;
|
||||||
BitSet32 fregsIn;
|
BitSet32 fregsIn;
|
||||||
s8 fregOut = 0;
|
s8 fregOut = 0;
|
||||||
|
BitSet8 crIn;
|
||||||
|
BitSet8 crOut;
|
||||||
bool isBranchTarget = false;
|
bool isBranchTarget = false;
|
||||||
bool branchUsesCtr = false;
|
bool branchUsesCtr = false;
|
||||||
bool branchIsIdleLoop = false;
|
bool branchIsIdleLoop = false;
|
||||||
|
@ -50,6 +52,8 @@ struct CodeOp // 16B
|
||||||
bool canCauseException = false;
|
bool canCauseException = false;
|
||||||
bool skipLRStack = false;
|
bool skipLRStack = false;
|
||||||
bool skip = false; // followed BL-s for example
|
bool skip = false; // followed BL-s for example
|
||||||
|
BitSet8 crInUse;
|
||||||
|
BitSet8 crDiscardable;
|
||||||
// which registers are still needed after this instruction in this block
|
// which registers are still needed after this instruction in this block
|
||||||
BitSet32 fprInUse;
|
BitSet32 fprInUse;
|
||||||
BitSet32 gprInUse;
|
BitSet32 gprInUse;
|
||||||
|
|
|
@ -87,8 +87,8 @@ constexpr std::array<GekkoOPTemplate, 54> s_primary_table{{
|
||||||
{38, "stb", OpType::Store, 1, FL_IN_A0 | FL_IN_S | FL_LOADSTORE},
|
{38, "stb", OpType::Store, 1, FL_IN_A0 | FL_IN_S | FL_LOADSTORE},
|
||||||
{39, "stbu", OpType::Store, 1, FL_OUT_A | FL_IN_A | FL_IN_S | FL_LOADSTORE},
|
{39, "stbu", OpType::Store, 1, FL_OUT_A | FL_IN_A | FL_IN_S | FL_LOADSTORE},
|
||||||
|
|
||||||
{46, "lmw", OpType::System, 11, FL_EVIL | FL_IN_A0 | FL_LOADSTORE},
|
{46, "lmw", OpType::System, 11, FL_IN_A0 | FL_LOADSTORE},
|
||||||
{47, "stmw", OpType::System, 11, FL_EVIL | FL_IN_A0 | FL_LOADSTORE},
|
{47, "stmw", OpType::System, 11, FL_IN_A0 | FL_LOADSTORE},
|
||||||
|
|
||||||
{48, "lfs", OpType::LoadFP, 1, FL_OUT_FLOAT_D | FL_IN_A | FL_USE_FPU | FL_LOADSTORE},
|
{48, "lfs", OpType::LoadFP, 1, FL_OUT_FLOAT_D | FL_IN_A | FL_USE_FPU | FL_LOADSTORE},
|
||||||
{49, "lfsu", OpType::LoadFP, 1,
|
{49, "lfsu", OpType::LoadFP, 1,
|
||||||
|
@ -224,17 +224,17 @@ constexpr std::array<GekkoOPTemplate, 4> s_table4_3{{
|
||||||
constexpr std::array<GekkoOPTemplate, 13> s_table19{{
|
constexpr std::array<GekkoOPTemplate, 13> s_table19{{
|
||||||
{528, "bcctrx", OpType::Branch, 1, FL_ENDBLOCK | FL_READ_CR_BI},
|
{528, "bcctrx", OpType::Branch, 1, FL_ENDBLOCK | FL_READ_CR_BI},
|
||||||
{16, "bclrx", OpType::Branch, 1, FL_ENDBLOCK | FL_READ_CR_BI},
|
{16, "bclrx", OpType::Branch, 1, FL_ENDBLOCK | FL_READ_CR_BI},
|
||||||
{257, "crand", OpType::CR, 1, FL_EVIL},
|
{257, "crand", OpType::CR, 1, 0},
|
||||||
{129, "crandc", OpType::CR, 1, FL_EVIL},
|
{129, "crandc", OpType::CR, 1, 0},
|
||||||
{289, "creqv", OpType::CR, 1, FL_EVIL},
|
{289, "creqv", OpType::CR, 1, 0},
|
||||||
{225, "crnand", OpType::CR, 1, FL_EVIL},
|
{225, "crnand", OpType::CR, 1, 0},
|
||||||
{33, "crnor", OpType::CR, 1, FL_EVIL},
|
{33, "crnor", OpType::CR, 1, 0},
|
||||||
{449, "cror", OpType::CR, 1, FL_EVIL},
|
{449, "cror", OpType::CR, 1, 0},
|
||||||
{417, "crorc", OpType::CR, 1, FL_EVIL},
|
{417, "crorc", OpType::CR, 1, 0},
|
||||||
{193, "crxor", OpType::CR, 1, FL_EVIL},
|
{193, "crxor", OpType::CR, 1, 0},
|
||||||
|
|
||||||
{150, "isync", OpType::InstructionCache, 1, FL_EVIL},
|
{150, "isync", OpType::InstructionCache, 1, FL_NO_REORDER},
|
||||||
{0, "mcrf", OpType::System, 1, FL_EVIL | FL_SET_CRn | FL_READ_CRn},
|
{0, "mcrf", OpType::System, 1, FL_SET_CRn | FL_READ_CRn},
|
||||||
|
|
||||||
{50, "rfi", OpType::System, 2, FL_ENDBLOCK | FL_CHECKEXCEPTIONS | FL_PROGRAMEXCEPTION},
|
{50, "rfi", OpType::System, 2, FL_ENDBLOCK | FL_CHECKEXCEPTIONS | FL_PROGRAMEXCEPTION},
|
||||||
}};
|
}};
|
||||||
|
@ -324,12 +324,12 @@ constexpr std::array<GekkoOPTemplate, 107> s_table31{{
|
||||||
{790, "lhbrx", OpType::Load, 1, FL_OUT_D | FL_IN_A0B | FL_LOADSTORE},
|
{790, "lhbrx", OpType::Load, 1, FL_OUT_D | FL_IN_A0B | FL_LOADSTORE},
|
||||||
|
|
||||||
// Conditional load/store (Wii SMP)
|
// Conditional load/store (Wii SMP)
|
||||||
{150, "stwcxd", OpType::Store, 1, FL_EVIL | FL_IN_S | FL_IN_A0B | FL_SET_CR0 | FL_LOADSTORE},
|
{150, "stwcxd", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_SET_CR0 | FL_LOADSTORE},
|
||||||
{20, "lwarx", OpType::Load, 1, FL_EVIL | FL_OUT_D | FL_IN_A0B | FL_SET_CR0 | FL_LOADSTORE},
|
{20, "lwarx", OpType::Load, 1, FL_OUT_D | FL_IN_A0B | FL_SET_CR0 | FL_LOADSTORE},
|
||||||
|
|
||||||
// load string (Inst these)
|
// load string (Inst these)
|
||||||
{533, "lswx", OpType::Load, 1, FL_EVIL | FL_IN_A0B | FL_OUT_D | FL_LOADSTORE},
|
{533, "lswx", OpType::Load, 1, FL_IN_A0B | FL_OUT_D | FL_LOADSTORE},
|
||||||
{597, "lswi", OpType::Load, 1, FL_EVIL | FL_IN_A0 | FL_OUT_D | FL_LOADSTORE},
|
{597, "lswi", OpType::Load, 1, FL_IN_A0 | FL_OUT_D | FL_LOADSTORE},
|
||||||
|
|
||||||
// store word
|
// store word
|
||||||
{151, "stwx", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_LOADSTORE},
|
{151, "stwx", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_LOADSTORE},
|
||||||
|
@ -347,8 +347,8 @@ constexpr std::array<GekkoOPTemplate, 107> s_table31{{
|
||||||
{662, "stwbrx", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_LOADSTORE},
|
{662, "stwbrx", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_LOADSTORE},
|
||||||
{918, "sthbrx", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_LOADSTORE},
|
{918, "sthbrx", OpType::Store, 1, FL_IN_S | FL_IN_A0B | FL_LOADSTORE},
|
||||||
|
|
||||||
{661, "stswx", OpType::Store, 1, FL_EVIL | FL_IN_A0B | FL_LOADSTORE},
|
{661, "stswx", OpType::Store, 1, FL_IN_A0B | FL_LOADSTORE},
|
||||||
{725, "stswi", OpType::Store, 1, FL_EVIL | FL_IN_A0 | FL_LOADSTORE},
|
{725, "stswi", OpType::Store, 1, FL_IN_A0 | FL_LOADSTORE},
|
||||||
|
|
||||||
// fp load/store
|
// fp load/store
|
||||||
{535, "lfsx", OpType::LoadFP, 1, FL_OUT_FLOAT_D | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE},
|
{535, "lfsx", OpType::LoadFP, 1, FL_OUT_FLOAT_D | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE},
|
||||||
|
|
|
@ -36,8 +36,7 @@ enum InstructionFlags : u64
|
||||||
FL_OUT_AD = FL_OUT_A | FL_OUT_D,
|
FL_OUT_AD = FL_OUT_A | FL_OUT_D,
|
||||||
FL_TIMER = (1ull << 15), // Used only for mftb.
|
FL_TIMER = (1ull << 15), // Used only for mftb.
|
||||||
FL_CHECKEXCEPTIONS = (1ull << 16), // Used with rfi/rfid.
|
FL_CHECKEXCEPTIONS = (1ull << 16), // Used with rfi/rfid.
|
||||||
FL_EVIL =
|
FL_NO_REORDER = (1ull << 17), // Instruction should not be reordered by our optimizations.
|
||||||
(1ull << 17), // Historically used to refer to instructions that messed up Super Monkey Ball.
|
|
||||||
FL_USE_FPU = (1ull << 18), // Used to indicate a floating point instruction.
|
FL_USE_FPU = (1ull << 18), // Used to indicate a floating point instruction.
|
||||||
FL_LOADSTORE = (1ull << 19), // Used to indicate a load/store instruction.
|
FL_LOADSTORE = (1ull << 19), // Used to indicate a load/store instruction.
|
||||||
FL_SET_FPRF = (1ull << 20), // Sets bits in the FPRF.
|
FL_SET_FPRF = (1ull << 20), // Sets bits in the FPRF.
|
||||||
|
|
Loading…
Reference in New Issue