translate_arm: Translate more than one conditional instruction in a block

This commit is contained in:
MerryMage 2016-08-25 03:18:27 +01:00
parent aa9b63bac4
commit dc26afbd7e
4 changed files with 38 additions and 7 deletions

View file

@ -1944,7 +1944,7 @@ void EmitX64::EmitCondPrelude(const IR::Block& block) {
// TODO: Improve, maybe. // TODO: Improve, maybe.
auto fixup = code->J_CC(cc, true); 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); EmitTerminalLinkBlock(IR::Term::LinkBlock{block.cond_failed.get()}, block.location);
code->SetJumpTarget(fixup); code->SetJumpTarget(fixup);
} }

View file

@ -69,6 +69,8 @@ public:
Arm::Cond cond = Arm::Cond::AL; Arm::Cond cond = Arm::Cond::AL;
/// Block to execute next if `cond` did not pass. /// Block to execute next if `cond` did not pass.
boost::optional<Arm::LocationDescriptor> cond_failed = {}; boost::optional<Arm::LocationDescriptor> 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. /// List of instructions in this block.
InstructionList instructions; InstructionList instructions;

View file

@ -4,6 +4,8 @@
* General Public License version 2 or any later version. * General Public License version 2 or any later version.
*/ */
#include <algorithm>
#include "common/assert.h" #include "common/assert.h"
#include "frontend/arm_types.h" #include "frontend/arm_types.h"
#include "frontend/decoder/arm.h" #include "frontend/decoder/arm.h"
@ -15,11 +17,21 @@
namespace Dynarmic { namespace Dynarmic {
namespace Arm { 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) { IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memory_read_32) {
ArmTranslatorVisitor visitor{descriptor}; ArmTranslatorVisitor visitor{descriptor};
bool should_continue = true; 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_pc = visitor.ir.current_location.PC();
const u32 arm_instruction = memory_read_32(arm_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++; 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) { if (should_continue) {
visitor.ir.SetTerm(IR::Term::LinkBlockFast{visitor.ir.current_location}); 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"); 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) { 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, ASSERT_MSG(cond_state != ConditionalState::Break,
"This should never happen. We requested a break but that wasn't honored."); "This should never happen. We requested a break but that wasn't honored.");
ASSERT_MSG(cond != Cond::NV, "NV conditional is obsolete"); 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) { if (cond == Cond::AL) {
// Everything is fine with the world // Everything is fine with the world
return true; return true;
@ -78,6 +103,8 @@ bool ArmTranslatorVisitor::ConditionPassed(Cond cond) {
cond_state = ConditionalState::Translating; cond_state = ConditionalState::Translating;
ir.block.cond = cond; ir.block.cond = cond;
ir.block.cond_failed = { ir.current_location.AdvancePC(4) };
ir.block.cond_failed_cycle_count = 1;
return true; return true;
} }

View file

@ -18,6 +18,8 @@ enum class ConditionalState {
Break, Break,
/// This basic block is made up solely of conditional instructions. /// This basic block is made up solely of conditional instructions.
Translating, Translating,
/// This basic block is made up of conditional instructions followed by unconditional instructions.
Trailing,
}; };
struct ArmTranslatorVisitor final { struct ArmTranslatorVisitor final {