translate_arm: Translate more than one conditional instruction in a block
This commit is contained in:
parent
aa9b63bac4
commit
dc26afbd7e
4 changed files with 38 additions and 7 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue