From dc26afbd7e19e3e104d3d63c42e4312176953c5d Mon Sep 17 00:00:00 2001 From: MerryMage Date: Thu, 25 Aug 2016 03:18:27 +0100 Subject: [PATCH] translate_arm: Translate more than one conditional instruction in a block --- src/backend_x64/emit_x64.cpp | 2 +- src/frontend/ir/basic_block.h | 2 + src/frontend/translate/translate_arm.cpp | 39 ++++++++++++++++--- .../translate/translate_arm/translate_arm.h | 2 + 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index d3c8b9b5..a70f5687 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -1944,7 +1944,7 @@ void EmitX64::EmitCondPrelude(const IR::Block& block) { // TODO: Improve, maybe. auto fixup = code->J_CC(cc, true); - EmitAddCycles(block.cycle_count); + EmitAddCycles(block.cond_failed_cycle_count); EmitTerminalLinkBlock(IR::Term::LinkBlock{block.cond_failed.get()}, block.location); code->SetJumpTarget(fixup); } diff --git a/src/frontend/ir/basic_block.h b/src/frontend/ir/basic_block.h index 006a2319..89534ad7 100644 --- a/src/frontend/ir/basic_block.h +++ b/src/frontend/ir/basic_block.h @@ -69,6 +69,8 @@ public: Arm::Cond cond = Arm::Cond::AL; /// Block to execute next if `cond` did not pass. boost::optional cond_failed = {}; + /// Number of cycles this block takes to execute if the conditional fails. + size_t cond_failed_cycle_count = 0; /// List of instructions in this block. InstructionList instructions; diff --git a/src/frontend/translate/translate_arm.cpp b/src/frontend/translate/translate_arm.cpp index a91c6407..87160152 100644 --- a/src/frontend/translate/translate_arm.cpp +++ b/src/frontend/translate/translate_arm.cpp @@ -4,6 +4,8 @@ * General Public License version 2 or any later version. */ +#include + #include "common/assert.h" #include "frontend/arm_types.h" #include "frontend/decoder/arm.h" @@ -15,11 +17,21 @@ namespace Dynarmic { namespace Arm { +static bool CondCanContinue(ConditionalState cond_state, IREmitter& ir) { + ASSERT_MSG(cond_state != ConditionalState::Break, "Should never happen."); + + if (cond_state == ConditionalState::None) + return true; + + // TODO: This is more conservative than necessary. + return std::all_of(ir.block.begin(), ir.block.end(), [](const IR::Inst& inst) { return !inst.WritesToCPSR(); }); +} + IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memory_read_32) { ArmTranslatorVisitor visitor{descriptor}; bool should_continue = true; - while (should_continue && visitor.cond_state == ConditionalState::None) { + while (should_continue && CondCanContinue(visitor.cond_state, visitor.ir)) { const u32 arm_pc = visitor.ir.current_location.PC(); const u32 arm_instruction = memory_read_32(arm_pc); @@ -39,11 +51,10 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memor visitor.ir.block.cycle_count++; } - if (visitor.cond_state == ConditionalState::Translating) { + if (visitor.cond_state == ConditionalState::Translating || visitor.cond_state == ConditionalState::Trailing) { if (should_continue) { visitor.ir.SetTerm(IR::Term::LinkBlockFast{visitor.ir.current_location}); } - visitor.ir.block.cond_failed = { visitor.ir.current_location }; } ASSERT_MSG(visitor.ir.block.terminal.which() != 0, "Terminal has not been set"); @@ -52,13 +63,27 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memor } bool ArmTranslatorVisitor::ConditionPassed(Cond cond) { - ASSERT_MSG(cond_state != ConditionalState::Translating, - "In the current impl, ConditionPassed should never be called again once a non-AL cond is hit. " - "(i.e.: one and only one conditional instruction per block)"); ASSERT_MSG(cond_state != ConditionalState::Break, "This should never happen. We requested a break but that wasn't honored."); ASSERT_MSG(cond != Cond::NV, "NV conditional is obsolete"); + if (cond_state == ConditionalState::Translating) { + if (ir.block.cond_failed != ir.current_location || cond == Cond::AL) { + cond_state = ConditionalState::Trailing; + } else { + if (cond == ir.block.cond) { + ir.block.cond_failed = { ir.current_location.AdvancePC(4) }; + ir.block.cond_failed_cycle_count++; + return true; + } + + // cond has changed, abort + cond_state = ConditionalState::Break; + ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location}); + return false; + } + } + if (cond == Cond::AL) { // Everything is fine with the world return true; @@ -78,6 +103,8 @@ bool ArmTranslatorVisitor::ConditionPassed(Cond cond) { cond_state = ConditionalState::Translating; ir.block.cond = cond; + ir.block.cond_failed = { ir.current_location.AdvancePC(4) }; + ir.block.cond_failed_cycle_count = 1; return true; } diff --git a/src/frontend/translate/translate_arm/translate_arm.h b/src/frontend/translate/translate_arm/translate_arm.h index 798185e2..496e9b70 100644 --- a/src/frontend/translate/translate_arm/translate_arm.h +++ b/src/frontend/translate/translate_arm/translate_arm.h @@ -18,6 +18,8 @@ enum class ConditionalState { Break, /// This basic block is made up solely of conditional instructions. Translating, + /// This basic block is made up of conditional instructions followed by unconditional instructions. + Trailing, }; struct ArmTranslatorVisitor final {