A64: Implement system registers FPCR and FPSR

This commit is contained in:
MerryMage 2018-02-20 17:38:29 +00:00
parent 9e4e4e9c1d
commit e3da92024e
12 changed files with 216 additions and 4 deletions

View file

@ -90,6 +90,11 @@ public:
/// Modify FPCR. /// Modify FPCR.
void SetFpcr(std::uint32_t value); void SetFpcr(std::uint32_t value);
/// View FPSR.
std::uint32_t GetFpsr() const;
/// Modify FPSR.
void SetFpsr(std::uint32_t value);
/// View PSTATE /// View PSTATE
std::uint32_t GetPstate() const; std::uint32_t GetPstate() const;
/// Modify PSTATE /// Modify PSTATE

View file

@ -347,6 +347,23 @@ void A64EmitX64::EmitA64GetSP(A64EmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.DefineValue(inst, result); ctx.reg_alloc.DefineValue(inst, result);
} }
void A64EmitX64::EmitA64GetFPCR(A64EmitContext& ctx, IR::Inst* inst) {
Xbyak::Reg64 result = ctx.reg_alloc.ScratchGpr();
code.mov(result, qword[r15 + offsetof(A64JitState, fpcr)]);
ctx.reg_alloc.DefineValue(inst, result);
}
static u32 GetFPSRImpl(A64JitState* jit_state) {
return jit_state->GetFpsr();
}
void A64EmitX64::EmitA64GetFPSR(A64EmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.HostCall(inst);
code.mov(code.ABI_PARAM1, code.r15);
code.stmxcsr(code.dword[code.r15 + offsetof(A64JitState, guest_MXCSR)]);
code.CallFunction(GetFPSRImpl);
}
void A64EmitX64::EmitA64SetW(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64SetW(A64EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
A64::Reg reg = inst->GetArg(0).GetA64RegRef(); A64::Reg reg = inst->GetArg(0).GetA64RegRef();
@ -422,6 +439,30 @@ void A64EmitX64::EmitA64SetSP(A64EmitContext& ctx, IR::Inst* inst) {
} }
} }
static void SetFPCRImpl(A64JitState* jit_state, u32 value) {
jit_state->SetFpcr(value);
}
void A64EmitX64::EmitA64SetFPCR(A64EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ctx.reg_alloc.HostCall(nullptr, {}, args[0]);
code.mov(code.ABI_PARAM1, code.r15);
code.CallFunction(SetFPCRImpl);
code.ldmxcsr(code.dword[code.r15 + offsetof(A64JitState, guest_MXCSR)]);
}
static void SetFPSRImpl(A64JitState* jit_state, u32 value) {
jit_state->SetFpsr(value);
}
void A64EmitX64::EmitA64SetFPSR(A64EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ctx.reg_alloc.HostCall(nullptr, {}, args[0]);
code.mov(code.ABI_PARAM1, code.r15);
code.CallFunction(SetFPSRImpl);
code.ldmxcsr(code.dword[code.r15 + offsetof(A64JitState, guest_MXCSR)]);
}
void A64EmitX64::EmitA64SetPC(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64SetPC(A64EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto addr = qword[r15 + offsetof(A64JitState, pc)]; auto addr = qword[r15 + offsetof(A64JitState, pc)];

View file

@ -151,6 +151,14 @@ public:
jit_state.SetFpcr(value); jit_state.SetFpcr(value);
} }
u32 GetFpsr() const {
return jit_state.GetFpsr();
}
void SetFpsr(u32 value) {
jit_state.SetFpsr(value);
}
u32 GetPstate() const { u32 GetPstate() const {
return jit_state.GetPstate(); return jit_state.GetPstate();
} }
@ -314,6 +322,14 @@ void Jit::SetFpcr(u32 value) {
impl->SetFpcr(value); impl->SetFpcr(value);
} }
u32 Jit::GetFpsr() const {
return impl->GetFpsr();
}
void Jit::SetFpsr(u32 value) {
impl->SetFpsr(value);
}
u32 Jit::GetPstate() const { u32 Jit::GetPstate() const {
return impl->GetPstate(); return impl->GetPstate();
} }

View file

