Implement Thumb PUSH instruction

This commit is contained in:
MerryMage 2016-07-18 15:11:16 +01:00
parent 9109b226af
commit f7e3d7b8d2
10 changed files with 191 additions and 54 deletions

View file

@ -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]});
}

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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));
}

View file

@ -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});
}

View file

@ -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);

View file

@ -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.