Implement thumb1_LDR_literal, thumb1_LDR_imm_t1

This commit is contained in:
MerryMage 2016-07-11 22:43:53 +01:00
parent cbcf61a9e6
commit e7922e4fef
11 changed files with 215 additions and 5 deletions

View file

@ -69,6 +69,8 @@ CodePtr EmitX64::Emit(Arm::LocationDescriptor descriptor, Dynarmic::IR::Block bl
EmitAddCycles(block.cycle_count);
EmitTerminal(block.terminal, block.location);
reg_alloc.AssertNoMoreUses();
return code_ptr;
}
@ -276,6 +278,8 @@ void EmitX64::EmitLogicalShiftLeft(IR::Value* value_) {
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
X64Reg carry = reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst);
reg_alloc.DecrementRemainingUses(value);
// TODO: Optimize this.
code->CMP(8, R(shift), Imm8(32));
@ -325,6 +329,8 @@ void EmitX64::EmitLogicalShiftRight(IR::Value* value_) {
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
X64Reg carry = reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst);
reg_alloc.DecrementRemainingUses(value);
// TODO: Optimize this.
code->CMP(8, R(shift), Imm8(32));
@ -385,6 +391,8 @@ void EmitX64::EmitArithmeticShiftRight(IR::Value* value_) {
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
X64Reg carry = reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst);
reg_alloc.DecrementRemainingUses(value);
// TODO: Optimize this.
code->CMP(8, R(shift), Imm8(31));
@ -424,6 +432,8 @@ void EmitX64::EmitRotateRight(IR::Value* value_) {
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
X64Reg carry = reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst);
reg_alloc.DecrementRemainingUses(value);
// TODO: Optimize
// if (Rs & 0xFF == 0) goto end;
@ -467,10 +477,12 @@ void EmitX64::EmitAddWithCarry(IR::Value* value_) {
if (carry_inst) {
inhibit_emission.insert(carry_inst);
reg_alloc.DecrementRemainingUses(value);
code->SETcc(Gen::CC_C, R(carry));
}
if (overflow_inst) {
inhibit_emission.insert(overflow_inst);
reg_alloc.DecrementRemainingUses(value);
code->SETcc(Gen::CC_O, R(overflow));
}
}
@ -499,10 +511,12 @@ void EmitX64::EmitSubWithCarry(IR::Value* value_) {
if (carry_inst) {
inhibit_emission.insert(carry_inst);
reg_alloc.DecrementRemainingUses(value);
code->SETcc(Gen::CC_NC, R(carry));
}
if (overflow_inst) {
inhibit_emission.insert(overflow_inst);
reg_alloc.DecrementRemainingUses(value);
code->SETcc(Gen::CC_O, R(overflow));
}
}
@ -542,6 +556,71 @@ void EmitX64::EmitNot(IR::Value* value_) {
code->NOT(32, R(result));
}
void EmitX64::EmitReadMemory8(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
reg_alloc.HostCall(value, value->GetArg(0).get());
code->CALL(reinterpret_cast<void*>(cb.MemoryRead8));
}
void EmitX64::EmitReadMemory16(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
reg_alloc.HostCall(value, value->GetArg(0).get());
code->CALL(reinterpret_cast<void*>(cb.MemoryRead16));
}
void EmitX64::EmitReadMemory32(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
reg_alloc.HostCall(value, value->GetArg(0).get());
code->CALL(reinterpret_cast<void*>(cb.MemoryRead32));
}
void EmitX64::EmitReadMemory64(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
reg_alloc.HostCall(value, value->GetArg(0).get());
code->CALL(reinterpret_cast<void*>(cb.MemoryRead64));
}
void EmitX64::EmitWriteMemory8(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get());
code->CALL(reinterpret_cast<void*>(cb.MemoryWrite8));
}
void EmitX64::EmitWriteMemory16(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get());
code->CALL(reinterpret_cast<void*>(cb.MemoryWrite16));
}
void EmitX64::EmitWriteMemory32(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get());
code->CALL(reinterpret_cast<void*>(cb.MemoryWrite32));
}
void EmitX64::EmitWriteMemory64(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get());
code->CALL(reinterpret_cast<void*>(cb.MemoryWrite64));
}
void EmitX64::EmitAddCycles(size_t cycles) {
ASSERT(cycles < std::numeric_limits<u32>::max());
code->SUB(64, MDisp(R15, offsetof(JitState, cycles_remaining)), Imm32(static_cast<u32>(cycles)));

View file

@ -59,6 +59,14 @@ public:
void EmitEor(IR::Value* value);
void EmitOr(IR::Value* value);
void EmitNot(IR::Value* value);
void EmitReadMemory8(IR::Value* value);
void EmitReadMemory16(IR::Value* value);
void EmitReadMemory32(IR::Value* value);
void EmitReadMemory64(IR::Value* value);
void EmitWriteMemory8(IR::Value* value);
void EmitWriteMemory16(IR::Value* value);
void EmitWriteMemory32(IR::Value* value);
void EmitWriteMemory64(IR::Value* value);
void EmitAddCycles(size_t cycles);

View file

@ -236,6 +236,16 @@ void RegAlloc::EndOfAllocScope() {
iter.second = nullptr;
}
void RegAlloc::DecrementRemainingUses(IR::Value* value) {
ASSERT_MSG(remaining_uses.find(value) != remaining_uses.end(), "value does not exist");
ASSERT_MSG(remaining_uses[value] > 0, "value doesn't have any remaining uses");
remaining_uses[value]--;
}
void RegAlloc::AssertNoMoreUses() {
ASSERT(std::all_of(hostloc_to_value.begin(), hostloc_to_value.end(), [](const auto& pair){ return !pair.second; }));
}
void RegAlloc::Reset() {
hostloc_to_value.clear();
hostloc_state.clear();

View file

@ -79,8 +79,12 @@ public:
// TODO: Values in host flags
void DecrementRemainingUses(IR::Value* value);
void EndOfAllocScope();
void AssertNoMoreUses();
void Reset();
private:

View file

@ -56,7 +56,7 @@ private:
};
template <typename V>
static const std::array<Thumb1Matcher<V>, 30> g_thumb1_instruction_table {{
static const std::array<Thumb1Matcher<V>, 32> g_thumb1_instruction_table {{
#define INST(fn, name, bitstring) detail::detail<Thumb1Matcher, u16, 16>::GetMatcher<decltype(fn), fn>(name, bitstring)
@ -97,7 +97,7 @@ static const std::array<Thumb1Matcher<V>, 30> g_thumb1_instruction_table {{
{ INST(&V::thumb1_MOV_reg, "MOV (reg)", "01000110Dmmmmddd") }, // v4T, Low regs: v6
// Store/Load single data item instructions
//{ INST(&V::thumb1_LDR_lit, "LDR (literal)", "01001dddvvvvvvvv") },
{ INST(&V::thumb1_LDR_literal, "LDR (literal)", "01001tttvvvvvvvv") },
//{ INST(&V::thumb1_STR_rrr, "STR (rrr)", "0101000mmmnnnddd") },
//{ INST(&V::thumb1_STRH_rrr, "STRH (rrr)", "0101001mmmnnnddd") },
//{ INST(&V::thumb1_STRB_rrr, "STRB (rrr)", "0101010mmmnnnddd") },
@ -106,6 +106,7 @@ static const std::array<Thumb1Matcher<V>, 30> g_thumb1_instruction_table {{
//{ INST(&V::thumb1_LDRH_rrr, "LDRH (rrr)", "0101101mmmnnnddd") },
//{ INST(&V::thumb1_LDRB_rrr, "LDRB (rrr)", "0101110mmmnnnddd") },
//{ INST(&V::thumb1_LDRSH_rrr, "LDRSH (rrr)", "0101111mmmnnnddd") },
{ INST(&V::thumb1_LDR_imm_t1, "LDR (imm, T1)", "01101vvvvvnnnttt") },
//{ INST(&V::thumb1_STRH_rri, "STRH (rri)", "10000vvvvvnnnddd") },
//{ INST(&V::thumb1_LDRH_rri, "LDRH (rri)", "10001vvvvvnnnddd") },
//{ INST(&V::thumb1_STR_sp, "STR (SP)", "10010dddvvvvvvvv") },

View file

@ -222,6 +222,16 @@ public:
return Common::StringFromFormat("mov %s, %s", RegStr(d), RegStr(m));
}
std::string thumb1_LDR_literal(Reg t, Imm8 imm8) {
u32 imm32 = imm8 << 2;
return Common::StringFromFormat("ldr %s, [pc, #%u]", RegStr(t), imm32);
}
std::string thumb1_LDR_imm_t1(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5 << 2;
return Common::StringFromFormat("ldr %s, [%s, #%u]", RegStr(t), RegStr(n), imm32);
}
std::string thumb1_UDF() {
return Common::StringFromFormat("udf");
}

View file

@ -36,3 +36,13 @@ OPCODE(And, T::U32, T::U32, T::U32
OPCODE(Eor, T::U32, T::U32, T::U32 )
OPCODE(Or, T::U32, T::U32, T::U32 )
OPCODE(Not, T::U32, T::U32 )
// Memory access
OPCODE(ReadMemory8, T::U8, T::U32 )
OPCODE(ReadMemory16, T::U16, T::U32 )
OPCODE(ReadMemory32, T::U32, T::U32 )
OPCODE(ReadMemory64, T::U64, T::U32 )
OPCODE(WriteMemory8, T::Void, T::U32, T::U8 )
OPCODE(WriteMemory16, T::Void, T::U32, T::U16 )
OPCODE(WriteMemory32, T::Void, T::U32, T::U32 )
OPCODE(WriteMemory64, T::Void, T::U32, T::U64 )

View file

@ -14,6 +14,16 @@ void IREmitter::Unimplemented() {
}
u32 IREmitter::PC() {
u32 offset = current_location.TFlag ? 4 : 8;
return current_location.arm_pc + offset;
}
u32 IREmitter::AlignPC(size_t alignment) {
u32 pc = PC();
return static_cast<u32>(pc - pc % alignment);
}
IR::ValuePtr IREmitter::Imm1(bool value) {
auto imm1 = std::make_shared<IR::ImmU1>(value);
AddToBlock(imm1);
@ -34,13 +44,13 @@ IR::ValuePtr IREmitter::Imm32(u32 i) {
IR::ValuePtr IREmitter::GetRegister(Reg reg) {
if (reg == Reg::PC) {
u32 offset = current_location.TFlag ? 4 : 8;
return Imm32(current_location.arm_pc + offset);
return Imm32(PC());
}
return Inst(IR::Opcode::GetRegister, { RegRef(reg) });
}
void IREmitter::SetRegister(const Reg reg, IR::ValuePtr value) {
ASSERT(reg != Reg::PC);
Inst(IR::Opcode::SetRegister, { RegRef(reg), value });
}
@ -112,6 +122,10 @@ IREmitter::ResultAndCarryAndOverflow IREmitter::AddWithCarry(IR::ValuePtr a, IR:
return {result, carry_out, overflow};
}
IR::ValuePtr IREmitter::Add(IR::ValuePtr a, IR::ValuePtr b) {
return Inst(IR::Opcode::AddWithCarry, {a, b, Imm1(0)});
}
IREmitter::ResultAndCarryAndOverflow IREmitter::SubWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in) {
// This is equivalent to AddWithCarry(a, Not(b), carry_in).
auto result = Inst(IR::Opcode::SubWithCarry, {a, b, carry_in});
@ -136,6 +150,39 @@ IR::ValuePtr IREmitter::Not(IR::ValuePtr a) {
return Inst(IR::Opcode::Not, {a});
}
IR::ValuePtr IREmitter::ReadMemory8(IR::ValuePtr vaddr) {
return Inst(IR::Opcode::ReadMemory8, {vaddr});
}
IR::ValuePtr IREmitter::ReadMemory16(IR::ValuePtr vaddr) {
return Inst(IR::Opcode::ReadMemory16, {vaddr});
}
IR::ValuePtr IREmitter::ReadMemory32(IR::ValuePtr vaddr) {
return Inst(IR::Opcode::ReadMemory32, {vaddr});
}
IR::ValuePtr IREmitter::ReadMemory64(IR::ValuePtr vaddr) {
return Inst(IR::Opcode::ReadMemory64, {vaddr});
}
void IREmitter::WriteMemory8(IR::ValuePtr vaddr, IR::ValuePtr value) {
Inst(IR::Opcode::WriteMemory8, {vaddr, value});
}
void IREmitter::WriteMemory16(IR::ValuePtr vaddr, IR::ValuePtr value) {
Inst(IR::Opcode::WriteMemory16, {vaddr, value});
}
void IREmitter::WriteMemory32(IR::ValuePtr vaddr, IR::ValuePtr value) {
Inst(IR::Opcode::WriteMemory32, {vaddr, value});
}
void IREmitter::WriteMemory64(IR::ValuePtr vaddr, IR::ValuePtr value) {
Inst(IR::Opcode::WriteMemory64, {vaddr, value});
}
void IREmitter::SetTerm(const IR::Terminal& terminal) {
ASSERT_MSG(block.terminal.which() == 0, "Terminal has already been set.");
block.terminal = terminal;

View file

@ -32,6 +32,8 @@ public:
};
void Unimplemented();
u32 PC();
u32 AlignPC(size_t alignment);
IR::ValuePtr Imm1(bool value);
IR::ValuePtr Imm8(u8 value);
@ -57,12 +59,22 @@ public:
ResultAndCarry ArithmeticShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
ResultAndCarry RotateRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
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 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);
IR::ValuePtr Not(IR::ValuePtr a);
IR::ValuePtr ReadMemory8(IR::ValuePtr vaddr);
IR::ValuePtr ReadMemory16(IR::ValuePtr vaddr);
IR::ValuePtr ReadMemory32(IR::ValuePtr vaddr);
IR::ValuePtr ReadMemory64(IR::ValuePtr vaddr);
void WriteMemory8(IR::ValuePtr vaddr, IR::ValuePtr value);
void WriteMemory16(IR::ValuePtr vaddr, IR::ValuePtr value);
void WriteMemory32(IR::ValuePtr vaddr, IR::ValuePtr value);
void WriteMemory64(IR::ValuePtr vaddr, IR::ValuePtr value);
void SetTerm(const IR::Terminal& terminal);
private:

View file

@ -365,6 +365,26 @@ struct TranslatorVisitor final {
}
}
bool thumb1_LDR_literal(Reg t, Imm8 imm8) {
u32 imm32 = imm8 << 2;
// LDR <Rt>, <label>
// Rt cannot encode R15.
u32 address = ir.AlignPC(4) + imm32;
auto data = ir.ReadMemory32(ir.Imm32(address));
ir.SetRegister(t, data);
return true;
}
bool thumb1_LDR_imm_t1(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5 << 2;
// LDR <Rt>, [<Rn>, #<imm>}
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
auto data = ir.ReadMemory32(address);
ir.SetRegister(t, data);
return true;
}
bool thumb1_UDF() {
return TranslateThisInstruction();
}

View file

@ -203,6 +203,11 @@ void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_e
printf("%s\n", Dynarmic::Arm::DisassembleThumb16(code_mem[i]).c_str());
}
printf("\nInitial Register Listing: \n");
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x\n", i, initial_regs[i]);
}
printf("\nFinal Register Listing: \n");
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x %08x %s\n", i, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : "");
@ -255,10 +260,14 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
return instructions[inst_index].Generate();
};
SECTION("single instructions") {
FuzzJitThumb(1, 2, 10000, instruction_select);
}
SECTION("short blocks") {
FuzzJitThumb(5, 6, 3000, instruction_select);
}
SECTION("long blocks") {
FuzzJitThumb(1024, 1025, 25, instruction_select);
}