@ -5,6 +5,7 @@
*/ */
#include "backend_x64/a64_jitstate.h" #include "backend_x64/a64_jitstate.h"
#include "common/bit_util.h"
#include "frontend/A64/location_descriptor.h" #include "frontend/A64/location_descriptor.h"
namespace Dynarmic::BackendX64 { namespace Dynarmic::BackendX64 {
@ -15,4 +16,103 @@ u64 A64JitState::GetUniqueHash() const {
return pc_u64 | fpcr_u64; return pc_u64 | fpcr_u64;
} }
/**
* Comparing MXCSR and FPCR
* ========================
*
* SSE MSCSR exception masks
* -------------------------
* PM bit 12 Precision Mask
* UM bit 11 Underflow Mask
* OM bit 10 Overflow Mask
* ZM bit 9 Divide By Zero Mask
* DM bit 8 Denormal Mask
* IM bit 7 Invalid Operation Mask
*
* A64 FPCR exception trap enables
* -------------------------------
* IDE bit 15 Input Denormal exception trap enable
* IXE bit 12 Inexact exception trap enable
* UFE bit 11 Underflow exception trap enable
* OFE bit 10 Overflow exception trap enable
* DZE bit 9 Division by Zero exception trap enable
* IOE bit 8 Invalid Operation exception trap enable
*
* SSE MXCSR mode bits
* -------------------
* FZ bit 15 Flush To Zero
* DAZ bit 6 Denormals Are Zero
* RN bits 13-14 Round to {0 = Nearest, 1 = Negative, 2 = Positive, 3 = Zero}
*
* A64 FPCR mode bits
* ------------------
* AHP bit 26 Alternative half-precision
* DN bit 25 Default NaN
* FZ bit 24 Flush to Zero
* RMode bits 22-23 Round to {0 = Nearest, 1 = Positive, 2 = Negative, 3 = Zero}
* FZ16 bit 19 Flush to Zero for half-precision
*/
constexpr u32 FPCR_MASK = 0x07C89F00;
u32 A64JitState::GetFpcr() const {
return fpcr;
}
void A64JitState::SetFpcr(u32 value) {
fpcr = value & FPCR_MASK;
guest_MXCSR = 0x00001f80; // Mask all exceptions
// RMode
const std::array<u32, 4> MXCSR_RMode {0x0, 0x4000, 0x2000, 0x6000};
guest_MXCSR |= MXCSR_RMode[(value >> 22) & 0x3];
if (Common::Bit<24>(value)) {
guest_MXCSR |= (1 << 15); // SSE Flush to Zero
guest_MXCSR |= (1 << 6); // SSE Denormals are Zero
}
}
/**
* Comparing MXCSR and FPSR
* ========================
*
* SSE MXCSR exception flags
* -------------------------
* PE bit 5 Precision Flag
* UE bit 4 Underflow Flag
* OE bit 3 Overflow Flag
* ZE bit 2 Divide By Zero Flag
* DE bit 1 Denormal Flag // Appears to only be set when MXCSR.DAZ = 0
* IE bit 0 Invalid Operation Flag
*
* A64 FPSR cumulative exception bits
* ----------------------------------
* QC bit 27 Cumulative saturation bit
* IDC bit 7 Input Denormal cumulative exception bit // Only ever set when FPCR.FTZ = 1
* IXC bit 4 Inexact cumulative exception bit
* UFC bit 3 Underflow cumulative exception bit
* OFC bit 2 Overflow cumulative exception bit
* DZC bit 1 Division by Zero cumulative exception bit
* IOC bit 0 Invalid Operation cumulative exception bit
*/
u32 A64JitState::GetFpsr() const {
u32 fpsr = 0;
fpsr |= (guest_MXCSR & 0b0000000000001); // IOC = IE
fpsr |= (guest_MXCSR & 0b0000000111100) >> 1; // IXC, UFC, OFC, DZC = PE, UE, OE, ZE
fpsr |= FPSCR_IDC;
fpsr |= FPSCR_UFC;
return fpsr;
}
void A64JitState::SetFpsr(u32 value) {
guest_MXCSR |= ( value ) & 0b0000000000001; // IE = IOC
guest_MXCSR |= ( value << 1) & 0b0000000111100; // PE, UE, OE, ZE = IXC, UFC, OFC, DZC
FPSCR_IDC = value & (1 << 7);
FPSCR_UFC = value & (1 << 3);
}
} // namespace Dynarmic::BackendX64 } // namespace Dynarmic::BackendX64

View file

