From 0992987c98850768083e6d8675fa9448a837f43f Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sat, 13 Jan 2018 17:54:29 +0000 Subject: [PATCH] A64: Add ExceptionRaised IR instruction The purpose of this instruction is to raise exceptions when certain decode-time issues happen, instead of asserting at translate time. This allows us to use the translator for code analysis without worrying about unnecessary asserts, but also provides flexibility for the library user to perform custom behaviour when one of these states are raised. --- include/dynarmic/A64/config.h | 13 +++++++++++++ src/backend_x64/a64_emit_x64.cpp | 12 ++++++++++++ src/frontend/A64/ir_emitter.cpp | 4 ++++ src/frontend/A64/ir_emitter.h | 3 +++ src/frontend/A64/translate/impl/impl.cpp | 9 ++++++--- src/frontend/ir/microinstruction.cpp | 3 ++- src/frontend/ir/opcodes.inc | 1 + 7 files changed, 41 insertions(+), 4 deletions(-) diff --git a/include/dynarmic/A64/config.h b/include/dynarmic/A64/config.h index fb7728d8..5a3bc1c5 100644 --- a/include/dynarmic/A64/config.h +++ b/include/dynarmic/A64/config.h @@ -16,6 +16,17 @@ namespace A64 { using VAddr = std::uint64_t; +enum class Exception { + /// An UndefinedFault occured due to executing instruction with an unallocated encoding + UnallocatedEncoding, + /// An UndefinedFault occured due to executing instruction containing a reserved value + ReservedValue, + /// An unpredictable instruction is to be executed. Implementation-defined behaviour should now happen. + /// This behaviour is up to the user of this library to define. + /// Note: Constraints on unpredictable behaviour are specified in the ARMv8 ARM. + UnpredictableInstruction, +}; + struct UserCallbacks { virtual ~UserCallbacks() = default; @@ -47,6 +58,8 @@ struct UserCallbacks { // This callback is called whenever a SVC instruction is executed. virtual void CallSVC(std::uint32_t swi) = 0; + virtual void ExceptionRaised(VAddr pc, Exception exception) = 0; + // Timing-related callbacks // ticks ticks have passed virtual void AddTicks(std::uint64_t ticks) = 0; diff --git a/src/backend_x64/a64_emit_x64.cpp b/src/backend_x64/a64_emit_x64.cpp index faa284b8..9397c67e 100644 --- a/src/backend_x64/a64_emit_x64.cpp +++ b/src/backend_x64/a64_emit_x64.cpp @@ -228,6 +228,18 @@ void A64EmitX64::EmitA64CallSupervisor(A64EmitContext& ctx, IR::Inst* inst) { }); } +void A64EmitX64::EmitA64ExceptionRaised(A64EmitContext& ctx, IR::Inst* inst) { + ctx.reg_alloc.HostCall(nullptr); + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + ASSERT(args[0].IsImmediate() && args[1].IsImmediate()); + u64 pc = args[0].GetImmediateU64(); + u64 exception = args[1].GetImmediateU64(); + DEVIRT(conf.callbacks, &A64::UserCallbacks::ExceptionRaised).EmitCall(code, [&](Xbyak::Reg64 param1, Xbyak::Reg64 param2) { + code->mov(param1, pc); + code->mov(param2, exception); + }); +} + void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) { DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead8).EmitCall(code, [&](Xbyak::Reg64 vaddr) { ASSERT(vaddr == code->ABI_PARAM2); diff --git a/src/frontend/A64/ir_emitter.cpp b/src/frontend/A64/ir_emitter.cpp index 3a07bf74..cfc15cdf 100644 --- a/src/frontend/A64/ir_emitter.cpp +++ b/src/frontend/A64/ir_emitter.cpp @@ -38,6 +38,10 @@ void IREmitter::CallSupervisor(u32 imm) { Inst(Opcode::A64CallSupervisor, Imm32(imm)); } +void IREmitter::ExceptionRaised(Exception exception) { + Inst(Opcode::A64ExceptionRaised, Imm64(PC()), Imm64(static_cast(exception))); +} + IR::U8 IREmitter::ReadMemory8(const IR::U64& vaddr) { return Inst(Opcode::A64ReadMemory8, vaddr); } diff --git a/src/frontend/A64/ir_emitter.h b/src/frontend/A64/ir_emitter.h index 53f2d488..ff0a3dfe 100644 --- a/src/frontend/A64/ir_emitter.h +++ b/src/frontend/A64/ir_emitter.h @@ -8,6 +8,8 @@ #include +#include + #include "common/common_types.h" #include "frontend/A64/location_descriptor.h" #include "frontend/A64/types.h" @@ -36,6 +38,7 @@ public: void SetNZCV(const IR::NZCV& nzcv); void CallSupervisor(u32 imm); + void ExceptionRaised(Exception exception); IR::U8 ReadMemory8(const IR::U64& vaddr); IR::U16 ReadMemory16(const IR::U64& vaddr); diff --git a/src/frontend/A64/translate/impl/impl.cpp b/src/frontend/A64/translate/impl/impl.cpp index 57f8fdb2..b66386a0 100644 --- a/src/frontend/A64/translate/impl/impl.cpp +++ b/src/frontend/A64/translate/impl/impl.cpp @@ -17,17 +17,20 @@ bool TranslatorVisitor::InterpretThisInstruction() { } bool TranslatorVisitor::UnpredictableInstruction() { - ASSERT_MSG(false, "UNPREDICTABLE"); + ir.ExceptionRaised(Exception::UnpredictableInstruction); + ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}}); return false; } bool TranslatorVisitor::ReservedValue() { - ASSERT_MSG(false, "RESERVEDVALUE"); + ir.ExceptionRaised(Exception::ReservedValue); + ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}}); return false; } bool TranslatorVisitor::UnallocatedEncoding() { - ASSERT_MSG(false, "UNALLOCATEDENCODING"); + ir.ExceptionRaised(Exception::UnallocatedEncoding); + ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}}); return false; } diff --git a/src/frontend/ir/microinstruction.cpp b/src/frontend/ir/microinstruction.cpp index f1eff458..bd68255b 100644 --- a/src/frontend/ir/microinstruction.cpp +++ b/src/frontend/ir/microinstruction.cpp @@ -229,7 +229,8 @@ bool Inst::WritesToFPSCR() const { bool Inst::CausesCPUException() const { return op == Opcode::Breakpoint || op == Opcode::A32CallSupervisor || - op == Opcode::A64CallSupervisor; + op == Opcode::A64CallSupervisor || + op == Opcode::A64ExceptionRaised; } bool Inst::AltersExclusiveState() const { diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index af5055cf..fbb9f46b 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -46,6 +46,7 @@ A64OPC(SetX, T::Void, T::A64Reg, T::U64 A64OPC(SetSP, T::Void, T::U64 ) A64OPC(SetPC, T::Void, T::U64 ) A64OPC(CallSupervisor, T::Void, T::U32 ) +A64OPC(ExceptionRaised, T::Void, T::U64, T::U64 ) // Hints OPCODE(PushRSB, T::Void, T::U64 )