Allow IR blocks to require a cond for block entry.

* IR: Add cond, cond_failed.
* backend_x64/EmitX64: Implement EmitCondPrelude
This commit is contained in:
MerryMage
2016-07-14 12:52:53 +01:00
parent 4ab4ca58f9
commit 7d7751c157
4 changed files with 178 additions and 7 deletions

View File

@@ -45,6 +45,8 @@ CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Bl
CodePtr code_ptr = code->GetCodePtr();
basic_blocks[descriptor] = code_ptr;
EmitCondPrelude(block.cond, block.cond_failed, block.location);
for (const auto& value : block.instructions) {
if (inhibit_emission.count(value.get()) != 0)
continue;
@@ -661,6 +663,157 @@ void EmitX64::EmitAddCycles(size_t cycles) {
code->SUB(64, MDisp(R15, offsetof(JitState, cycles_remaining)), Imm32(static_cast<u32>(cycles)));
}
void EmitX64::EmitCondPrelude(Arm::Cond cond,
boost::optional<Arm::LocationDescriptor> cond_failed,
Arm::LocationDescriptor initial_location) {
if (cond == Arm::Cond::AL) {
ASSERT(!cond_failed.is_initialized());
return;
}
ASSERT(cond_failed.is_initialized());
// TODO: This code is a quick copy-paste-and-quickly-modify job from a previous JIT. Clean this up.
auto NFlag = [this](X64Reg reg){
this->code->MOV(32, R(reg), MJitStateCpsr());
this->code->SHR(32, R(reg), Imm8(31));
this->code->AND(32, R(reg), Imm32(1));
};
auto ZFlag = [this](X64Reg reg){
this->code->MOV(32, R(reg), MJitStateCpsr());
this->code->SHR(32, R(reg), Imm8(30));
this->code->AND(32, R(reg), Imm32(1));
};
auto CFlag = [this](X64Reg reg){
this->code->MOV(32, R(reg), MJitStateCpsr());
this->code->SHR(32, R(reg), Imm8(29));
this->code->AND(32, R(reg), Imm32(1));
};
auto VFlag = [this](X64Reg reg){
this->code->MOV(32, R(reg), MJitStateCpsr());
this->code->SHR(32, R(reg), Imm8(28));
this->code->AND(32, R(reg), Imm32(1));
};
CCFlags cc;
switch (cond) {
case Arm::Cond::EQ: //z
ZFlag(RAX);
code->CMP(8, R(RAX), Imm8(0));
cc = CC_NE;
break;
case Arm::Cond::NE: //!z
ZFlag(RAX);
code->CMP(8, R(RAX), Imm8(0));
cc = CC_E;
break;
case Arm::Cond::CS: //c
CFlag(RBX);
code->CMP(8, R(RBX), Imm8(0));
cc = CC_NE;
break;
case Arm::Cond::CC: //!c
CFlag(RBX);
code->CMP(8, R(RBX), Imm8(0));
cc = CC_E;
break;
case Arm::Cond::MI: //n
NFlag(RCX);
code->CMP(8, R(RCX), Imm8(0));
cc = CC_NE;
break;
case Arm::Cond::PL: //!n
NFlag(RCX);
code->CMP(8, R(RCX), Imm8(0));
cc = CC_E;
break;
case Arm::Cond::VS: //v
VFlag(RDX);
code->CMP(8, R(RDX), Imm8(0));
cc = CC_NE;
break;
case Arm::Cond::VC: //!v
VFlag(RDX);
code->CMP(8, R(RDX), Imm8(0));
cc = CC_E;
break;
case Arm::Cond::HI: { //c & !z
const X64Reg tmp = RSI;
ZFlag(RAX);
code->MOVZX(64, 8, tmp, R(RAX));
CFlag(RBX);
code->CMP(8, R(RBX), R(tmp));
cc = CC_A;
break;
}
case Arm::Cond::LS: { //!c | z
const X64Reg tmp = RSI;
ZFlag(RAX);
code->MOVZX(64, 8, tmp, R(RAX));
CFlag(RBX);
code->CMP(8, R(RBX), R(tmp));
cc = CC_BE;
break;
}
case Arm::Cond::GE: { // n == v
const X64Reg tmp = RSI;
VFlag(RDX);
code->MOVZX(64, 8, tmp, R(RDX));
NFlag(RCX);
code->CMP(8, R(RCX), R(tmp));
cc = CC_E;
break;
}
case Arm::Cond::LT: { // n != v
const X64Reg tmp = RSI;
VFlag(RDX);
code->MOVZX(64, 8, tmp, R(RDX));
NFlag(RCX);
code->CMP(8, R(RCX), R(tmp));
cc = CC_NE;
break;
}
case Arm::Cond::GT: { // !z & (n == v)
const X64Reg tmp = RSI;
NFlag(RCX);
code->MOVZX(64, 8, tmp, R(RCX));
VFlag(RDX);
code->XOR(8, R(tmp), R(RDX));
ZFlag(RAX);
code->OR(8, R(tmp), R(RAX));
code->TEST(8, R(tmp), R(tmp));
cc = CC_Z;
break;
}
case Arm::Cond::LE: { // z | (n != v)
X64Reg tmp = RSI;
NFlag(RCX);
code->MOVZX(64, 8, tmp, R(RCX));
VFlag(RDX);
code->XOR(8, R(tmp), R(RDX));
ZFlag(RAX);
code->OR(8, R(tmp), R(RAX));
code->TEST(8, R(tmp), R(tmp));
cc = CC_NZ;
break;
}
default:
ASSERT_MSG(0, "Unknown cond %zu", static_cast<size_t>(cond));
break;
}
// TODO: Improve, maybe.
auto fixup = code->J_CC(cc, true);
EmitAddCycles(1); // TODO: Proper cycle count
EmitTerminalLinkBlock(IR::Term::LinkBlock{cond_failed.get()}, initial_location);
code->SetJumpTarget(fixup);
}
void EmitX64::EmitTerminal(IR::Terminal terminal, Arm::LocationDescriptor initial_location) {
switch (terminal.which()) {
case 1: