Implement Thumb PUSH instruction
This commit is contained in:
parent
9109b226af
commit
f7e3d7b8d2
10 changed files with 191 additions and 54 deletions
|
@ -106,7 +106,7 @@ Gen::X64Reg RegAlloc::UseRegister(IR::Value* use_value, std::initializer_list<Ho
|
|||
} else if (HostLocIsRegister(current_location)) {
|
||||
ASSERT(hostloc_state[current_location] == HostLocState::Idle);
|
||||
|
||||
code->XCHG(32, Gen::R(hostloc_to_x64.at(current_location)), Gen::R(hostloc_to_x64.at(new_location)));
|
||||
code->XCHG(32, Gen::R(hostloc_to_x64.at(new_location)), Gen::R(hostloc_to_x64.at(current_location)));
|
||||
|
||||
hostloc_state[new_location] = HostLocState::Use;
|
||||
std::swap(hostloc_to_value[new_location], hostloc_to_value[current_location]);
|
||||
|
@ -118,6 +118,43 @@ Gen::X64Reg RegAlloc::UseRegister(IR::Value* use_value, std::initializer_list<Ho
|
|||
return hostloc_to_x64.at(new_location);
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::UseScratchRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations) {
|
||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||
ASSERT_MSG(remaining_uses.find(use_value) != remaining_uses.end(), "use_value has not been defined");
|
||||
ASSERT_MSG(!ValueLocations(use_value).empty(), "use_value has not been defined");
|
||||
ASSERT_MSG(remaining_uses[use_value] != 0, "use_value ran out of uses. (Use-d an IR::Value* too many times)");
|
||||
|
||||
HostLoc current_location = ValueLocations(use_value).front();
|
||||
HostLoc new_location = SelectARegister(desired_locations);
|
||||
|
||||
if (HostLocIsSpill(current_location)) {
|
||||
if (IsRegisterOccupied(new_location)) {
|
||||
SpillRegister(new_location);
|
||||
}
|
||||
|
||||
code->MOV(32, Gen::R(hostloc_to_x64.at(new_location)), SpillToOpArg(current_location));
|
||||
|
||||
hostloc_state[new_location] = HostLocState::Scratch;
|
||||
remaining_uses[use_value]--;
|
||||
} else if (HostLocIsRegister(current_location)) {
|
||||
ASSERT(hostloc_state[current_location] == HostLocState::Idle);
|
||||
|
||||
if (IsRegisterOccupied(new_location)) {
|
||||
SpillRegister(new_location);
|
||||
}
|
||||
|
||||
code->MOV(32, Gen::R(hostloc_to_x64.at(new_location)), Gen::R(hostloc_to_x64.at(current_location)));
|
||||
|
||||
hostloc_state[new_location] = HostLocState::Scratch;
|
||||
remaining_uses[use_value]--;
|
||||
} else {
|
||||
ASSERT_MSG(0, "Invalid current_location");
|
||||
}
|
||||
|
||||
return hostloc_to_x64.at(new_location);
|
||||
}
|
||||
|
||||
|
||||
Gen::X64Reg RegAlloc::ScratchRegister(std::initializer_list<HostLoc> desired_locations) {
|
||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||
|
||||
|
@ -161,7 +198,7 @@ void RegAlloc::HostCall(IR::Value* result_def, IR::Value* arg0_use, IR::Value* a
|
|||
|
||||
for (size_t i = 0; i < AbiArgs.size(); i++) {
|
||||
if (args[i]) {
|
||||
UseRegister(args[i], {AbiArgs[i]});
|
||||
UseScratchRegister(args[i], {AbiArgs[i]});
|
||||
} else {
|
||||
ScratchRegister({AbiArgs[i]});
|
||||
}
|
||||
|
|
|
@ -71,6 +71,8 @@ public:
|
|||
Gen::X64Reg UseDefRegister(IR::Value* use_value, IR::Value* def_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||
/// Early-use
|
||||
Gen::X64Reg UseRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||
/// Early-use, Destroyed
|
||||
Gen::X64Reg UseScratchRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||
/// Early-def, Late-use, single-use
|
||||
Gen::X64Reg ScratchRegister(std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
|
||||
#include <climits>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4554)
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
namespace Dynarmic {
|
||||
|
@ -35,6 +35,10 @@ constexpr T Bits(const T value) {
|
|||
return (value >> begin_bit) & ((1 << (end_bit - begin_bit + 1)) - 1);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4554)
|
||||
#endif
|
||||
/// Extracts a single bit at bit_position from value of type T.
|
||||
template<size_t bit_position, typename T>
|
||||
constexpr bool Bit(const T value) {
|
||||
|
@ -43,6 +47,17 @@ constexpr bool Bit(const T value) {
|
|||
return ((value >> bit_position) & 1) != 0;
|
||||
}
|
||||
|
||||
/// Extracts a single bit at bit_position from value of type T.
|
||||
template<typename T>
|
||||
constexpr bool Bit(size_t bit_position, const T value) {
|
||||
ASSERT_MSG(bit_position < BitSize<T>(), "bit_position must be smaller than size of T");
|
||||
|
||||
return ((value >> bit_position) & 1) != 0;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
/// Sign-extends a value that has NBits bits to the full bitwidth of type T.
|
||||
template<size_t bit_count, typename T>
|
||||
inline T SignExtend(const T value) {
|
||||
|
@ -56,9 +71,13 @@ inline T SignExtend(const T value) {
|
|||
return value;
|
||||
}
|
||||
|
||||
inline size_t BitCount(u32 value) {
|
||||
#ifdef _MSC_VER
|
||||
return __popcnt(value);
|
||||
#else
|
||||
return __builtin_popcount(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Dynarmic
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
|
|
@ -124,7 +124,7 @@ boost::optional<const Thumb16Matcher<V>&> DecodeThumb16(u16 instruction) {
|
|||
INST(&V::thumb16_SXTB, "SXTB", "1011001001mmmddd"), // v6
|
||||
INST(&V::thumb16_UXTH, "UXTH", "1011001010mmmddd"), // v6
|
||||
INST(&V::thumb16_UXTB, "UXTB", "1011001011mmmddd"), // v6
|
||||
//INST(&V::thumb16_PUSH, "PUSH", "1011010rxxxxxxxx"), // v4T
|
||||
INST(&V::thumb16_PUSH, "PUSH", "1011010Mxxxxxxxx"), // v4T
|
||||
//INST(&V::thumb16_POP, "POP", "1011110rxxxxxxxx"), // v4T
|
||||
//INST(&V::thumb16_SETEND, "SETEND", "101101100101x000"), // v6
|
||||
//INST(&V::thumb16_CPS, "CPS", "10110110011m0aif"), // v6
|
||||
|
|
|
@ -280,6 +280,24 @@ public:
|
|||
return Common::StringFromFormat("uxtb %s, %s", RegStr(d), RegStr(m));
|
||||
}
|
||||
|
||||
std::string thumb16_PUSH(bool M, RegList reg_list) {
|
||||
if (M)
|
||||
reg_list |= 1 << 14;
|
||||
|
||||
std::string ret = "PUSH ";
|
||||
bool first_reg = true;
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
if (Common::Bit(i, reg_list)) {
|
||||
if (!first_reg)
|
||||
ret += ", ";
|
||||
ret += RegStr(static_cast<Reg>(i));
|
||||
first_reg = false;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string thumb16_REV(Reg m, Reg d) {
|
||||
return Common::StringFromFormat("rev %s, %s", RegStr(d), RegStr(m));
|
||||
}
|
||||
|
|
|
@ -155,6 +155,10 @@ IREmitter::ResultAndCarryAndOverflow IREmitter::SubWithCarry(IR::ValuePtr a, IR:
|
|||
return {result, carry_out, overflow};
|
||||
}
|
||||
|
||||
IR::ValuePtr IREmitter::Sub(IR::ValuePtr a, IR::ValuePtr b) {
|
||||
return Inst(IR::Opcode::SubWithCarry, {a, b, Imm1(1)});
|
||||
}
|
||||
|
||||
IR::ValuePtr IREmitter::And(IR::ValuePtr a, IR::ValuePtr b) {
|
||||
return Inst(IR::Opcode::And, {a, b});
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ public:
|
|||
ResultAndCarryAndOverflow AddWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in);
|
||||
IR::ValuePtr Add(IR::ValuePtr a, IR::ValuePtr b);
|
||||
ResultAndCarryAndOverflow SubWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in);
|
||||
IR::ValuePtr Sub(IR::ValuePtr a, IR::ValuePtr b);
|
||||
IR::ValuePtr And(IR::ValuePtr a, IR::ValuePtr b);
|
||||
IR::ValuePtr Eor(IR::ValuePtr a, IR::ValuePtr b);
|
||||
IR::ValuePtr Or(IR::ValuePtr a, IR::ValuePtr b);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <tuple>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "frontend/arm_types.h"
|
||||
#include "frontend/decoder/thumb16.h"
|
||||
#include "frontend/ir/ir_emitter.h"
|
||||
|
@ -507,6 +508,28 @@ struct ThumbTranslatorVisitor final {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool thumb16_PUSH(bool M, RegList reg_list) {
|
||||
if (M) reg_list |= 1 << 14;
|
||||
if (Common::BitCount(reg_list) < 1) {
|
||||
return UnpredictableInstruction();
|
||||
}
|
||||
// PUSH <reg_list>
|
||||
// reg_list cannot encode for R15.
|
||||
u32 num_bytes_to_push = static_cast<u32>(4 * Common::BitCount(reg_list));
|
||||
const auto final_address = ir.Sub(ir.GetRegister(Reg::SP), ir.Imm32(num_bytes_to_push));
|
||||
auto address = final_address;
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
if (Common::Bit(i, reg_list)) {
|
||||
auto Ri = ir.GetRegister(static_cast<Reg>(i));
|
||||
ir.WriteMemory32(address, Ri);
|
||||
address = ir.Add(address, ir.Imm32(4));
|
||||
}
|
||||
}
|
||||
ir.SetRegister(Reg::SP, final_address);
|
||||
// TODO(optimization): Possible location for an RSB push.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool thumb16_REV(Reg m, Reg d) {
|
||||
// REV <Rd>, <Rm>
|
||||
// Rd cannot encode R15.
|
||||
|
|
|
@ -115,9 +115,9 @@ static Dynarmic::UserCallbacks GetUserCallbacks() {
|
|||
return user_callbacks;
|
||||
}
|
||||
|
||||
struct InstructionGenerator final {
|
||||
struct ThumbInstGen final {
|
||||
public:
|
||||
InstructionGenerator(const char* format, std::function<bool(u16)> is_valid = [](u16){ return true; }) : is_valid(is_valid) {
|
||||
ThumbInstGen(const char* format, std::function<bool(u16)> is_valid = [](u16){ return true; }) : is_valid(is_valid) {
|
||||
REQUIRE(strlen(format) == 16);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
|
@ -138,10 +138,14 @@ public:
|
|||
}
|
||||
u16 Generate() const {
|
||||
u16 inst;
|
||||
|
||||
do {
|
||||
u16 random = RandInt<u16>(0, 0xFFFF);
|
||||
inst = bits | (random & ~mask);
|
||||
} while (!is_valid(inst));
|
||||
|
||||
ASSERT((inst & mask) == bits);
|
||||
|
||||
return inst;
|
||||
}
|
||||
private:
|
||||
|
@ -223,6 +227,16 @@ void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_e
|
|||
}
|
||||
printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : "");
|
||||
|
||||
printf("\nInterp Write Records:\n");
|
||||
for (auto& record : interp_write_records) {
|
||||
printf("%zu [%x] = %llx\n", record.size, record.address, record.data);
|
||||
}
|
||||
|
||||
printf("\nJIT Write Records:\n");
|
||||
for (auto& record : jit_write_records) {
|
||||
printf("%zu [%x] = %llx\n", record.size, record.address, record.data);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
__debugbreak();
|
||||
#endif
|
||||
|
@ -234,33 +248,35 @@ void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_e
|
|||
}
|
||||
|
||||
TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
|
||||
const std::array<InstructionGenerator, 23> instructions = {{
|
||||
InstructionGenerator("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
|
||||
InstructionGenerator("00001xxxxxxxxxxx"), // LSR <Rd>, <Rm>, #<imm5>
|
||||
InstructionGenerator("00010xxxxxxxxxxx"), // ASR <Rd>, <Rm>, #<imm5>
|
||||
InstructionGenerator("000110oxxxxxxxxx"), // ADD/SUB_reg
|
||||
InstructionGenerator("000111oxxxxxxxxx"), // ADD/SUB_imm
|
||||
InstructionGenerator("001ooxxxxxxxxxxx"), // ADD/SUB/CMP/MOV_imm
|
||||
InstructionGenerator("010000ooooxxxxxx"), // Data Processing
|
||||
InstructionGenerator("010001000hxxxxxx"), // ADD (high registers)
|
||||
InstructionGenerator("0100010101xxxxxx", // CMP (high registers)
|
||||
[](u16 inst){ return Dynarmic::Common::Bits<3, 5>(inst) != 0b111; }), // R15 is UNPREDICTABLE
|
||||
InstructionGenerator("0100010110xxxxxx", // CMP (high registers)
|
||||
[](u16 inst){ return Dynarmic::Common::Bits<0, 2>(inst) != 0b111; }), // R15 is UNPREDICTABLE
|
||||
InstructionGenerator("010001100hxxxxxx"), // MOV (high registers)
|
||||
InstructionGenerator("10110000oxxxxxxx"), // Adjust stack pointer
|
||||
InstructionGenerator("10110010ooxxxxxx"), // SXT/UXT
|
||||
InstructionGenerator("1011101000xxxxxx"), // REV
|
||||
InstructionGenerator("1011101001xxxxxx"), // REV16
|
||||
InstructionGenerator("1011101011xxxxxx"), // REVSH
|
||||
InstructionGenerator("01001xxxxxxxxxxx"), // LDR Rd, [PC, #]
|
||||
InstructionGenerator("0101oooxxxxxxxxx"), // LDR/STR Rd, [Rn, Rm]
|
||||
InstructionGenerator("011xxxxxxxxxxxxx"), // LDR(B)/STR(B) Rd, [Rn, #]
|
||||
InstructionGenerator("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset]
|
||||
InstructionGenerator("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #]
|
||||
InstructionGenerator("1011x100xxxxxxxx"), // PUSH/POP (R = 0)
|
||||
InstructionGenerator("1100xxxxxxxxxxxx"), // STMIA/LDMIA
|
||||
//InstructionGenerator("101101100101x000"), // SETEND
|
||||
const std::array<ThumbInstGen, 24> instructions = {{
|
||||
ThumbInstGen("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
|
||||
ThumbInstGen("00001xxxxxxxxxxx"), // LSR <Rd>, <Rm>, #<imm5>
|
||||
ThumbInstGen("00010xxxxxxxxxxx"), // ASR <Rd>, <Rm>, #<imm5>
|
||||
ThumbInstGen("000110oxxxxxxxxx"), // ADD/SUB_reg
|
||||
ThumbInstGen("000111oxxxxxxxxx"), // ADD/SUB_imm
|
||||
ThumbInstGen("001ooxxxxxxxxxxx"), // ADD/SUB/CMP/MOV_imm
|
||||
ThumbInstGen("010000ooooxxxxxx"), // Data Processing
|
||||
ThumbInstGen("010001000hxxxxxx"), // ADD (high registers)
|
||||
ThumbInstGen("0100010101xxxxxx", // CMP (high registers)
|
||||
[](u16 inst){ return Dynarmic::Common::Bits<3, 5>(inst) != 0b111; }), // R15 is UNPREDICTABLE
|
||||
ThumbInstGen("0100010110xxxxxx", // CMP (high registers)
|
||||
[](u16 inst){ return Dynarmic::Common::Bits<0, 2>(inst) != 0b111; }), // R15 is UNPREDICTABLE
|
||||
ThumbInstGen("010001100hxxxxxx"), // MOV (high registers)
|
||||
ThumbInstGen("10110000oxxxxxxx"), // Adjust stack pointer
|
||||
ThumbInstGen("10110010ooxxxxxx"), // SXT/UXT
|
||||
ThumbInstGen("1011101000xxxxxx"), // REV
|
||||
ThumbInstGen("1011101001xxxxxx"), // REV16
|
||||
ThumbInstGen("1011101011xxxxxx"), // REVSH
|
||||
ThumbInstGen("01001xxxxxxxxxxx"), // LDR Rd, [PC, #]
|
||||
ThumbInstGen("0101oooxxxxxxxxx"), // LDR/STR Rd, [Rn, Rm]
|
||||
ThumbInstGen("011xxxxxxxxxxxxx"), // LDR(B)/STR(B) Rd, [Rn, #]
|
||||
ThumbInstGen("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset]
|
||||
ThumbInstGen("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #]
|
||||
ThumbInstGen("10110100xxxxxxxx", // PUSH (R = 0)
|
||||
[](u16 inst){ return Dynarmic::Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
|
||||
ThumbInstGen("10111100xxxxxxxx"), // POP (R = 0)
|
||||
ThumbInstGen("1100xxxxxxxxxxxx"), // STMIA/LDMIA
|
||||
//ThumbInstGen("101101100101x000"), // SETEND
|
||||
}};
|
||||
|
||||
auto instruction_select = [&]() -> u16 {
|
||||
|
@ -283,22 +299,22 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
|
|||
}
|
||||
|
||||
TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
|
||||
const std::array<InstructionGenerator, 7> instructions = {{
|
||||
InstructionGenerator("01000111xmmmm000", // BLX/BX
|
||||
[](u16 inst){
|
||||
u32 Rm = Dynarmic::Common::Bits<3, 6>(inst);
|
||||
return Rm != 15;
|
||||
}),
|
||||
InstructionGenerator("1010oxxxxxxxxxxx"), // add to pc/sp
|
||||
InstructionGenerator("11100xxxxxxxxxxx"), // B
|
||||
InstructionGenerator("01000100h0xxxxxx"), // ADD (high registers)
|
||||
InstructionGenerator("01000110h0xxxxxx"), // MOV (high registers)
|
||||
InstructionGenerator("1101ccccxxxxxxxx", // B<cond>
|
||||
[](u16 inst){
|
||||
u32 c = Dynarmic::Common::Bits<9, 12>(inst);
|
||||
return c < 0b1110; // Don't want SWI or undefined instructions.
|
||||
}),
|
||||
InstructionGenerator("10110110011x0xxx"), // CPS
|
||||
const std::array<ThumbInstGen, 7> instructions = {{
|
||||
ThumbInstGen("01000111xmmmm000", // BLX/BX
|
||||
[](u16 inst){
|
||||
u32 Rm = Dynarmic::Common::Bits<3, 6>(inst);
|
||||
return Rm != 15;
|
||||
}),
|
||||
ThumbInstGen("1010oxxxxxxxxxxx"), // add to pc/sp
|
||||
ThumbInstGen("11100xxxxxxxxxxx"), // B
|
||||
ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers)
|
||||
ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers)
|
||||
ThumbInstGen("1101ccccxxxxxxxx", // B<cond>
|
||||
[](u16 inst){
|
||||
u32 c = Dynarmic::Common::Bits<9, 12>(inst);
|
||||
return c < 0b1110; // Don't want SWI or undefined instructions.
|
||||
}),
|
||||
ThumbInstGen("10110110011x0xxx"), // CPS
|
||||
}};
|
||||
|
||||
auto instruction_select = [&]() -> u16 {
|
||||
|
|
|
@ -103,3 +103,20 @@ TEST_CASE( "thumb: revsh r4, r3", "[thumb]" ) {
|
|||
REQUIRE( jit.Regs()[15] == 2 );
|
||||
REQUIRE( jit.Cpsr() == 0x00000030 ); // Thumb, User-mode
|
||||
}
|
||||
|
||||
TEST_CASE( "thumb: ldr r3, [r3, #28]", "[thumb]" ) {
|
||||
Dynarmic::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0x69DB; // ldr r3, [r3, #28]
|
||||
code_mem[1] = 0xE7FE; // b +#0
|
||||
|
||||
jit.Regs()[3] = 0x12345678;
|
||||
jit.Regs()[15] = 0; // PC = 0
|
||||
jit.Cpsr() = 0x00000030; // Thumb, User-mode
|
||||
|
||||
jit.Run(1);
|
||||
|
||||
REQUIRE( jit.Regs()[3] == 0x12345694 );
|
||||
REQUIRE( jit.Regs()[15] == 2 );
|
||||
REQUIRE( jit.Cpsr() == 0x00000030 ); // Thumb, User-mode
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue