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.
This commit is contained in:
parent
71a1851ee6
commit
0992987c98
7 changed files with 41 additions and 4 deletions
|
@ -16,6 +16,17 @@ namespace A64 {
|
||||||
|
|
||||||
using VAddr = std::uint64_t;
|
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 {
|
struct UserCallbacks {
|
||||||
virtual ~UserCallbacks() = default;
|
virtual ~UserCallbacks() = default;
|
||||||
|
|
||||||
|
@ -47,6 +58,8 @@ struct UserCallbacks {
|
||||||
// This callback is called whenever a SVC instruction is executed.
|
// This callback is called whenever a SVC instruction is executed.
|
||||||
virtual void CallSVC(std::uint32_t swi) = 0;
|
virtual void CallSVC(std::uint32_t swi) = 0;
|
||||||
|
|
||||||
|
virtual void ExceptionRaised(VAddr pc, Exception exception) = 0;
|
||||||
|
|
||||||
// Timing-related callbacks
|
// Timing-related callbacks
|
||||||
// ticks ticks have passed
|
// ticks ticks have passed
|
||||||
virtual void AddTicks(std::uint64_t ticks) = 0;
|
virtual void AddTicks(std::uint64_t ticks) = 0;
|
||||||
|
|
|
@ -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) {
|
void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead8).EmitCall(code, [&](Xbyak::Reg64 vaddr) {
|
DEVIRT(conf.callbacks, &A64::UserCallbacks::MemoryRead8).EmitCall(code, [&](Xbyak::Reg64 vaddr) {
|
||||||
ASSERT(vaddr == code->ABI_PARAM2);
|
ASSERT(vaddr == code->ABI_PARAM2);
|
||||||
|
|
|
@ -38,6 +38,10 @@ void IREmitter::CallSupervisor(u32 imm) {
|
||||||
Inst(Opcode::A64CallSupervisor, Imm32(imm));
|
Inst(Opcode::A64CallSupervisor, Imm32(imm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IREmitter::ExceptionRaised(Exception exception) {
|
||||||
|
Inst(Opcode::A64ExceptionRaised, Imm64(PC()), Imm64(static_cast<u64>(exception)));
|
||||||
|
}
|
||||||
|
|
||||||
IR::U8 IREmitter::ReadMemory8(const IR::U64& vaddr) {
|
IR::U8 IREmitter::ReadMemory8(const IR::U64& vaddr) {
|
||||||
return Inst<IR::U8>(Opcode::A64ReadMemory8, vaddr);
|
return Inst<IR::U8>(Opcode::A64ReadMemory8, vaddr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
|
||||||
|
#include <dynarmic/A64/config.h>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "frontend/A64/location_descriptor.h"
|
#include "frontend/A64/location_descriptor.h"
|
||||||
#include "frontend/A64/types.h"
|
#include "frontend/A64/types.h"
|
||||||
|
@ -36,6 +38,7 @@ public:
|
||||||
void SetNZCV(const IR::NZCV& nzcv);
|
void SetNZCV(const IR::NZCV& nzcv);
|
||||||
|
|
||||||
void CallSupervisor(u32 imm);
|
void CallSupervisor(u32 imm);
|
||||||
|
void ExceptionRaised(Exception exception);
|
||||||
|
|
||||||
IR::U8 ReadMemory8(const IR::U64& vaddr);
|
IR::U8 ReadMemory8(const IR::U64& vaddr);
|
||||||
IR::U16 ReadMemory16(const IR::U64& vaddr);
|
IR::U16 ReadMemory16(const IR::U64& vaddr);
|
||||||
|
|
|
@ -17,17 +17,20 @@ bool TranslatorVisitor::InterpretThisInstruction() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::UnpredictableInstruction() {
|
bool TranslatorVisitor::UnpredictableInstruction() {
|
||||||
ASSERT_MSG(false, "UNPREDICTABLE");
|
ir.ExceptionRaised(Exception::UnpredictableInstruction);
|
||||||
|
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::ReservedValue() {
|
bool TranslatorVisitor::ReservedValue() {
|
||||||
ASSERT_MSG(false, "RESERVEDVALUE");
|
ir.ExceptionRaised(Exception::ReservedValue);
|
||||||
|
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::UnallocatedEncoding() {
|
bool TranslatorVisitor::UnallocatedEncoding() {
|
||||||
ASSERT_MSG(false, "UNALLOCATEDENCODING");
|
ir.ExceptionRaised(Exception::UnallocatedEncoding);
|
||||||
|
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -229,7 +229,8 @@ bool Inst::WritesToFPSCR() const {
|
||||||
bool Inst::CausesCPUException() const {
|
bool Inst::CausesCPUException() const {
|
||||||
return op == Opcode::Breakpoint ||
|
return op == Opcode::Breakpoint ||
|
||||||
op == Opcode::A32CallSupervisor ||
|
op == Opcode::A32CallSupervisor ||
|
||||||
op == Opcode::A64CallSupervisor;
|
op == Opcode::A64CallSupervisor ||
|
||||||
|
op == Opcode::A64ExceptionRaised;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Inst::AltersExclusiveState() const {
|
bool Inst::AltersExclusiveState() const {
|
||||||
|
|
|
@ -46,6 +46,7 @@ A64OPC(SetX, T::Void, T::A64Reg, T::U64
|
||||||
A64OPC(SetSP, T::Void, T::U64 )
|
A64OPC(SetSP, T::Void, T::U64 )
|
||||||
A64OPC(SetPC, T::Void, T::U64 )
|
A64OPC(SetPC, T::Void, T::U64 )
|
||||||
A64OPC(CallSupervisor, T::Void, T::U32 )
|
A64OPC(CallSupervisor, T::Void, T::U32 )
|
||||||
|
A64OPC(ExceptionRaised, T::Void, T::U64, T::U64 )
|
||||||
|
|
||||||
// Hints
|
// Hints
|
||||||
OPCODE(PushRSB, T::Void, T::U64 )
|
OPCODE(PushRSB, T::Void, T::U64 )
|
||||||
|
|
Loading…
Reference in a new issue