A32/thumb16: Implement IT instruction
This commit is contained in:
parent
97d8b50c25
commit
f77b0e2fbe
6 changed files with 155 additions and 29 deletions
|
@ -60,12 +60,15 @@ INST(thumb16_ADD_sp_t2, "ADD (SP plus imm, T2)", "101100000vvvvvvv") //
|
|||
INST(thumb16_SUB_sp, "SUB (SP minus imm)", "101100001vvvvvvv") // v4T
|
||||
|
||||
// Hint instructions
|
||||
INST(thumb16_NOP, "NOP", "1011111100000000") // v6T2
|
||||
INST(thumb16_SEV, "SEV", "1011111101000000") // v7
|
||||
INST(thumb16_SEVL, "SEVL", "1011111101010000") // v8
|
||||
INST(thumb16_WFE, "WFE", "1011111100100000") // v7
|
||||
INST(thumb16_WFI, "WFI", "1011111100110000") // v7
|
||||
INST(thumb16_YIELD, "YIELD", "1011111100010000") // v7
|
||||
INST(thumb16_NOP, "NOP", "10111111----0000") // v7
|
||||
|
||||
// IT instruction
|
||||
INST(thumb16_IT, "IT", "10111111iiiiiiii") // v7
|
||||
|
||||
// Miscellaneous 16-bit instructions
|
||||
INST(thumb16_SXTH, "SXTH", "1011001000mmmddd") // v6
|
||||
|
|
|
@ -269,6 +269,25 @@ public:
|
|||
return "yield";
|
||||
}
|
||||
|
||||
std::string thumb16_IT(Imm<8> imm8) {
|
||||
const Cond firstcond = imm8.Bits<4, 7, Cond>();
|
||||
const bool firstcond0 = imm8.Bit<4>();
|
||||
const auto [x, y, z] = [&]{
|
||||
if (imm8.Bits<0, 3>() == 0b1000) {
|
||||
return std::make_tuple("", "", "");
|
||||
}
|
||||
if (imm8.Bits<0, 2>() == 0b100) {
|
||||
return std::make_tuple(imm8.Bit<3>() == firstcond0 ? "t" : "e", "", "");
|
||||
}
|
||||
if (imm8.Bits<0, 1>() == 0b10) {
|
||||
return std::make_tuple(imm8.Bit<3>() == firstcond0 ? "t" : "e", imm8.Bit<2>() == firstcond0 ? "t" : "e", "");
|
||||
}
|
||||
// Sanity note: Here imm8.Bit<0>() is guaranteed to be == 1. (imm8 can never be 0bxxxx0000)
|
||||
return std::make_tuple(imm8.Bit<3>() == firstcond0 ? "t" : "e", imm8.Bit<2>() == firstcond0 ? "t" : "e", imm8.Bit<1>() == firstcond0 ? "t" : "e");
|
||||
}();
|
||||
return fmt::format("it{}{}{} {}", x, y, z, firstcond);
|
||||
}
|
||||
|
||||
std::string thumb16_SXTH(Reg m, Reg d) {
|
||||
return fmt::format("sxth {}, {}", d, m);
|
||||
}
|
||||
|
|
|
@ -697,11 +697,6 @@ bool ThumbTranslatorVisitor::thumb16_SUB_sp(Imm<7> imm7) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// NOP<c>
|
||||
bool ThumbTranslatorVisitor::thumb16_NOP() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// SEV<c>
|
||||
bool ThumbTranslatorVisitor::thumb16_SEV() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
|
@ -742,6 +737,26 @@ bool ThumbTranslatorVisitor::thumb16_YIELD() {
|
|||
return RaiseException(Exception::Yield);
|
||||
}
|
||||
|
||||
// NOP<c>
|
||||
bool ThumbTranslatorVisitor::thumb16_NOP() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// IT{<x>{<y>{<z>}}} <cond>
|
||||
bool ThumbTranslatorVisitor::thumb16_IT(Imm<8> imm8) {
|
||||
ASSERT_MSG((imm8.Bits<0, 3>() != 0b0000), "Decode Error");
|
||||
if (imm8.Bits<4, 7>() == 0b1111 || (imm8.Bits<4, 7>() == 0b1110 && Common::BitCount(imm8.Bits<0, 3>()) != 1)) {
|
||||
return UnpredictableInstruction();
|
||||
}
|
||||
if (ir.current_location.IT().IsInITBlock()) {
|
||||
return UnpredictableInstruction();
|
||||
}
|
||||
|
||||
const auto next_location = ir.current_location.AdvancePC(2).SetIT(ITState{imm8.ZeroExtend<u8>()});
|
||||
ir.SetTerm(IR::Term::LinkBlockFast{next_location});
|
||||
return false;
|
||||
}
|
||||
|
||||
// SXTH <Rd>, <Rm>
|
||||
// Rd cannot encode R15.
|
||||
bool ThumbTranslatorVisitor::thumb16_SXTH(Reg m, Reg d) {
|
||||
|
|
|
@ -9,6 +9,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"
|
||||
|
||||
|
@ -24,8 +25,11 @@ struct ThumbTranslatorVisitor final {
|
|||
}
|
||||
|
||||
A32::IREmitter ir;
|
||||
ConditionalState cond_state = ConditionalState::None;
|
||||
TranslationOptions options;
|
||||
|
||||
bool ConditionPassed(bool is_thumb_16);
|
||||
|
||||
bool InterpretThisInstruction();
|
||||
bool UnpredictableInstruction();
|
||||
bool UndefinedInstruction();
|
||||
|
@ -83,12 +87,13 @@ struct ThumbTranslatorVisitor final {
|
|||
bool thumb16_ADD_sp_t1(Reg d, Imm<8> imm8);
|
||||
bool thumb16_ADD_sp_t2(Imm<7> imm7);
|
||||
bool thumb16_SUB_sp(Imm<7> imm7);
|
||||
bool thumb16_NOP();
|
||||
bool thumb16_SEV();
|
||||
bool thumb16_SEVL();
|
||||
bool thumb16_WFE();
|
||||
bool thumb16_WFI();
|
||||
bool thumb16_YIELD();
|
||||
bool thumb16_NOP();
|
||||
bool thumb16_IT(Imm<8> imm8);
|
||||
bool thumb16_SXTH(Reg m, Reg d);
|
||||
bool thumb16_SXTB(Reg m, Reg d);
|
||||
bool thumb16_UXTH(Reg m, Reg d);
|
||||
|
|
|
@ -9,13 +9,14 @@
|
|||
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "frontend/imm.h"
|
||||
#include "frontend/A32/decoder/thumb16.h"
|
||||
#include "frontend/A32/decoder/thumb32.h"
|
||||
#include "frontend/A32/ir_emitter.h"
|
||||
#include "frontend/A32/location_descriptor.h"
|
||||
#include "frontend/A32/translate/conditional_state.h"
|
||||
#include "frontend/A32/translate/impl/translate_thumb.h"
|
||||
#include "frontend/A32/translate/translate.h"
|
||||
#include "frontend/imm.h"
|
||||
|
||||
namespace Dynarmic::A32 {
|
||||
namespace {
|
||||
|
@ -28,6 +29,16 @@ bool IsThumb16(u16 first_part) {
|
|||
return (first_part & 0xF800) < 0xE800;
|
||||
}
|
||||
|
||||
bool IsUnconditionalInstruction(bool is_thumb_16, u32 instruction) {
|
||||
if (!is_thumb_16)
|
||||
return false;
|
||||
if ((instruction & 0xFF00) == 0b10111110'00000000) // BKPT
|
||||
return true;
|
||||
if ((instruction & 0xFFC0) == 0b10111010'10000000) // HLT
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::tuple<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, MemoryReadCodeFuncType memory_read_code) {
|
||||
u32 first_part = memory_read_code(arm_pc & 0xFFFFFFFC);
|
||||
if ((arm_pc & 0x2) != 0) {
|
||||
|
@ -64,8 +75,10 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType m
|
|||
do {
|
||||
const u32 arm_pc = visitor.ir.current_location.PC();
|
||||
const auto [thumb_instruction, inst_size] = ReadThumbInstruction(arm_pc, memory_read_code);
|
||||
const bool is_thumb_16 = inst_size == ThumbInstSize::Thumb16;
|
||||
|
||||
if (inst_size == ThumbInstSize::Thumb16) {
|
||||
if (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ConditionPassed(is_thumb_16)) {
|
||||
if (is_thumb_16) {
|
||||
if (const auto decoder = DecodeThumb16<ThumbTranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
|
||||
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
|
||||
} else {
|
||||
|
@ -78,16 +91,28 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType m
|
|||
should_continue = visitor.thumb32_UDF();
|
||||
}
|
||||
}
|
||||
|
||||
const s32 advance_pc = (inst_size == ThumbInstSize::Thumb16) ? 2 : 4;
|
||||
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(advance_pc);
|
||||
block.CycleCount()++;
|
||||
} while (should_continue && !single_step);
|
||||
|
||||
if (single_step && should_continue) {
|
||||
visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location});
|
||||
}
|
||||
|
||||
if (visitor.cond_state == ConditionalState::Break) {
|
||||
break;
|
||||
}
|
||||
|
||||
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(is_thumb_16 ? 2 : 4).AdvanceIT();
|
||||
block.CycleCount()++;
|
||||
} while (should_continue && CondCanContinue(visitor.cond_state, visitor.ir) && !single_step);
|
||||
|
||||
if (visitor.cond_state == ConditionalState::Translating || visitor.cond_state == ConditionalState::Trailing || single_step) {
|
||||
if (should_continue) {
|
||||
if (single_step) {
|
||||
visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location});
|
||||
} else {
|
||||
visitor.ir.SetTerm(IR::Term::LinkBlockFast{visitor.ir.current_location});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_MSG(block.HasTerminal(), "Terminal has not been set");
|
||||
|
||||
block.SetEndLocation(visitor.ir.current_location);
|
||||
|
||||
return block;
|
||||
|
@ -122,6 +147,11 @@ bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descri
|
|||
return should_continue;
|
||||
}
|
||||
|
||||
bool ThumbTranslatorVisitor::ConditionPassed(bool is_thumb_16) {
|
||||
const Cond cond = ir.current_location.IT().Cond();
|
||||
return IsConditionPassed(cond, cond_state, ir, is_thumb_16 ? 2 : 4);
|
||||
}
|
||||
|
||||
bool ThumbTranslatorVisitor::InterpretThisInstruction() {
|
||||
ir.SetTerm(IR::Term::Interpret(ir.current_location));
|
||||
return false;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "common/fp/fpsr.h"
|
||||
#include "common/llvm_disassemble.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "frontend/A32/ITState.h"
|
||||
#include "frontend/A32/location_descriptor.h"
|
||||
#include "frontend/A32/translate/translate.h"
|
||||
#include "frontend/A32/types.h"
|
||||
|
@ -36,8 +37,8 @@
|
|||
namespace {
|
||||
using namespace Dynarmic;
|
||||
|
||||
bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst) {
|
||||
const A32::LocationDescriptor location = A32::LocationDescriptor{pc, {}, {}}.SetTFlag(is_thumb);
|
||||
bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) {
|
||||
const A32::LocationDescriptor location = A32::LocationDescriptor{pc, {}, {}}.SetTFlag(is_thumb).SetIT(it_state);
|
||||
IR::Block block{location};
|
||||
const bool should_continue = A32::TranslateSingleInstruction(block, location, instruction);
|
||||
|
||||
|
@ -145,7 +146,7 @@ u32 GenRandomArmInst(u32 pc, bool is_last_inst) {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<u16> GenRandomThumbInst(u32 pc, bool is_last_inst) {
|
||||
std::vector<u16> GenRandomThumbInst(u32 pc, bool is_last_inst, A32::ITState it_state = {}) {
|
||||
static const struct InstructionGeneratorInfo {
|
||||
std::vector<InstructionGenerator> generators;
|
||||
std::vector<InstructionGenerator> invalid;
|
||||
|
@ -162,8 +163,9 @@ std::vector<u16> GenRandomThumbInst(u32 pc, bool is_last_inst) {
|
|||
|
||||
// List of instructions not to test
|
||||
static constexpr std::array do_not_test {
|
||||
"thumb16_SETEND",
|
||||
"thumb16_BKPT",
|
||||
"thumb16_IT",
|
||||
"thumb16_SETEND",
|
||||
};
|
||||
|
||||
for (const auto& [fn, bitstring] : list) {
|
||||
|
@ -181,7 +183,7 @@ std::vector<u16> GenRandomThumbInst(u32 pc, bool is_last_inst) {
|
|||
const u32 inst = instructions.generators[index].Generate();
|
||||
const bool is_four_bytes = (inst >> 16) != 0;
|
||||
|
||||
if (ShouldTestInst(is_four_bytes ? Common::SwapHalves32(inst) : inst, pc, true, is_last_inst)) {
|
||||
if (ShouldTestInst(is_four_bytes ? Common::SwapHalves32(inst) : inst, pc, true, is_last_inst, it_state)) {
|
||||
if (is_four_bytes)
|
||||
return { static_cast<u16>(inst >> 16), static_cast<u16>(inst) };
|
||||
return { static_cast<u16>(inst) };
|
||||
|
@ -480,3 +482,55 @@ TEST_CASE("A32: Small random thumb block", "[thumb]") {
|
|||
RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 5);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("A32: Test thumb IT instruction", "[thumb]") {
|
||||
ThumbTestEnv jit_env{};
|
||||
ThumbTestEnv uni_env{};
|
||||
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(jit_env)};
|
||||
A32Unicorn<ThumbTestEnv> uni{uni_env};
|
||||
|
||||
A32Unicorn<ThumbTestEnv>::RegisterArray regs;
|
||||
A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg;
|
||||
std::vector<u16> instructions;
|
||||
|
||||
for (size_t iteration = 0; iteration < 100000; ++iteration) {
|
||||
std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
|
||||
std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
|
||||
|
||||
const size_t pre_instructions = RandInt<size_t>(0, 3);
|
||||
const size_t post_instructions = RandInt<size_t>(5, 8);
|
||||
|
||||
instructions.clear();
|
||||
|
||||
for (size_t i = 0; i < pre_instructions; i++) {
|
||||
const std::vector<u16> inst = GenRandomThumbInst(instructions.size() * 2, false);
|
||||
instructions.insert(instructions.end(), inst.begin(), inst.end());
|
||||
}
|
||||
|
||||
// Emit IT instruction
|
||||
A32::ITState it_state = [&]{
|
||||
while (true) {
|
||||
const u16 imm8 = RandInt<u16>(0, 0xFF);
|
||||
if (Common::Bits<0, 3>(imm8) == 0b0000 || Common::Bits<4, 7>(imm8) == 0b1111 || (Common::Bits<4, 7>(imm8) == 0b1110 && Common::BitCount(Common::Bits<0, 3>(imm8)) != 1)) {
|
||||
continue;
|
||||
}
|
||||
instructions.push_back(0b1011111100000000 | imm8);
|
||||
return A32::ITState{static_cast<u8>(imm8)};
|
||||
}
|
||||
}();
|
||||
|
||||
for (size_t i = 0; i < post_instructions; i++) {
|
||||
const std::vector<u16> inst = GenRandomThumbInst(instructions.size() * 2, i == post_instructions - 1, it_state);
|
||||
instructions.insert(instructions.end(), inst.begin(), inst.end());
|
||||
it_state = it_state.Advance();
|
||||
}
|
||||
|
||||
const u32 start_address = 100;
|
||||
const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0;
|
||||
const u32 fpcr = RandomFpcr();
|
||||
|
||||
regs[15] = start_address;
|
||||
RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, pre_instructions + 1 + post_instructions);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue