diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 5c3a1fcd..81e9b6c2 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -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(value_); + + reg_alloc.HostCall(value, value->GetArg(0).get()); + + code->CALL(reinterpret_cast(cb.MemoryRead8)); +} + +void EmitX64::EmitReadMemory16(IR::Value* value_) { + auto value = reinterpret_cast(value_); + + reg_alloc.HostCall(value, value->GetArg(0).get()); + + code->CALL(reinterpret_cast(cb.MemoryRead16)); +} + +void EmitX64::EmitReadMemory32(IR::Value* value_) { + auto value = reinterpret_cast(value_); + + reg_alloc.HostCall(value, value->GetArg(0).get()); + + code->CALL(reinterpret_cast(cb.MemoryRead32)); +} + +void EmitX64::EmitReadMemory64(IR::Value* value_) { + auto value = reinterpret_cast(value_); + + reg_alloc.HostCall(value, value->GetArg(0).get()); + + code->CALL(reinterpret_cast(cb.MemoryRead64)); +} + +void EmitX64::EmitWriteMemory8(IR::Value* value_) { + auto value = reinterpret_cast(value_); + + reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get()); + + code->CALL(reinterpret_cast(cb.MemoryWrite8)); +} + +void EmitX64::EmitWriteMemory16(IR::Value* value_) { + auto value = reinterpret_cast(value_); + + reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get()); + + code->CALL(reinterpret_cast(cb.MemoryWrite16)); +} + +void EmitX64::EmitWriteMemory32(IR::Value* value_) { + auto value = reinterpret_cast(value_); + + reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get()); + + code->CALL(reinterpret_cast(cb.MemoryWrite32)); +} + +void EmitX64::EmitWriteMemory64(IR::Value* value_) { + auto value = reinterpret_cast(value_); + + reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get()); + + code->CALL(reinterpret_cast(cb.MemoryWrite64)); +} + + void EmitX64::EmitAddCycles(size_t cycles) { ASSERT(cycles < std::numeric_limits::max()); code->SUB(64, MDisp(R15, offsetof(JitState, cycles_remaining)), Imm32(static_cast(cycles))); diff --git a/src/backend_x64/emit_x64.h b/src/backend_x64/emit_x64.h index 90432223..589e59ff 100644 --- a/src/backend_x64/emit_x64.h +++ b/src/backend_x64/emit_x64.h @@ -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); diff --git a/src/backend_x64/reg_alloc.cpp b/src/backend_x64/reg_alloc.cpp index 5b72db36..4e412f67 100644 --- a/src/backend_x64/reg_alloc.cpp +++ b/src/backend_x64/reg_alloc.cpp @@ -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(); diff --git a/src/backend_x64/reg_alloc.h b/src/backend_x64/reg_alloc.h index bf4d6ee6..23317b2d 100644 --- a/src/backend_x64/reg_alloc.h +++ b/src/backend_x64/reg_alloc.h @@ -79,8 +79,12 @@ public: // TODO: Values in host flags + void DecrementRemainingUses(IR::Value* value); + void EndOfAllocScope(); + void AssertNoMoreUses(); + void Reset(); private: diff --git a/src/frontend/decoder/thumb1.h b/src/frontend/decoder/thumb1.h index da8ceb78..bd2e56b4 100644 --- a/src/frontend/decoder/thumb1.h +++ b/src/frontend/decoder/thumb1.h @@ -56,7 +56,7 @@ private: }; template -static const std::array, 30> g_thumb1_instruction_table {{ +static const std::array, 32> g_thumb1_instruction_table {{ #define INST(fn, name, bitstring) detail::detail::GetMatcher(name, bitstring) @@ -97,7 +97,7 @@ static const std::array, 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, 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") }, diff --git a/src/frontend/disassembler_thumb.cpp b/src/frontend/disassembler_thumb.cpp index 1d705867..43fa5dc0 100644 --- a/src/frontend/disassembler_thumb.cpp +++ b/src/frontend/disassembler_thumb.cpp @@ -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"); } diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index 94479f2a..f4b65d7f 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -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 ) diff --git a/src/frontend/ir_emitter.cpp b/src/frontend/ir_emitter.cpp index e907b965..d5b6cd65 100644 --- a/src/frontend/ir_emitter.cpp +++ b/src/frontend/ir_emitter.cpp @@ -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(pc - pc % alignment); +} + IR::ValuePtr IREmitter::Imm1(bool value) { auto imm1 = std::make_shared(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; diff --git a/src/frontend/ir_emitter.h b/src/frontend/ir_emitter.h index 80cc8b0d..19523920 100644 --- a/src/frontend/ir_emitter.h +++ b/src/frontend/ir_emitter.h @@ -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: diff --git a/src/frontend/translate_thumb.cpp b/src/frontend/translate_thumb.cpp index 2dd513e2..2c8116c6 100644 --- a/src/frontend/translate_thumb.cpp +++ b/src/frontend/translate_thumb.cpp @@ -365,6 +365,26 @@ struct TranslatorVisitor final { } } + bool thumb1_LDR_literal(Reg t, Imm8 imm8) { + u32 imm32 = imm8 << 2; + // LDR ,