From 7d7751c157d2d6094c6e1ae02a33d4c1dd79317a Mon Sep 17 00:00:00 2001 From: MerryMage Date: Thu, 14 Jul 2016 12:52:53 +0100 Subject: [PATCH] Allow IR blocks to require a cond for block entry. * IR: Add cond, cond_failed. * backend_x64/EmitX64: Implement EmitCondPrelude --- src/backend_x64/emit_x64.cpp | 153 +++++++++++++++++++++++++++++++++++ src/backend_x64/emit_x64.h | 14 +++- src/frontend/arm_types.h | 7 +- src/frontend/ir/ir.h | 11 +++ 4 files changed, 178 insertions(+), 7 deletions(-) diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 7c0f14f6..2299f079 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -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(cycles))); } +void EmitX64::EmitCondPrelude(Arm::Cond cond, + boost::optional 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(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: diff --git a/src/backend_x64/emit_x64.h b/src/backend_x64/emit_x64.h index 21daa52a..17711da4 100644 --- a/src/backend_x64/emit_x64.h +++ b/src/backend_x64/emit_x64.h @@ -30,6 +30,10 @@ public: return iter != basic_blocks.end() ? iter->second : nullptr; } + void ClearCache(); + +private: + // Microinstruction emitters void EmitImmU1(IR::Value* value); void EmitImmU8(IR::Value* value); void EmitImmU32(IR::Value* value); @@ -70,8 +74,13 @@ public: void EmitWriteMemory32(IR::Value* value); void EmitWriteMemory64(IR::Value* value); + // Helpers void EmitAddCycles(size_t cycles); + void EmitCondPrelude(Arm::Cond cond, + boost::optional cond_failed, + Arm::LocationDescriptor current_location); + // Terminal instruction emitters void EmitTerminal(IR::Terminal 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); @@ -80,12 +89,11 @@ public: void EmitTerminalPopRSBHint(IR::Term::PopRSBHint terminal, Arm::LocationDescriptor initial_location); void EmitTerminalIf(IR::Term::If terminal, Arm::LocationDescriptor initial_location); - void ClearCache(); - -private: + // Per-block state std::set inhibit_emission; RegAlloc reg_alloc; + // State Gen::XEmitter* code; Routines* routines; UserCallbacks cb; diff --git a/src/frontend/arm_types.h b/src/frontend/arm_types.h index c09209e5..af5add76 100644 --- a/src/frontend/arm_types.h +++ b/src/frontend/arm_types.h @@ -60,16 +60,15 @@ enum class SignExtendRotation { }; struct LocationDescriptor { - LocationDescriptor(u32 arm_pc, bool TFlag, bool EFlag, Cond cond = Cond::AL) - : arm_pc(arm_pc), TFlag(TFlag), EFlag(EFlag), cond(cond) {} + LocationDescriptor(u32 arm_pc, bool TFlag, bool EFlag) + : arm_pc(arm_pc), TFlag(TFlag), EFlag(EFlag) {} u32 arm_pc; bool TFlag; ///< Thumb / ARM bool EFlag; ///< Big / Little Endian - Cond cond; 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); } }; diff --git a/src/frontend/ir/ir.h b/src/frontend/ir/ir.h index fbee0b02..c0a3f03f 100644 --- a/src/frontend/ir/ir.h +++ b/src/frontend/ir/ir.h @@ -11,6 +11,7 @@ #include #include +#include #include "common/common_types.h" #include "frontend/arm_types.h" @@ -246,9 +247,19 @@ class Block final { public: explicit Block(const Arm::LocationDescriptor& location) : location(location) {} + /// Description of the starting location of this block 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 cond_failed = {}; + + /// List of instructions in this block. std::list instructions; + /// Terminal instruction of this block. Terminal terminal = Term::Invalid{}; + + /// Number of cycles this block takes to execute. size_t cycle_count = 0; };