From d0b48bfb59873683e07595022b3304ea31f45eb1 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Fri, 8 Jul 2016 17:09:18 +0800 Subject: [PATCH] Implement thumb1_ADD_reg_t1 and thumb1_ADD_reg_t2 --- src/backend_x64/emit_x64.cpp | 42 +++++++++++++++++++++++++++-- src/backend_x64/emit_x64.h | 2 ++ src/frontend/decoder/thumb1.h | 6 ++--- src/frontend/disassembler_thumb.cpp | 9 +++++++ src/frontend/ir/ir.h | 9 +++++++ src/frontend/ir/opcodes.inc | 2 ++ src/frontend/ir_emitter.cpp | 34 ++++++++++++++++++++++- src/frontend/ir_emitter.h | 12 +++++++++ src/frontend/translate_thumb.cpp | 34 +++++++++++++++++++++++ 9 files changed, 144 insertions(+), 6 deletions(-) diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index ca071cd6..20228bb9 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -33,6 +33,7 @@ static IR::Inst* FindUseWithOpcode(IR::Inst* inst, IR::Opcode opcode) { // Gets first found use. auto uses = inst->GetUses(); auto iter = std::find_if(uses.begin(), uses.end(), [opcode](const auto& use){ return use->GetOpcode() == opcode; }); + ASSERT(std::count_if(uses.begin(), uses.end(), [opcode](const auto& use){ return use->GetOpcode() == opcode; }) <= 1); return iter == uses.end() ? nullptr : reinterpret_cast(iter->get()); } @@ -71,8 +72,12 @@ CodePtr EmitX64::Emit(Arm::LocationDescriptor descriptor, Dynarmic::IR::Block bl return code_ptr; } -void EmitX64::EmitImmU1(IR::Value*) { - ASSERT_MSG(0, "Unimplemented"); +void EmitX64::EmitImmU1(IR::Value* value_) { + auto value = reinterpret_cast(value_); + + X64Reg result = reg_alloc.DefRegister(value); + + code->MOV(32, R(result), Imm32(value->value)); } void EmitX64::EmitImmU8(IR::Value* value_) { @@ -212,6 +217,10 @@ void EmitX64::EmitGetCarryFromOp(IR::Value*) { ASSERT_MSG(0, "should never happen"); } +void EmitX64::EmitGetOverflowFromOp(IR::Value*) { + ASSERT_MSG(0, "should never happen"); +} + void EmitX64::EmitLeastSignificantByte(IR::Value* value_) { auto value = reinterpret_cast(value_); @@ -398,6 +407,35 @@ void EmitX64::EmitArithmeticShiftRight(IR::Value* value_) { } } +void EmitX64::EmitAddWithCarry(IR::Value* value_) { + auto value = reinterpret_cast(value_); + auto carry_inst = FindUseWithOpcode(value, IR::Opcode::GetCarryFromOp); + auto overflow_inst = FindUseWithOpcode(value, IR::Opcode::GetOverflowFromOp); + + X64Reg addend = reg_alloc.UseRegister(value->GetArg(1).get()); + X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); + X64Reg carry = carry_inst + ? reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst) + : reg_alloc.UseRegister(value->GetArg(2).get()); + X64Reg overflow = overflow_inst + ? reg_alloc.DefRegister(overflow_inst) + : X64Reg::INVALID_REG; + + // TODO: Consider using LEA. + + code->BT(32, R(carry), Imm8(0)); // Sets x64 CF appropriately. + code->ADC(32, R(result), R(addend)); + + if (carry_inst) { + inhibit_emission.insert(carry_inst); + code->SETcc(Gen::CC_C, R(carry)); + } + if (overflow_inst) { + inhibit_emission.insert(overflow_inst); + code->SETcc(Gen::CC_O, R(overflow)); + } +} + 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 4e4a2840..ba802c29 100644 --- a/src/backend_x64/emit_x64.h +++ b/src/backend_x64/emit_x64.h @@ -45,12 +45,14 @@ public: void EmitGetVFlag(IR::Value* value); void EmitSetVFlag(IR::Value* value); void EmitGetCarryFromOp(IR::Value* value); + void EmitGetOverflowFromOp(IR::Value* value); void EmitLeastSignificantByte(IR::Value* value); void EmitMostSignificantBit(IR::Value* value); void EmitIsZero(IR::Value* value); void EmitLogicalShiftLeft(IR::Value* value); void EmitLogicalShiftRight(IR::Value* value); void EmitArithmeticShiftRight(IR::Value* value); + void EmitAddWithCarry(IR::Value* value); void EmitAddCycles(size_t cycles); diff --git a/src/frontend/decoder/thumb1.h b/src/frontend/decoder/thumb1.h index 8c09f7bd..cb6972e2 100644 --- a/src/frontend/decoder/thumb1.h +++ b/src/frontend/decoder/thumb1.h @@ -56,7 +56,7 @@ private: }; template -static const std::array, 7> g_thumb1_instruction_table {{ +static const std::array, 9> g_thumb1_instruction_table {{ #define INST(fn, name, bitstring) detail::detail::GetMatcher(name, bitstring) @@ -64,7 +64,7 @@ static const std::array, 7> g_thumb1_instruction_table {{ { INST(&V::thumb1_LSL_imm, "LSL (imm)", "00000vvvvvmmmddd") }, { INST(&V::thumb1_LSR_imm, "LSR (imm)", "00001vvvvvmmmddd") }, { INST(&V::thumb1_ASR_imm, "ASR (imm)", "00010vvvvvmmmddd") }, - //{ INST(&V::thumb1_ADD_rrr, "ADD (rrr)", "0001100mmmnnnddd") }, + { INST(&V::thumb1_ADD_reg_t1, "ADD (reg, T1)", "0001100mmmnnnddd") }, //{ INST(&V::thumb1_SUB_rrr, "SUB (rrr)", "0001101mmmnnnddd") }, //{ INST(&V::thumb1_ADD_rri, "ADD (rri)", "0001110mmmnnnddd") }, //{ INST(&V::thumb1_SUB_rri, "SUB (rri)", "0001111mmmnnnddd") }, @@ -92,7 +92,7 @@ static const std::array, 7> g_thumb1_instruction_table {{ //{ INST(&V::thumb1_MVNS_rr, "MVNS (rr)", "0100001111mmmddd") }, // Special data instructions - //{ INST(&V::thumb1_ADD_high, "ADD (high)", "01000100dmmmmddd") }, // v4T, Low regs: v6T2 + { INST(&V::thumb1_ADD_reg_t2, "ADD (reg, T2)", "01000100Dmmmmddd") }, // v4T, Low regs: v6T2 //{ INST(&V::thumb1_CMP_high, "CMP (high)", "01000101dmmmmddd") }, // v4T //{ INST(&V::thumb1_MOV_high, "MOV (high)", "01000110dmmmmddd") }, // v4T, Low regs: v6 diff --git a/src/frontend/disassembler_thumb.cpp b/src/frontend/disassembler_thumb.cpp index 529cb75e..ea411618 100644 --- a/src/frontend/disassembler_thumb.cpp +++ b/src/frontend/disassembler_thumb.cpp @@ -114,6 +114,10 @@ public: return Common::StringFromFormat("asrs %s, %s, #%u", RegStr(d), RegStr(m), imm5); } + std::string thumb1_ADD_reg_t1(Reg m, Reg n, Reg d) { + return Common::StringFromFormat("adds %s, %s, %s", RegStr(d), RegStr(n), RegStr(m)); + } + std::string thumb1_LSL_reg(Reg d_n, Reg m) { return Common::StringFromFormat("lsls %s, %s", RegStr(d_n), RegStr(m)); } @@ -126,6 +130,11 @@ public: return Common::StringFromFormat("asrs %s, %s", RegStr(d_n), RegStr(m)); } + std::string thumb1_ADD_reg_t2(bool d_n_hi, Reg m, Reg d_n_lo) { + Reg d_n = d_n_hi ? (d_n_lo + 8) : d_n_lo; + return Common::StringFromFormat("add %s, %s", RegStr(d_n), RegStr(m)); + } + std::string thumb1_UDF() { return Common::StringFromFormat("udf"); } diff --git a/src/frontend/ir/ir.h b/src/frontend/ir/ir.h index b1e7227e..fbee0b02 100644 --- a/src/frontend/ir/ir.h +++ b/src/frontend/ir/ir.h @@ -100,6 +100,15 @@ private: intptr_t tag; }; +/// Representation of a u1 immediate. +class ImmU1 final : public Value { +public: + explicit ImmU1(bool value_) : Value(Opcode::ImmU1), value(value_) {} + ~ImmU1() override = default; + + const bool value; ///< Literal value to load +}; + /// Representation of a u8 immediate. class ImmU8 final : public Value { public: diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index 2e9eadba..bf042336 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -20,6 +20,7 @@ OPCODE(SetVFlag, T::Void, T::U1 // Pseudo-operation, handled specially at final emit OPCODE(GetCarryFromOp, T::U1, T::U32 ) +OPCODE(GetOverflowFromOp, T::U1, T::U32 ) // Calculations OPCODE(LeastSignificantByte, T::U8, T::U32 ) @@ -28,3 +29,4 @@ OPCODE(IsZero, T::U1, T::U32 OPCODE(LogicalShiftLeft, T::U32, T::U32, T::U8, T::U1 ) OPCODE(LogicalShiftRight, T::U32, T::U32, T::U8, T::U1 ) OPCODE(ArithmeticShiftRight, T::U32, T::U32, T::U8, T::U1 ) +OPCODE(AddWithCarry, T::U32, T::U32, T::U32, T::U1 ) diff --git a/src/frontend/ir_emitter.cpp b/src/frontend/ir_emitter.cpp index b50001fe..277c0e8a 100644 --- a/src/frontend/ir_emitter.cpp +++ b/src/frontend/ir_emitter.cpp @@ -14,13 +14,29 @@ void IREmitter::Unimplemented() { } +IR::ValuePtr IREmitter::Imm1(bool value) { + auto imm1 = std::make_shared(value); + AddToBlock(imm1); + return imm1; +} + IR::ValuePtr IREmitter::Imm8(u8 i) { auto imm8 = std::make_shared(i); AddToBlock(imm8); return imm8; } -IR::ValuePtr IREmitter::GetRegister(Dynarmic::Arm::Reg reg) { +IR::ValuePtr IREmitter::Imm32(u32 i) { + auto imm32 = std::make_shared(i); + AddToBlock(imm32); + return imm32; +} + +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 Inst(IR::Opcode::GetRegister, { RegRef(reg) }); } @@ -28,6 +44,11 @@ void IREmitter::SetRegister(const Reg reg, IR::ValuePtr value) { Inst(IR::Opcode::SetRegister, { RegRef(reg), value }); } +void IREmitter::ALUWritePC(IR::ValuePtr value) { + // This behaviour is ARM version-dependent. + ASSERT_MSG(false, "Unimplemented"); +} + IR::ValuePtr IREmitter::GetCFlag() { return Inst(IR::Opcode::GetCFlag, {}); } @@ -44,6 +65,10 @@ void IREmitter::SetCFlag(IR::ValuePtr value) { Inst(IR::Opcode::SetCFlag, {value}); } +void IREmitter::SetVFlag(IR::ValuePtr value) { + Inst(IR::Opcode::SetVFlag, {value}); +} + IR::ValuePtr IREmitter::LeastSignificantByte(IR::ValuePtr value) { return Inst(IR::Opcode::LeastSignificantByte, {value}); } @@ -74,6 +99,13 @@ IREmitter::ResultAndCarry IREmitter::ArithmeticShiftRight(IR::ValuePtr value_in, return {result, carry_out}; } +IREmitter::ResultAndCarryAndOverflow IREmitter::AddWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in) { + auto result = Inst(IR::Opcode::AddWithCarry, {a, b, carry_in}); + auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result}); + auto overflow = Inst(IR::Opcode::GetOverflowFromOp, {result}); + return {result, carry_out, overflow}; +} + 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 c8c073d5..ea9081f7 100644 --- a/src/frontend/ir_emitter.h +++ b/src/frontend/ir_emitter.h @@ -25,17 +25,28 @@ public: IR::ValuePtr carry; }; + struct ResultAndCarryAndOverflow { + IR::ValuePtr result; + IR::ValuePtr carry; + IR::ValuePtr overflow; + }; + void Unimplemented(); + IR::ValuePtr Imm1(bool value); IR::ValuePtr Imm8(u8 value); + IR::ValuePtr Imm32(u32 value); IR::ValuePtr GetRegister(Reg source_reg); void SetRegister(const Reg dest_reg, IR::ValuePtr value); + void ALUWritePC(IR::ValuePtr value); + IR::ValuePtr GetCFlag(); void SetNFlag(IR::ValuePtr value); void SetZFlag(IR::ValuePtr value); void SetCFlag(IR::ValuePtr value); + void SetVFlag(IR::ValuePtr value); IR::ValuePtr LeastSignificantByte(IR::ValuePtr value); IR::ValuePtr MostSignificantBit(IR::ValuePtr value); @@ -44,6 +55,7 @@ public: ResultAndCarry LogicalShiftLeft(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in); ResultAndCarry LogicalShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in); ResultAndCarry ArithmeticShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in); + ResultAndCarryAndOverflow AddWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in); void SetTerm(const IR::Terminal& terminal); diff --git a/src/frontend/translate_thumb.cpp b/src/frontend/translate_thumb.cpp index d3122223..14b95b2e 100644 --- a/src/frontend/translate_thumb.cpp +++ b/src/frontend/translate_thumb.cpp @@ -26,6 +26,10 @@ struct TranslatorVisitor final { ir.SetTerm(IR::Term::Interpret(ir.current_location)); return false; } + bool UnpredictableInstruction() { + ASSERT_MSG(false, "UNPREDICTABLE"); + return false; + } bool thumb1_LSL_imm(Imm5 imm5, Reg m, Reg d) { u8 shift_n = imm5; @@ -60,6 +64,17 @@ struct TranslatorVisitor final { ir.SetCFlag(result.carry); return true; } + bool thumb1_ADD_reg_t1(Reg m, Reg n, Reg d) { + // ADDS , , + // Note that it is not possible to encode Rd == R15. + auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0)); + ir.SetRegister(d, result.result); + ir.SetNFlag(ir.MostSignificantBit(result.result)); + ir.SetZFlag(ir.IsZero(result.result)); + ir.SetCFlag(result.carry); + ir.SetVFlag(result.overflow); + return true; + } bool thumb1_LSL_reg(Reg m, Reg d_n) { const Reg d = d_n, n = d_n; @@ -98,6 +113,25 @@ struct TranslatorVisitor final { return true; } + bool thumb1_ADD_reg_t2(bool d_n_hi, Reg m, Reg d_n_lo) { + Reg d_n = d_n_hi ? (d_n_lo + 8) : d_n_lo; + Reg d = d_n, n = d_n; + if (n == Reg::PC && m == Reg::PC) { + return UnpredictableInstruction(); + } + // ADD , + auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0)); + if (d == Reg::PC) { + ir.ALUWritePC(result.result); + // Return to dispatch as we can't predict what PC is going to be. Stop compilation. + ir.SetTerm(IR::Term::ReturnToDispatch{}); + return false; + } else { + ir.SetRegister(d, result.result); + return true; + } + } + bool thumb1_UDF() { return TranslateThisInstruction(); }