A32/translate: Factor conditional state handling out
This commit is contained in:
parent
1e29ef8b0e
commit
714b0b9a8b
5 changed files with 116 additions and 70 deletions
|
@ -125,6 +125,8 @@ if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
|||
frontend/A32/location_descriptor.cpp
|
||||
frontend/A32/location_descriptor.h
|
||||
frontend/A32/PSR.h
|
||||
frontend/A32/translate/conditional_state.cpp
|
||||
frontend/A32/translate/conditional_state.h
|
||||
frontend/A32/translate/impl/asimd_load_store_structures.cpp
|
||||
frontend/A32/translate/impl/asimd_misc.cpp
|
||||
frontend/A32/translate/impl/asimd_one_reg_modified_immediate.cpp
|
||||
|
|
79
src/frontend/A32/translate/conditional_state.cpp
Normal file
79
src/frontend/A32/translate/conditional_state.cpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2020 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <dynarmic/A32/config.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "frontend/A32/ir_emitter.h"
|
||||
#include "frontend/A32/translate/conditional_state.h"
|
||||
#include "frontend/ir/cond.h"
|
||||
|
||||
namespace Dynarmic::A32 {
|
||||
|
||||
bool CondCanContinue(ConditionalState cond_state, const A32::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(); });
|
||||
}
|
||||
|
||||
bool IsConditionPassed(IR::Cond cond, ConditionalState& cond_state, A32::IREmitter& ir, int instruction_size) {
|
||||
ASSERT_MSG(cond_state != ConditionalState::Break,
|
||||
"This should never happen. We requested a break but that wasn't honored.");
|
||||
|
||||
if (cond == IR::Cond::NV) {
|
||||
// NV conditional is obsolete
|
||||
ir.ExceptionRaised(Exception::UnpredictableInstruction);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cond_state == ConditionalState::Translating) {
|
||||
if (ir.block.ConditionFailedLocation() != ir.current_location || cond == IR::Cond::AL) {
|
||||
cond_state = ConditionalState::Trailing;
|
||||
} else {
|
||||
if (cond == ir.block.GetCondition()) {
|
||||
ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(instruction_size).AdvanceIT());
|
||||
ir.block.ConditionFailedCycleCount()++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// cond has changed, abort
|
||||
cond_state = ConditionalState::Break;
|
||||
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cond == IR::Cond::AL) {
|
||||
// Everything is fine with the world
|
||||
return true;
|
||||
}
|
||||
|
||||
// non-AL cond
|
||||
|
||||
if (!ir.block.empty()) {
|
||||
// We've already emitted instructions. Quit for now, we'll make a new block here later.
|
||||
cond_state = ConditionalState::Break;
|
||||
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location});
|
||||
return false;
|
||||
}
|
||||
|
||||
// We've not emitted instructions yet.
|
||||
// We'll emit one instruction, and set the block-entry conditional appropriately.
|
||||
|
||||
cond_state = ConditionalState::Translating;
|
||||
ir.block.SetCondition(cond);
|
||||
ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(instruction_size).AdvanceIT());
|
||||
ir.block.ConditionFailedCycleCount() = ir.block.CycleCount() + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::A32
|
32
src/frontend/A32/translate/conditional_state.h
Normal file
32
src/frontend/A32/translate/conditional_state.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2020 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic::IR {
|
||||
enum class Cond;
|
||||
} // namespace Dynarmic::IR
|
||||
|
||||
namespace Dynarmic::A32 {
|
||||
|
||||
class IREmitter;
|
||||
|
||||
enum class ConditionalState {
|
||||
/// We haven't met any conditional instructions yet.
|
||||
None,
|
||||
/// Current instruction is a conditional. This marks the end of this basic block.
|
||||
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,
|
||||
};
|
||||
|
||||
bool CondCanContinue(ConditionalState cond_state, const A32::IREmitter& ir);
|
||||
bool IsConditionPassed(IR::Cond cond, ConditionalState& cond_state, A32::IREmitter& ir, int instruction_size);
|
||||
|
||||
} // namespace Dynarmic::A32
|
|
@ -10,6 +10,7 @@
|
|||
#include "frontend/imm.h"
|
||||
#include "frontend/A32/ir_emitter.h"
|
||||
#include "frontend/A32/location_descriptor.h"
|
||||
#include "frontend/A32/translate/conditional_state.h"
|
||||
#include "frontend/A32/translate/translate.h"
|
||||
#include "frontend/A32/types.h"
|
||||
|
||||
|
@ -17,17 +18,6 @@ namespace Dynarmic::A32 {
|
|||
|
||||
enum class Exception;
|
||||
|
||||
enum class ConditionalState {
|
||||
/// We haven't met any conditional instructions yet.
|
||||
None,
|
||||
/// Current instruction is a conditional. This marks the end of this basic block.
|
||||
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 {
|
||||
using instruction_return_type = bool;
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <dynarmic/A32/config.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
|
@ -12,6 +10,7 @@
|
|||
#include "frontend/A32/decoder/asimd.h"
|
||||
#include "frontend/A32/decoder/vfp.h"
|
||||
#include "frontend/A32/location_descriptor.h"
|
||||
#include "frontend/A32/translate/conditional_state.h"
|
||||
#include "frontend/A32/translate/impl/translate_arm.h"
|
||||
#include "frontend/A32/translate/translate.h"
|
||||
#include "frontend/A32/types.h"
|
||||
|
@ -19,16 +18,6 @@
|
|||
|
||||
namespace Dynarmic::A32 {
|
||||
|
||||
static bool CondCanContinue(ConditionalState cond_state, const A32::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, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) {
|
||||
const bool single_step = descriptor.SingleStepping();
|
||||
|
||||
|
@ -102,53 +91,7 @@ bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descript
|
|||
}
|
||||
|
||||
bool ArmTranslatorVisitor::ConditionPassed(Cond cond) {
|
||||
ASSERT_MSG(cond_state != ConditionalState::Break,
|
||||
"This should never happen. We requested a break but that wasn't honored.");
|
||||
if (cond == Cond::NV) {
|
||||
// NV conditional is obsolete
|
||||
ir.ExceptionRaised(Exception::UnpredictableInstruction);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cond_state == ConditionalState::Translating) {
|
||||
if (ir.block.ConditionFailedLocation() != ir.current_location || cond == Cond::AL) {
|
||||
cond_state = ConditionalState::Trailing;
|
||||
} else {
|
||||
if (cond == ir.block.GetCondition()) {
|
||||
ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(4));
|
||||
ir.block.ConditionFailedCycleCount()++;
|
||||
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;
|
||||
}
|
||||
|
||||
// non-AL cond
|
||||
|
||||
if (!ir.block.empty()) {
|
||||
// We've already emitted instructions. Quit for now, we'll make a new block here later.
|
||||
cond_state = ConditionalState::Break;
|
||||
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location});
|
||||
return false;
|
||||
}
|
||||
|
||||
// We've not emitted instructions yet.
|
||||
// We'll emit one instruction, and set the block-entry conditional appropriately.
|
||||
|
||||
cond_state = ConditionalState::Translating;
|
||||
ir.block.SetCondition(cond);
|
||||
ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(4));
|
||||
ir.block.ConditionFailedCycleCount() = ir.block.CycleCount() + 1;
|
||||
return true;
|
||||
return IsConditionPassed(cond, cond_state, ir, 4);
|
||||
}
|
||||
|
||||
bool ArmTranslatorVisitor::InterpretThisInstruction() {
|
||||
|
|
Loading…
Reference in a new issue