@ -74,8 +74,10 @@ struct A64JitState {
u32 FPSCR_IDC = 0; u32 FPSCR_IDC = 0;
u32 FPSCR_UFC = 0; u32 FPSCR_UFC = 0;
u32 fpcr = 0; u32 fpcr = 0;
u32 GetFpcr() const { return fpcr; } u32 GetFpcr() const;
void SetFpcr(u32 new_fpcr) { fpcr = new_fpcr; } u32 GetFpsr() const;
void SetFpcr(u32 new_fpcr);
void SetFpsr(u32 new_fpcr);
u64 GetUniqueHash() const; u64 GetUniqueHash() const;
}; };

View file

@ -66,7 +66,7 @@ INST(DSB, "DSB", "11010
INST(DMB, "DMB", "11010101000000110011MMMM10111111") INST(DMB, "DMB", "11010101000000110011MMMM10111111")
//INST(ISB, "ISB", "11010101000000110011MMMM11011111") //INST(ISB, "ISB", "11010101000000110011MMMM11011111")
//INST(SYS, "SYS", "1101010100001oooNNNNMMMMooottttt") //INST(SYS, "SYS", "1101010100001oooNNNNMMMMooottttt")
//INST(MSR_reg, "MSR (register)", "110101010001poooNNNNMMMMooottttt") INST(MSR_reg, "MSR (register)", "110101010001poooNNNNMMMMooottttt")
//INST(SYSL, "SYSL", "1101010100101oooNNNNMMMMooottttt") //INST(SYSL, "SYSL", "1101010100101oooNNNNMMMMooottttt")
INST(MRS, "MRS", "110101010011poooNNNNMMMMooottttt") INST(MRS, "MRS", "110101010011poooNNNNMMMMooottttt")

View file

@ -166,6 +166,14 @@ IR::U64 IREmitter::GetSP() {
return Inst<IR::U64>(Opcode::A64GetSP); return Inst<IR::U64>(Opcode::A64GetSP);
} }
IR::U32 IREmitter::GetFPCR() {
return Inst<IR::U32>(Opcode::A64GetFPCR);
}
IR::U32 IREmitter::GetFPSR() {
return Inst<IR::U32>(Opcode::A64GetFPSR);
}
void IREmitter::SetW(const Reg reg, const IR::U32& value) { void IREmitter::SetW(const Reg reg, const IR::U32& value) {
if (reg == Reg::ZR) if (reg == Reg::ZR)
return; return;
@ -194,6 +202,14 @@ void IREmitter::SetSP(const IR::U64& value) {
Inst(Opcode::A64SetSP, value); Inst(Opcode::A64SetSP, value);
} }
void IREmitter::SetFPCR(const IR::U32& value) {
Inst(Opcode::A64SetFPCR, value);
}
void IREmitter::SetFPSR(const IR::U32& value) {
Inst(Opcode::A64SetFPSR, value);
}
void IREmitter::SetPC(const IR::U64& value) { void IREmitter::SetPC(const IR::U64& value) {
Inst(Opcode::A64SetPC, value); Inst(Opcode::A64SetPC, value);
} }

View file

@ -73,12 +73,16 @@ public:
IR::U128 GetD(Vec source_vec); IR::U128 GetD(Vec source_vec);
IR::U128 GetQ(Vec source_vec); IR::U128 GetQ(Vec source_vec);
IR::U64 GetSP(); IR::U64 GetSP();
IR::U32 GetFPCR();
IR::U32 GetFPSR();
void SetW(Reg dest_reg, const IR::U32& value); void SetW(Reg dest_reg, const IR::U32& value);
void SetX(Reg dest_reg, const IR::U64& value); void SetX(Reg dest_reg, const IR::U64& value);
void SetS(Vec dest_vec, const IR::U128& value); void SetS(Vec dest_vec, const IR::U128& value);
void SetD(Vec dest_vec, const IR::U128& value); void SetD(Vec dest_vec, const IR::U128& value);
void SetQ(Vec dest_vec, const IR::U128& value); void SetQ(Vec dest_vec, const IR::U128& value);
void SetSP(const IR::U64& value); void SetSP(const IR::U64& value);
void SetFPCR(const IR::U32& value);
void SetFPSR(const IR::U32& value);
void SetPC(const IR::U64& value); void SetPC(const IR::U64& value);
}; };

View file

@ -138,7 +138,7 @@ struct TranslatorVisitor final {
bool DMB(Imm<4> CRm); bool DMB(Imm<4> CRm);
bool ISB(Imm<4> CRm); bool ISB(Imm<4> CRm);
bool SYS(Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt); bool SYS(Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt);
bool MSR_reg(bool o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt); bool MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt);
bool SYSL(Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt); bool SYSL(Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt);
bool MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt); bool MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt);

View file

