diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 06659820..47e1e8ba 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -6,6 +6,8 @@ #include +#include + #include "backend_x64/abi.h" #include "backend_x64/block_of_code.h" #include "backend_x64/emit_x64.h" @@ -2955,6 +2957,202 @@ void EmitX64::EmitExclusiveWriteMemory64(IR::Block&, IR::Inst* inst) { code->L(end); } +static void EmitCoprocessorException() { + ASSERT_MSG(false, "Should raise coproc exception here"); +} + +static void CallCoprocCallback(BlockOfCode* code, RegAlloc& reg_alloc, Jit* jit_interface, Coprocessor::Callback callback, IR::Inst* inst = nullptr, IR::Value arg0 = {}, IR::Value arg1 = {}) { + reg_alloc.HostCall(inst, {}, {}, arg0, arg1); + + code->mov(code->ABI_PARAM1, reinterpret_cast(jit_interface)); + if (callback.user_arg) { + code->mov(code->ABI_PARAM2, reinterpret_cast(*callback.user_arg)); + } + + code->CallFunction(callback.function); +} + +void EmitX64::EmitCoprocInternalOperation(IR::Block&, IR::Inst* inst) { + auto coproc_info = inst->GetArg(0).GetCoprocInfo(); + + size_t coproc_num = coproc_info[0]; + bool two = coproc_info[1] != 0; + unsigned opc1 = static_cast(coproc_info[2]); + Arm::CoprocReg CRd = static_cast(coproc_info[3]); + Arm::CoprocReg CRn = static_cast(coproc_info[4]); + Arm::CoprocReg CRm = static_cast(coproc_info[5]); + unsigned opc2 = static_cast(coproc_info[6]); + + std::shared_ptr coproc = cb.coprocessors[coproc_num]; + if (!coproc) { + EmitCoprocessorException(); + return; + } + + auto action = coproc->CompileInternalOperation(two, opc1, CRd, CRn, CRm, opc2); + if (!action) { + EmitCoprocessorException(); + return; + } + + CallCoprocCallback(code, reg_alloc, jit_interface, *action); +} + +void EmitX64::EmitCoprocSendOneWord(IR::Block&, IR::Inst* inst) { + auto coproc_info = inst->GetArg(0).GetCoprocInfo(); + + size_t coproc_num = coproc_info[0]; + bool two = coproc_info[1] != 0; + unsigned opc1 = static_cast(coproc_info[2]); + Arm::CoprocReg CRn = static_cast(coproc_info[3]); + Arm::CoprocReg CRm = static_cast(coproc_info[4]); + unsigned opc2 = static_cast(coproc_info[5]); + + IR::Value word = inst->GetArg(1); + + std::shared_ptr coproc = cb.coprocessors[coproc_num]; + if (!coproc) { + EmitCoprocessorException(); + return; + } + + auto action = coproc->CompileSendOneWord(two, opc1, CRn, CRm, opc2); + if (!action) { + EmitCoprocessorException(); + return; + } + + CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), nullptr, word); +} + +void EmitX64::EmitCoprocSendTwoWords(IR::Block&, IR::Inst* inst) { + auto coproc_info = inst->GetArg(0).GetCoprocInfo(); + + size_t coproc_num = coproc_info[0]; + bool two = coproc_info[1] != 0; + unsigned opc = static_cast(coproc_info[2]); + Arm::CoprocReg CRm = static_cast(coproc_info[3]); + + IR::Value word1 = inst->GetArg(1); + IR::Value word2 = inst->GetArg(2); + + std::shared_ptr coproc = cb.coprocessors[coproc_num]; + if (!coproc) { + EmitCoprocessorException(); + return; + } + + auto action = coproc->CompileSendTwoWords(two, opc, CRm); + if (!action) { + EmitCoprocessorException(); + return; + } + + CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), nullptr, word1, word2); + } + +void EmitX64::EmitCoprocGetOneWord(IR::Block&, IR::Inst* inst) { + auto coproc_info = inst->GetArg(0).GetCoprocInfo(); + + size_t coproc_num = coproc_info[0]; + bool two = coproc_info[1] != 0; + unsigned opc1 = static_cast(coproc_info[2]); + Arm::CoprocReg CRn = static_cast(coproc_info[3]); + Arm::CoprocReg CRm = static_cast(coproc_info[4]); + unsigned opc2 = static_cast(coproc_info[5]); + + std::shared_ptr coproc = cb.coprocessors[coproc_num]; + if (!coproc) { + EmitCoprocessorException(); + return; + } + + auto action = coproc->CompileGetOneWord(two, opc1, CRn, CRm, opc2); + if (!action) { + EmitCoprocessorException(); + return; + } + + CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), inst); +} + +void EmitX64::EmitCoprocGetTwoWords(IR::Block&, IR::Inst* inst) { + auto coproc_info = inst->GetArg(0).GetCoprocInfo(); + + size_t coproc_num = coproc_info[0]; + bool two = coproc_info[1] != 0; + unsigned opc = coproc_info[2]; + Arm::CoprocReg CRm = static_cast(coproc_info[3]); + + std::shared_ptr coproc = cb.coprocessors[coproc_num]; + if (!coproc) { + EmitCoprocessorException(); + return; + } + + auto action = coproc->CompileGetTwoWords(two, opc, CRm); + if (!action) { + EmitCoprocessorException(); + return; + } + + CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), inst); +} + +void EmitX64::EmitCoprocLoadWords(IR::Block&, IR::Inst* inst) { + auto coproc_info = inst->GetArg(0).GetCoprocInfo(); + + size_t coproc_num = coproc_info[0]; + bool two = coproc_info[1] != 0; + bool long_transfer = coproc_info[2] != 0; + Arm::CoprocReg CRd = static_cast(coproc_info[3]); + bool has_option = coproc_info[4] != 0; + boost::optional option{has_option, coproc_info[5]}; + + IR::Value address = inst->GetArg(1); + + std::shared_ptr coproc = cb.coprocessors[coproc_num]; + if (!coproc) { + EmitCoprocessorException(); + return; + } + + auto action = coproc->CompileLoadWords(two, long_transfer, CRd, option); + if (!action) { + EmitCoprocessorException(); + return; + } + + CallCoprocCallback(code, reg_alloc, jit_interface, *action, nullptr, address); +} + +void EmitX64::EmitCoprocStoreWords(IR::Block&, IR::Inst* inst) { + auto coproc_info = inst->GetArg(0).GetCoprocInfo(); + + size_t coproc_num = coproc_info[0]; + bool two = coproc_info[1] != 0; + bool long_transfer = coproc_info[2] != 0; + Arm::CoprocReg CRd = static_cast(coproc_info[3]); + bool has_option = coproc_info[4] != 0; + boost::optional option{has_option, coproc_info[5]}; + + IR::Value address = inst->GetArg(1); + + std::shared_ptr coproc = cb.coprocessors[coproc_num]; + if (!coproc) { + EmitCoprocessorException(); + return; + } + + auto action = coproc->CompileStoreWords(two, long_transfer, CRd, option); + if (!action) { + EmitCoprocessorException(); + return; + } + + CallCoprocCallback(code, reg_alloc, jit_interface, *action, nullptr, address); +} + void EmitX64::EmitAddCycles(size_t cycles) { using namespace Xbyak::util; ASSERT(cycles < std::numeric_limits::max()); diff --git a/src/frontend/ir/ir_emitter.cpp b/src/frontend/ir/ir_emitter.cpp index 9d7795cb..2bd7d864 100644 --- a/src/frontend/ir/ir_emitter.cpp +++ b/src/frontend/ir/ir_emitter.cpp @@ -709,6 +709,80 @@ Value IREmitter::ExclusiveWriteMemory64(const Value& vaddr, const Value& value_l } } +void IREmitter::CoprocInternalOperation(size_t coproc_no, bool two, size_t opc1, Arm::CoprocReg CRd, Arm::CoprocReg CRn, Arm::CoprocReg CRm, size_t opc2) { + ASSERT(coproc_no <= 15); + std::array coproc_info{static_cast(coproc_no), + static_cast(two ? 1 : 0), + static_cast(opc1), + static_cast(CRd), + static_cast(CRn), + static_cast(CRm), + static_cast(opc2)}; + Inst(Opcode::CoprocInternalOperation, {Value(coproc_info)}); +} + +void IREmitter::CoprocSendOneWord(size_t coproc_no, bool two, size_t opc1, Arm::CoprocReg CRn, Arm::CoprocReg CRm, size_t opc2, const Value& word) { + ASSERT(coproc_no <= 15); + std::array coproc_info{static_cast(coproc_no), + static_cast(two ? 1 : 0), + static_cast(opc1), + static_cast(CRn), + static_cast(CRm), + static_cast(opc2)}; + Inst(Opcode::CoprocSendOneWord, {Value(coproc_info), word}); +} + +void IREmitter::CoprocSendTwoWords(size_t coproc_no, bool two, size_t opc, Arm::CoprocReg CRm, const Value& word1, const Value& word2) { + ASSERT(coproc_no <= 15); + std::array coproc_info{static_cast(coproc_no), + static_cast(two ? 1 : 0), + static_cast(opc), + static_cast(CRm)}; + Inst(Opcode::CoprocSendTwoWords, {Value(coproc_info), word1, word2}); +} + +Value IREmitter::CoprocGetOneWord(size_t coproc_no, bool two, size_t opc1, Arm::CoprocReg CRn, Arm::CoprocReg CRm, size_t opc2) { + ASSERT(coproc_no <= 15); + std::array coproc_info{static_cast(coproc_no), + static_cast(two ? 1 : 0), + static_cast(opc1), + static_cast(CRn), + static_cast(CRm), + static_cast(opc2)}; + return Inst(Opcode::CoprocGetOneWord, {Value(coproc_info)}); +} + +Value IREmitter::CoprocGetTwoWords(size_t coproc_no, bool two, size_t opc, Arm::CoprocReg CRm) { + ASSERT(coproc_no <= 15); + std::array coproc_info{static_cast(coproc_no), + static_cast(two ? 1 : 0), + static_cast(opc), + static_cast(CRm)}; + return Inst(Opcode::CoprocGetTwoWords, {Value(coproc_info)}); +} + +void IREmitter::CoprocLoadWords(size_t coproc_no, bool two, bool long_transfer, Arm::CoprocReg CRd, const Value& address, bool has_option, u8 option) { + ASSERT(coproc_no <= 15); + std::array coproc_info{static_cast(coproc_no), + static_cast(two ? 1 : 0), + static_cast(long_transfer ? 1 : 0), + static_cast(CRd), + static_cast(has_option ? 1 : 0), + static_cast(option)}; + Inst(Opcode::CoprocLoadWords, {Value(coproc_info), address}); +} + +void IREmitter::CoprocStoreWords(size_t coproc_no, bool two, bool long_transfer, Arm::CoprocReg CRd, const Value& address, bool has_option, u8 option) { + ASSERT(coproc_no <= 15); + std::array coproc_info{static_cast(coproc_no), + static_cast(two ? 1 : 0), + static_cast(long_transfer ? 1 : 0), + static_cast(CRd), + static_cast(has_option ? 1 : 0), + static_cast(option)}; + Inst(Opcode::CoprocStoreWords, {Value(coproc_info), address}); +} + void IREmitter::Breakpoint() { Inst(Opcode::Breakpoint, {}); } diff --git a/src/frontend/ir/ir_emitter.h b/src/frontend/ir/ir_emitter.h index 250137f6..e2f701f2 100644 --- a/src/frontend/ir/ir_emitter.h +++ b/src/frontend/ir/ir_emitter.h @@ -8,6 +8,8 @@ #include +#include + #include "common/common_types.h" #include "frontend/ir/basic_block.h" #include "frontend/ir/location_descriptor.h" @@ -213,6 +215,14 @@ public: Value ExclusiveWriteMemory32(const Value& vaddr, const Value& value); Value ExclusiveWriteMemory64(const Value& vaddr, const Value& value_lo, const Value& value_hi); + void CoprocInternalOperation(size_t coproc_no, bool two, size_t opc1, Arm::CoprocReg CRd, Arm::CoprocReg CRn, Arm::CoprocReg CRm, size_t opc2); + void CoprocSendOneWord(size_t coproc_no, bool two, size_t opc1, Arm::CoprocReg CRn, Arm::CoprocReg CRm, size_t opc2, const Value& word); + void CoprocSendTwoWords(size_t coproc_no, bool two, size_t opc, Arm::CoprocReg CRm, const Value& word1, const Value& word2); + Value CoprocGetOneWord(size_t coproc_no, bool two, size_t opc1, Arm::CoprocReg CRn, Arm::CoprocReg CRm, size_t opc2); + Value CoprocGetTwoWords(size_t coproc_no, bool two, size_t opc, Arm::CoprocReg CRm); + void CoprocLoadWords(size_t coproc_no, bool two, bool long_transfer, Arm::CoprocReg CRd, const Value& address, bool has_option, u8 option); + void CoprocStoreWords(size_t coproc_no, bool two, bool long_transfer, Arm::CoprocReg CRd, const Value& address, bool has_option, u8 option); + void Breakpoint(); void SetTerm(const Terminal& terminal); diff --git a/src/frontend/ir/microinstruction.cpp b/src/frontend/ir/microinstruction.cpp index 25cc83e1..2d2ef31a 100644 --- a/src/frontend/ir/microinstruction.cpp +++ b/src/frontend/ir/microinstruction.cpp @@ -213,6 +213,22 @@ bool Inst::AltersExclusiveState() const { IsExclusiveMemoryWrite(); } +bool Inst::IsCoprocessorInstruction() const { + switch (op) { + case Opcode::CoprocInternalOperation: + case Opcode::CoprocSendOneWord: + case Opcode::CoprocSendTwoWords: + case Opcode::CoprocGetOneWord: + case Opcode::CoprocGetTwoWords: + case Opcode::CoprocLoadWords: + case Opcode::CoprocStoreWords: + return true; + + default: + return false; + } +} + bool Inst::MayHaveSideEffects() const { return op == Opcode::PushRSB || CausesCPUException() || @@ -220,7 +236,8 @@ bool Inst::MayHaveSideEffects() const { WritesToCPSR() || WritesToFPSCR() || AltersExclusiveState() || - IsMemoryWrite(); + IsMemoryWrite() || + IsCoprocessorInstruction(); } void Inst::DecrementRemainingUses() { diff --git a/src/frontend/ir/microinstruction.h b/src/frontend/ir/microinstruction.h index 1f569a6a..12bcf9f2 100644 --- a/src/frontend/ir/microinstruction.h +++ b/src/frontend/ir/microinstruction.h @@ -67,6 +67,9 @@ public: /// Determines whether or not this instruction alters memory-exclusivity. bool AltersExclusiveState() const; + /// Determines whether or not this instruction accesses a coprocessor. + bool IsCoprocessorInstruction() const; + /// Determines whether or not this instruction causes a CPU exception. bool CausesCPUException() const; diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index 907e07d0..1d4c39fe 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -158,3 +158,12 @@ OPCODE(ExclusiveWriteMemory8, T::U32, T::U32, T::U8 OPCODE(ExclusiveWriteMemory16, T::U32, T::U32, T::U16 ) OPCODE(ExclusiveWriteMemory32, T::U32, T::U32, T::U32 ) OPCODE(ExclusiveWriteMemory64, T::U32, T::U32, T::U32, T::U32 ) + +// Coprocessor +OPCODE(CoprocInternalOperation, T::Void, T::CoprocInfo ) +OPCODE(CoprocSendOneWord, T::Void, T::CoprocInfo, T::U32 ) +OPCODE(CoprocSendTwoWords, T::Void, T::CoprocInfo, T::U32, T::U32 ) +OPCODE(CoprocGetOneWord, T::U32, T::CoprocInfo ) +OPCODE(CoprocGetTwoWords, T::U64, T::CoprocInfo ) +OPCODE(CoprocLoadWords, T::Void, T::CoprocInfo, T::U32 ) +OPCODE(CoprocStoreWords, T::Void, T::CoprocInfo, T::U32 )