diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 436c2240..cf1320aa 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -327,6 +327,50 @@ void EmitX64::EmitCallSupervisor(IR::Block&, IR::Inst* inst) { code->SwitchMxcsrOnEntry(); } +static u32 GetFpscrImpl(JitState* jit_state) { + return jit_state->Fpscr(); +} + +void EmitX64::EmitGetFpscr(IR::Block&, IR::Inst* inst) { + reg_alloc.HostCall(inst); + code->mov(code->ABI_PARAM1, code->r15); + + code->SwitchMxcsrOnExit(); + code->CallFunction(reinterpret_cast(&GetFpscrImpl)); + code->SwitchMxcsrOnEntry(); +} + +static void SetFpscrImpl(u32 value, JitState* jit_state) { + jit_state->SetFpscr(value); +} + +void EmitX64::EmitSetFpscr(IR::Block&, IR::Inst* inst) { + auto a = inst->GetArg(0); + + reg_alloc.HostCall(nullptr, a); + code->mov(code->ABI_PARAM2, code->r15); + + code->SwitchMxcsrOnExit(); + code->CallFunction(reinterpret_cast(&SetFpscrImpl)); + code->SwitchMxcsrOnEntry(); +} + +void EmitX64::EmitGetFpscrNZCV(IR::Block&, IR::Inst* inst) { + using namespace Xbyak::util; + + Xbyak::Reg32 result = reg_alloc.DefGpr(inst).cvt32(); + + code->mov(result, dword[r15 + offsetof(JitState, guest_FPSCR_nzcv)]); +} + +void EmitX64::EmitSetFpscrNZCV(IR::Block&, IR::Inst* inst) { + using namespace Xbyak::util; + + Xbyak::Reg32 value = reg_alloc.UseGpr(inst->GetArg(0)).cvt32(); + + code->mov(dword[r15 + offsetof(JitState, guest_FPSCR_nzcv)], value); +} + void EmitX64::EmitPushRSB(IR::Block&, IR::Inst* inst) { using namespace Xbyak::util; diff --git a/src/frontend/decoder/vfp2.h b/src/frontend/decoder/vfp2.h index 74eedb61..de48d6bb 100644 --- a/src/frontend/decoder/vfp2.h +++ b/src/frontend/decoder/vfp2.h @@ -96,6 +96,10 @@ boost::optional&> DecodeVFP2(u32 instruction) { // VCMP // VCMPE + // Floating-point system register access + INST(&V::vfp2_VMSR, "VMSR", "cccc111011100001tttt101000010000"), + INST(&V::vfp2_VMRS, "VMRS", "cccc111011110001tttt101000010000"), + // Extension register load-store instructions INST(&V::vfp2_VPUSH, "VPUSH", "cccc11010D101101dddd101zvvvvvvvv"), INST(&V::vfp2_VPOP, "VPOP", "cccc11001D111101dddd101zvvvvvvvv"), diff --git a/src/frontend/disassembler/disassembler_arm.cpp b/src/frontend/disassembler/disassembler_arm.cpp index e19da7f0..f2ebbdac 100644 --- a/src/frontend/disassembler/disassembler_arm.cpp +++ b/src/frontend/disassembler/disassembler_arm.cpp @@ -889,6 +889,17 @@ public: return fmt::format("vcvt{}{}.s32.{} {}, {}", round_towards_zero ? "" : "r", CondToString(cond), sz ? "f64" : "f32", FPRegStr(false, Vd, D), FPRegStr(sz, Vm, M)); } + std::string vfp2_VMSR(Cond cond, Reg t) { + return fmt::format("vmsr{} fpscr, {}", CondToString(cond), RegToString(t)); + } + std::string vfp2_VMRS(Cond cond, Reg t) { + if (t == Reg::R15) { + return fmt::format("vmrs{} apsr_nzcv, fpscr", CondToString(cond)); + } else { + return fmt::format("vmrs{} {}, fpscr", CondToString(cond), RegToString(t)); + } + } + std::string vfp2_VPOP(Cond cond, bool D, size_t Vd, bool sz, Imm8 imm8) { return fmt::format("vpop{} {}(+{})", CondToString(cond), FPRegStr(sz, Vd, D), imm8 >> (sz ? 1 : 0)); } diff --git a/src/frontend/ir/ir_emitter.cpp b/src/frontend/ir/ir_emitter.cpp index f1cb1e03..5f7ef5ab 100644 --- a/src/frontend/ir/ir_emitter.cpp +++ b/src/frontend/ir/ir_emitter.cpp @@ -134,8 +134,23 @@ void IREmitter::OrQFlag(const Value& value) { Inst(Opcode::OrQFlag, {value}); } -Value IREmitter::Pack2x32To1x64(const Value& lo, const Value& hi) -{ +Value IREmitter::GetFpscr() { + return Inst(Opcode::GetFpscr, {}); +} + +void IREmitter::SetFpscr(const Value& new_fpscr) { + Inst(Opcode::SetFpscr, {new_fpscr}); +} + +Value IREmitter::GetFpscrNZCV() { + return Inst(Opcode::GetFpscrNZCV, {}); +} + +void IREmitter::SetFpscrNZCV(const Value& new_fpscr_nzcv) { + Inst(Opcode::SetFpscrNZCV, {new_fpscr_nzcv}); +} + +Value IREmitter::Pack2x32To1x64(const Value& lo, const Value& hi) { return Inst(Opcode::Pack2x32To1x64, {lo, hi}); } diff --git a/src/frontend/ir/ir_emitter.h b/src/frontend/ir/ir_emitter.h index f0fc3643..1d0c5220 100644 --- a/src/frontend/ir/ir_emitter.h +++ b/src/frontend/ir/ir_emitter.h @@ -77,6 +77,11 @@ public: void SetVFlag(const Value& value); void OrQFlag(const Value& value); + Value GetFpscr(); + void SetFpscr(const Value& new_fpscr); + Value GetFpscrNZCV(); + void SetFpscrNZCV(const Value& new_fpscr_nzcv); + Value Pack2x32To1x64(const Value& lo, const Value& hi); Value LeastSignificantWord(const Value& value); ResultAndCarry MostSignificantWord(const Value& value); diff --git a/src/frontend/ir/microinstruction.cpp b/src/frontend/ir/microinstruction.cpp index 557c0590..bfcb2fd0 100644 --- a/src/frontend/ir/microinstruction.cpp +++ b/src/frontend/ir/microinstruction.cpp @@ -148,6 +148,8 @@ bool Inst::WritesToCoreRegister() const { bool Inst::ReadsFromFPSCR() const { switch (op) { + case Opcode::GetFpscr: + case Opcode::GetFpscrNZCV: case Opcode::FPAbs32: case Opcode::FPAbs64: case Opcode::FPAdd32: @@ -171,6 +173,8 @@ bool Inst::ReadsFromFPSCR() const { bool Inst::WritesToFPSCR() const { switch (op) { + case Opcode::SetFpscr: + case Opcode::SetFpscrNZCV: case Opcode::FPAbs32: case Opcode::FPAbs64: case Opcode::FPAdd32: diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index 192febeb..04f2b52e 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -23,6 +23,10 @@ OPCODE(SetVFlag, T::Void, T::U1 OPCODE(OrQFlag, T::Void, T::U1 ) OPCODE(BXWritePC, T::Void, T::U32 ) OPCODE(CallSupervisor, T::Void, T::U32 ) +OPCODE(GetFpscr, T::U32, ) +OPCODE(SetFpscr, T::Void, T::U32, ) +OPCODE(GetFpscrNZCV, T::U32, ) +OPCODE(SetFpscrNZCV, T::Void, T::U32, ) // Hints OPCODE(PushRSB, T::Void, T::U64 ) diff --git a/src/frontend/translate/translate_arm/translate_arm.h b/src/frontend/translate/translate_arm/translate_arm.h index 88b57d93..5ab87997 100644 --- a/src/frontend/translate/translate_arm/translate_arm.h +++ b/src/frontend/translate/translate_arm/translate_arm.h @@ -364,6 +364,10 @@ struct ArmTranslatorVisitor final { bool vfp2_VCVT_to_u32(Cond cond, bool D, size_t Vd, bool sz, bool round_towards_zero, bool M, size_t Vm); bool vfp2_VCVT_to_s32(Cond cond, bool D, size_t Vd, bool sz, bool round_towards_zero, bool M, size_t Vm); + // Floating-point system register access + bool vfp2_VMSR(Cond cond, Reg t); + bool vfp2_VMRS(Cond cond, Reg t); + // Floating-point load-store instructions bool vfp2_VLDR(Cond cond, bool U, bool D, Reg n, size_t Vd, bool sz, Imm8 imm8); bool vfp2_VSTR(Cond cond, bool U, bool D, Reg n, size_t Vd, bool sz, Imm8 imm8); diff --git a/src/frontend/translate/translate_arm/vfp2.cpp b/src/frontend/translate/translate_arm/vfp2.cpp index b7cee0d6..07439d56 100644 --- a/src/frontend/translate/translate_arm/vfp2.cpp +++ b/src/frontend/translate/translate_arm/vfp2.cpp @@ -425,6 +425,35 @@ bool ArmTranslatorVisitor::vfp2_VCVT_to_s32(Cond cond, bool D, size_t Vd, bool s return true; } +bool ArmTranslatorVisitor::vfp2_VMSR(Cond cond, Reg t) { + if (t == Reg::PC) + return UnpredictableInstruction(); + + // VMSR FPSCR, + if (ConditionPassed(cond)) { + ir.PushRSB(ir.current_location.AdvancePC(4)); // TODO: Replace this with a local cache. + ir.SetFpscr(ir.GetRegister(t)); + ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 4)); + ir.SetTerm(IR::Term::PopRSBHint{}); + return false; + } + return true; +} + +bool ArmTranslatorVisitor::vfp2_VMRS(Cond cond, Reg t) { + // VMRS , FPSCR + if (ConditionPassed(cond)) { + if (t == Reg::R15) { + // This encodes ASPR_nzcv access + auto nzcv = ir.GetFpscrNZCV(); + auto old_cpsr = ir.And(ir.GetCpsr(), ir.Imm32(0x0FFFFFFF)); + ir.SetCpsr(ir.Or(nzcv, old_cpsr)); + } else { + ir.SetRegister(t, ir.GetFpscr()); + } + } + return true; +} bool ArmTranslatorVisitor::vfp2_VPOP(Cond cond, bool D, size_t Vd, bool sz, Imm8 imm8) { const ExtReg d = ToExtReg(sz, Vd, D);