@ -51,6 +51,20 @@ bool TranslatorVisitor::DMB(Imm<4> /*CRm*/) {
return true; return true;
} }
bool TranslatorVisitor::MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) {
const size_t sys_reg = concatenate(Imm<1>{1}, o0, op1, CRn, CRm, op2).ZeroExtend<size_t>();
switch (sys_reg) {
case 0b11'011'0100'0100'000: // FPCR
ir.SetFPCR(X(32, Rt));
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
case 0b11'011'0100'0100'001: // FPSR
ir.SetFPSR(X(32, Rt));
return true;
}
return InterpretThisInstruction();
}
bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) {
const size_t sys_reg = concatenate(Imm<1>{1}, o0, op1, CRn, CRm, op2).ZeroExtend<size_t>(); const size_t sys_reg = concatenate(Imm<1>{1}, o0, op1, CRn, CRm, op2).ZeroExtend<size_t>();
switch (sys_reg) { switch (sys_reg) {
@ -66,6 +80,12 @@ bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3
case 0b11'011'1110'0000'001: // CNTPCT_EL0 case 0b11'011'1110'0000'001: // CNTPCT_EL0
X(64, Rt, ir.GetCNTPCT()); X(64, Rt, ir.GetCNTPCT());
return true; return true;
case 0b11'011'0100'0100'000: // FPCR
X(32, Rt, ir.GetFPCR());
return true;
case 0b11'011'0100'0100'001: // FPSR
X(32, Rt, ir.GetFPSR());
return true;
} }
return InterpretThisInstruction(); return InterpretThisInstruction();
} }

View file

@ -194,6 +194,8 @@ bool Inst::ReadsFromFPSCR() const {
switch (op) { switch (op) {
case Opcode::A32GetFpscr: case Opcode::A32GetFpscr:
case Opcode::A32GetFpscrNZCV: case Opcode::A32GetFpscrNZCV:
case Opcode::A64GetFPCR:
case Opcode::A64GetFPSR:
case Opcode::FPAbs32: case Opcode::FPAbs32:
case Opcode::FPAbs64: case Opcode::FPAbs64:
case Opcode::FPAdd32: case Opcode::FPAdd32:
@ -221,6 +223,8 @@ bool Inst::WritesToFPSCR() const {
switch (op) { switch (op) {
case Opcode::A32SetFpscr: case Opcode::A32SetFpscr:
case Opcode::A32SetFpscrNZCV: case Opcode::A32SetFpscrNZCV:
case Opcode::A64SetFPCR:
case Opcode::A64SetFPSR:
case Opcode::FPAbs32: case Opcode::FPAbs32:
case Opcode::FPAbs64: case Opcode::FPAbs64:
case Opcode::FPAdd32: case Opcode::FPAdd32:

View file

@ -47,6 +47,8 @@ A64OPC(GetS, T::U128, T::A64Vec
A64OPC(GetD, T::U128, T::A64Vec ) A64OPC(GetD, T::U128, T::A64Vec )
A64OPC(GetQ, T::U128, T::A64Vec ) A64OPC(GetQ, T::U128, T::A64Vec )
A64OPC(GetSP, T::U64, ) A64OPC(GetSP, T::U64, )
A64OPC(GetFPCR, T::U32, )
A64OPC(GetFPSR, T::U32, )
A64OPC(SetW, T::Void, T::A64Reg, T::U32 ) A64OPC(SetW, T::Void, T::A64Reg, T::U32 )
A64OPC(SetX, T::Void, T::A64Reg, T::U64 ) A64OPC(SetX, T::Void, T::A64Reg, T::U64 )
//A64OPC(SetB, T::Void, T::A64Vec, T::U8 ) //A64OPC(SetB, T::Void, T::A64Vec, T::U8 )
@ -55,6 +57,8 @@ A64OPC(SetS, T::Void, T::A64Vec, T::U128
A64OPC(SetD, T::Void, T::A64Vec, T::U128 ) A64OPC(SetD, T::Void, T::A64Vec, T::U128 )
A64OPC(SetQ, T::Void, T::A64Vec, T::U128 ) A64OPC(SetQ, T::Void, T::A64Vec, T::U128 )
A64OPC(SetSP, T::Void, T::U64 ) A64OPC(SetSP, T::Void, T::U64 )
A64OPC(SetFPCR, T::Void, T::U32 )
A64OPC(SetFPSR, T::Void, T::U32 )
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 ) A64OPC(ExceptionRaised, T::Void, T::U64, T::U64 )