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:
parent
4ab4ca58f9
commit
7d7751c157
4 changed files with 178 additions and 7 deletions
|
@ -45,6 +45,8 @@ CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Bl
|
||||||
CodePtr code_ptr = code->GetCodePtr();
|
CodePtr code_ptr = code->GetCodePtr();
|
||||||
basic_blocks[descriptor] = code_ptr;
|
basic_blocks[descriptor] = code_ptr;
|
||||||
|
|
||||||
|
EmitCondPrelude(block.cond, block.cond_failed, block.location);
|
||||||
|
|
||||||
for (const auto& value : block.instructions) {
|
for (const auto& value : block.instructions) {
|
||||||
if (inhibit_emission.count(value.get()) != 0)
|
if (inhibit_emission.count(value.get()) != 0)
|
||||||
continue;
|
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)));
|
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) {
|
void EmitX64::EmitTerminal(IR::Terminal terminal, Arm::LocationDescriptor initial_location) {
|
||||||
switch (terminal.which()) {
|
switch (terminal.which()) {
|
||||||
case 1:
|
case 1:
|
||||||
|
|
|
@ -30,6 +30,10 @@ public:
|
||||||
return iter != basic_blocks.end() ? iter->second : nullptr;
|
return iter != basic_blocks.end() ? iter->second : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearCache();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Microinstruction emitters
|
||||||
void EmitImmU1(IR::Value* value);
|
void EmitImmU1(IR::Value* value);
|
||||||
void EmitImmU8(IR::Value* value);
|
void EmitImmU8(IR::Value* value);
|
||||||
void EmitImmU32(IR::Value* value);
|
void EmitImmU32(IR::Value* value);
|
||||||
|
@ -70,8 +74,13 @@ public:
|
||||||
void EmitWriteMemory32(IR::Value* value);
|
void EmitWriteMemory32(IR::Value* value);
|
||||||
void EmitWriteMemory64(IR::Value* value);
|
void EmitWriteMemory64(IR::Value* value);
|
||||||
|
|
||||||
|
// Helpers
|
||||||
void EmitAddCycles(size_t cycles);
|
void EmitAddCycles(size_t cycles);
|
||||||
|
void EmitCondPrelude(Arm::Cond cond,
|
||||||
|
boost::optional<Arm::LocationDescriptor> cond_failed,
|
||||||
|
Arm::LocationDescriptor current_location);
|
||||||
|
|
||||||
|
// Terminal instruction emitters
|
||||||
void EmitTerminal(IR::Terminal terminal, Arm::LocationDescriptor initial_location);
|
void EmitTerminal(IR::Terminal terminal, Arm::LocationDescriptor initial_location);
|
||||||
void EmitTerminalInterpret(IR::Term::Interpret terminal, Arm::LocationDescriptor initial_location);
|
void EmitTerminalInterpret(IR::Term::Interpret terminal, Arm::LocationDescriptor initial_location);
|
||||||
void EmitTerminalReturnToDispatch(IR::Term::ReturnToDispatch terminal, Arm::LocationDescriptor initial_location);
|
void EmitTerminalReturnToDispatch(IR::Term::ReturnToDispatch terminal, Arm::LocationDescriptor initial_location);
|
||||||
|
@ -80,12 +89,11 @@ public:
|
||||||
void EmitTerminalPopRSBHint(IR::Term::PopRSBHint terminal, Arm::LocationDescriptor initial_location);
|
void EmitTerminalPopRSBHint(IR::Term::PopRSBHint terminal, Arm::LocationDescriptor initial_location);
|
||||||
void EmitTerminalIf(IR::Term::If terminal, Arm::LocationDescriptor initial_location);
|
void EmitTerminalIf(IR::Term::If terminal, Arm::LocationDescriptor initial_location);
|
||||||
|
|
||||||
void ClearCache();
|
// Per-block state
|
||||||
|
|
||||||
private:
|
|
||||||
std::set<IR::Value*> inhibit_emission;
|
std::set<IR::Value*> inhibit_emission;
|
||||||
RegAlloc reg_alloc;
|
RegAlloc reg_alloc;
|
||||||
|
|
||||||
|
// State
|
||||||
Gen::XEmitter* code;
|
Gen::XEmitter* code;
|
||||||
Routines* routines;
|
Routines* routines;
|
||||||
UserCallbacks cb;
|
UserCallbacks cb;
|
||||||
|
|
|
@ -60,16 +60,15 @@ enum class SignExtendRotation {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LocationDescriptor {
|
struct LocationDescriptor {
|
||||||
LocationDescriptor(u32 arm_pc, bool TFlag, bool EFlag, Cond cond = Cond::AL)
|
LocationDescriptor(u32 arm_pc, bool TFlag, bool EFlag)
|
||||||
: arm_pc(arm_pc), TFlag(TFlag), EFlag(EFlag), cond(cond) {}
|
: arm_pc(arm_pc), TFlag(TFlag), EFlag(EFlag) {}
|
||||||
|
|
||||||
u32 arm_pc;
|
u32 arm_pc;
|
||||||
bool TFlag; ///< Thumb / ARM
|
bool TFlag; ///< Thumb / ARM
|
||||||
bool EFlag; ///< Big / Little Endian
|
bool EFlag; ///< Big / Little Endian
|
||||||
Cond cond;
|
|
||||||
|
|
||||||
bool operator == (const LocationDescriptor& o) const {
|
bool operator == (const LocationDescriptor& o) const {
|
||||||
return std::tie(arm_pc, TFlag, EFlag, cond) == std::tie(o.arm_pc, o.TFlag, o.EFlag, o.cond);
|
return std::tie(arm_pc, TFlag, EFlag) == std::tie(o.arm_pc, o.TFlag, o.EFlag);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
|
@ -246,9 +247,19 @@ class Block final {
|
||||||
public:
|
public:
|
||||||
explicit Block(const Arm::LocationDescriptor& location) : location(location) {}
|
explicit Block(const Arm::LocationDescriptor& location) : location(location) {}
|
||||||
|
|
||||||
|
/// Description of the starting location of this block
|
||||||
Arm::LocationDescriptor location;
|
Arm::LocationDescriptor location;
|
||||||
|
/// Conditional to pass in order to execute this block
|
||||||
|
Arm::Cond cond = Arm::Cond::AL;
|
||||||
|
/// Block to execute next if `cond` did not pass.
|
||||||
|
boost::optional<Arm::LocationDescriptor> cond_failed = {};
|
||||||
|
|
||||||
|
/// List of instructions in this block.
|
||||||
std::list<ValuePtr> instructions;
|
std::list<ValuePtr> instructions;
|
||||||
|
/// Terminal instruction of this block.
|
||||||
Terminal terminal = Term::Invalid{};
|
Terminal terminal = Term::Invalid{};
|
||||||
|
|
||||||
|
/// Number of cycles this block takes to execute.
|
||||||
size_t cycle_count = 0;
|
size_t cycle_count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue