From 597a8be5d59f55bbb138d8fa4dde9f65b7f7ddd3 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 6 Mar 2019 13:54:09 -0500 Subject: [PATCH 1/5] ir: Add A64-specific opcodes for getting and setting raw NZCV values This will be necessary to implement the flag manipulation and flag format instructions. --- src/backend/x64/a64_emit_x64.cpp | 15 +++++++++++++++ src/frontend/A64/ir_emitter.cpp | 8 ++++++++ src/frontend/A64/ir_emitter.h | 2 ++ src/frontend/ir/microinstruction.cpp | 2 ++ src/frontend/ir/opcodes.inc | 2 ++ 5 files changed, 29 insertions(+) diff --git a/src/backend/x64/a64_emit_x64.cpp b/src/backend/x64/a64_emit_x64.cpp index a73cceb2..27dd1d76 100644 --- a/src/backend/x64/a64_emit_x64.cpp +++ b/src/backend/x64/a64_emit_x64.cpp @@ -368,6 +368,21 @@ void A64EmitX64::EmitA64GetCFlag(A64EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.DefineValue(inst, result); } +void A64EmitX64::EmitA64GetNZCVRaw(A64EmitContext& ctx, IR::Inst* inst) { + const Xbyak::Reg32 nzcv_raw = ctx.reg_alloc.ScratchGpr().cvt32(); + + code.mov(nzcv_raw, dword[r15 + offsetof(A64JitState, CPSR_nzcv)]); + ctx.reg_alloc.DefineValue(inst, nzcv_raw); +} + +void A64EmitX64::EmitA64SetNZCVRaw(A64EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + const Xbyak::Reg32 nzcv_raw = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32(); + + code.and_(nzcv_raw, 0xF0000000); + code.mov(dword[r15 + offsetof(A64JitState, CPSR_nzcv)], nzcv_raw); +} + void A64EmitX64::EmitA64SetNZCV(A64EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); Xbyak::Reg32 to_store = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32(); diff --git a/src/frontend/A64/ir_emitter.cpp b/src/frontend/A64/ir_emitter.cpp index 3a274b96..ef949d8c 100644 --- a/src/frontend/A64/ir_emitter.cpp +++ b/src/frontend/A64/ir_emitter.cpp @@ -29,6 +29,14 @@ IR::U1 IREmitter::GetCFlag() { return Inst(Opcode::A64GetCFlag); } +IR::U32 IREmitter::GetNZCVRaw() { + return Inst(Opcode::A64GetNZCVRaw); +} + +void IREmitter::SetNZCVRaw(IR::U32 value) { + Inst(Opcode::A64SetNZCVRaw, value); +} + void IREmitter::SetNZCV(const IR::NZCV& nzcv) { Inst(Opcode::A64SetNZCV, nzcv); } diff --git a/src/frontend/A64/ir_emitter.h b/src/frontend/A64/ir_emitter.h index 9983f4b4..ac56448d 100644 --- a/src/frontend/A64/ir_emitter.h +++ b/src/frontend/A64/ir_emitter.h @@ -36,6 +36,8 @@ public: void SetCheckBit(const IR::U1& value); IR::U1 GetCFlag(); + IR::U32 GetNZCVRaw(); + void SetNZCVRaw(IR::U32 value); void SetNZCV(const IR::NZCV& nzcv); void OrQC(const IR::U1& value); diff --git a/src/frontend/ir/microinstruction.cpp b/src/frontend/ir/microinstruction.cpp index c3849bab..10257e54 100644 --- a/src/frontend/ir/microinstruction.cpp +++ b/src/frontend/ir/microinstruction.cpp @@ -124,6 +124,7 @@ bool Inst::ReadsFromCPSR() const { case Opcode::A32GetVFlag: case Opcode::A32GetGEFlags: case Opcode::A64GetCFlag: + case Opcode::A64GetNZCVRaw: case Opcode::ConditionalSelect32: case Opcode::ConditionalSelect64: case Opcode::ConditionalSelectNZCV: @@ -146,6 +147,7 @@ bool Inst::WritesToCPSR() const { case Opcode::A32OrQFlag: case Opcode::A32SetGEFlags: case Opcode::A32SetGEFlagsCompressed: + case Opcode::A64SetNZCVRaw: case Opcode::A64SetNZCV: return true; diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index 256eeba0..1705334c 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -38,6 +38,8 @@ A32OPC(SetFpscrNZCV, Void, NZCV // A64 Context getters/setters A64OPC(SetCheckBit, Void, U1 ) A64OPC(GetCFlag, U1, ) +A64OPC(GetNZCVRaw, U32, ) +A64OPC(SetNZCVRaw, Void, U32 ) A64OPC(SetNZCV, Void, NZCV ) A64OPC(GetW, U32, A64Reg ) A64OPC(GetX, U64, A64Reg ) From 51b526e4536076e43e4bd691779f6deb333b07eb Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 6 Mar 2019 13:56:04 -0500 Subject: [PATCH 2/5] A64: Implement CFINV --- src/CMakeLists.txt | 1 + src/frontend/A64/decoder/a64.inc | 2 +- .../impl/system_flag_manipulation.cpp | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/frontend/A64/translate/impl/system_flag_manipulation.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c9ef4fcb..da59d6f5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -175,6 +175,7 @@ add_library(dynarmic frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp frontend/A64/translate/impl/sys_dc.cpp frontend/A64/translate/impl/system.cpp + frontend/A64/translate/impl/system_flag_manipulation.cpp frontend/A64/translate/translate.cpp frontend/A64/translate/translate.h frontend/A64/types.cpp diff --git a/src/frontend/A64/decoder/a64.inc b/src/frontend/A64/decoder/a64.inc index ae47ff0e..328d4e13 100644 --- a/src/frontend/A64/decoder/a64.inc +++ b/src/frontend/A64/decoder/a64.inc @@ -81,7 +81,7 @@ INST(MSR_reg, "MSR (register)", "11010 INST(MRS, "MRS", "110101010011poooNNNNMMMMooottttt") // System - Flag manipulation instructions -//INST(CFINV, "CFINV", "11010101000000000100000000011111") // ARMv8.4 +INST(CFINV, "CFINV", "11010101000000000100000000011111") // ARMv8.4 //INST(RMIF, "RMIF", "10111010000iiiiii00001nnnnn0IIII") // ARMv8.4 //INST(SETF8, "SETF8", "0011101000000000000010nnnnn01101") // ARMv8.4 //INST(SETF16, "SETF16", "0011101000000000010010nnnnn01101") // ARMv8.4 diff --git a/src/frontend/A64/translate/impl/system_flag_manipulation.cpp b/src/frontend/A64/translate/impl/system_flag_manipulation.cpp new file mode 100644 index 00000000..0ce291ff --- /dev/null +++ b/src/frontend/A64/translate/impl/system_flag_manipulation.cpp @@ -0,0 +1,19 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2019 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include "frontend/A64/translate/impl/impl.h" + +namespace Dynarmic::A64 { + +bool TranslatorVisitor::CFINV() { + const IR::U32 nzcv = ir.GetNZCVRaw(); + const IR::U32 result = ir.Eor(nzcv, ir.Imm32(0x20000000)); + + ir.SetNZCVRaw(result); + return true; +} + +} // namespace Dynarmic::A64 From 20ffe568d0c50836ccbbf8d7cb269d7ab3b66393 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 6 Mar 2019 14:37:15 -0500 Subject: [PATCH 3/5] A64: Implement RMIF --- src/frontend/A64/decoder/a64.inc | 2 +- .../impl/system_flag_manipulation.cpp | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/frontend/A64/decoder/a64.inc b/src/frontend/A64/decoder/a64.inc index 328d4e13..078e97db 100644 --- a/src/frontend/A64/decoder/a64.inc +++ b/src/frontend/A64/decoder/a64.inc @@ -82,7 +82,7 @@ INST(MRS, "MRS", "11010 // System - Flag manipulation instructions INST(CFINV, "CFINV", "11010101000000000100000000011111") // ARMv8.4 -//INST(RMIF, "RMIF", "10111010000iiiiii00001nnnnn0IIII") // ARMv8.4 +INST(RMIF, "RMIF", "10111010000iiiiii00001nnnnn0IIII") // ARMv8.4 //INST(SETF8, "SETF8", "0011101000000000000010nnnnn01101") // ARMv8.4 //INST(SETF16, "SETF16", "0011101000000000010010nnnnn01101") // ARMv8.4 diff --git a/src/frontend/A64/translate/impl/system_flag_manipulation.cpp b/src/frontend/A64/translate/impl/system_flag_manipulation.cpp index 0ce291ff..418caf3f 100644 --- a/src/frontend/A64/translate/impl/system_flag_manipulation.cpp +++ b/src/frontend/A64/translate/impl/system_flag_manipulation.cpp @@ -16,4 +16,48 @@ bool TranslatorVisitor::CFINV() { return true; } +bool TranslatorVisitor::RMIF(Imm<6> lsb, Reg Rn, Imm<4> mask) { + const u32 mask_value = mask.ZeroExtend(); + + // If no bits are to be moved into the NZCV bits, then we + // just preserve the bits and do no extra work. + if (mask_value == 0) { + ir.SetNZCVRaw(ir.GetNZCVRaw()); + return true; + } + + const IR::U64 tmp_reg = ir.GetX(Rn); + const IR::U64 rotated = ir.RotateRight(tmp_reg, ir.Imm8(lsb.ZeroExtend())); + const IR::U32 shifted = ir.LeastSignificantWord(ir.LogicalShiftLeft(rotated, ir.Imm8(28))); + + // On the other hand, if all mask bits are set, then we move all four + // relevant bits in the source register to the NZCV bits. + if (mask_value == 0b1111) { + ir.SetNZCVRaw(shifted); + return true; + } + + // Determine which bits from the PSTATE will be preserved during the operation. + u32 preservation_mask = 0; + if ((mask_value & 0b1000) == 0) { + preservation_mask |= 1U << 31; + } + if ((mask_value & 0b0100) == 0) { + preservation_mask |= 1U << 30; + } + if ((mask_value & 0b0010) == 0) { + preservation_mask |= 1U << 29; + } + if ((mask_value & 0b0001) == 0) { + preservation_mask |= 1U << 28; + } + + const IR::U32 masked = ir.And(shifted, ir.Imm32(~preservation_mask)); + const IR::U32 nzcv = ir.And(ir.GetNZCVRaw(), ir.Imm32(preservation_mask)); + const IR::U32 result = ir.Or(nzcv, masked); + + ir.SetNZCVRaw(result); + return true; +} + } // namespace Dynarmic::A64 From aa22db534b2a69bcfb6a141afe05d3827a94529d Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 6 Mar 2019 17:38:15 -0500 Subject: [PATCH 4/5] A64: Implement AXFlag and XAFlag --- src/CMakeLists.txt | 1 + src/frontend/A64/decoder/a64.inc | 4 +- .../A64/translate/impl/system_flag_format.cpp | 47 +++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 src/frontend/A64/translate/impl/system_flag_format.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da59d6f5..9fd65fe5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -175,6 +175,7 @@ add_library(dynarmic frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp frontend/A64/translate/impl/sys_dc.cpp frontend/A64/translate/impl/system.cpp + frontend/A64/translate/impl/system_flag_format.cpp frontend/A64/translate/impl/system_flag_manipulation.cpp frontend/A64/translate/translate.cpp frontend/A64/translate/translate.h diff --git a/src/frontend/A64/decoder/a64.inc b/src/frontend/A64/decoder/a64.inc index 078e97db..d1d3fbf2 100644 --- a/src/frontend/A64/decoder/a64.inc +++ b/src/frontend/A64/decoder/a64.inc @@ -87,8 +87,8 @@ INST(RMIF, "RMIF", "10111 //INST(SETF16, "SETF16", "0011101000000000010010nnnnn01101") // ARMv8.4 // System - Flag format instructions -//INST(XAFlag, "XAFlag", "11010101000000000100000000111111") // ARMv8.5 -//INST(AXFlag, "AXFlag", "11010101000000000100000001011111") // ARMv8.5 +INST(XAFlag, "XAFlag", "11010101000000000100000000111111") // ARMv8.5 +INST(AXFlag, "AXFlag", "11010101000000000100000001011111") // ARMv8.5 // SYS: Data Cache INST(DC_IVAC, "DC IVAC", "110101010000100001110110001ttttt") diff --git a/src/frontend/A64/translate/impl/system_flag_format.cpp b/src/frontend/A64/translate/impl/system_flag_format.cpp new file mode 100644 index 00000000..c7fc56d9 --- /dev/null +++ b/src/frontend/A64/translate/impl/system_flag_format.cpp @@ -0,0 +1,47 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2019 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include "frontend/A64/translate/impl/impl.h" + +namespace Dynarmic::A64 { + +bool TranslatorVisitor::AXFlag() { + const IR::U32 nzcv = ir.GetNZCVRaw(); + + const IR::U32 z = ir.And(nzcv, ir.Imm32(0x40000000)); + const IR::U32 c = ir.And(nzcv, ir.Imm32(0x20000000)); + const IR::U32 v = ir.And(nzcv, ir.Imm32(0x10000000)); + + const IR::U32 new_z = ir.Or(ir.LogicalShiftLeft(v, ir.Imm8(2)), z); + const IR::U32 new_c = ir.And(ir.And(c, ir.Not(ir.LogicalShiftLeft(v, ir.Imm8(1)))), ir.Imm32(0x20000000)); + + ir.SetNZCVRaw(ir.Or(new_z, new_c)); + return true; +} + +bool TranslatorVisitor::XAFlag() { + const IR::U32 nzcv = ir.GetNZCVRaw(); + + const IR::U32 z = ir.And(nzcv, ir.Imm32(0x40000000)); + const IR::U32 c = ir.And(nzcv, ir.Imm32(0x20000000)); + + const IR::U32 not_z = ir.And(ir.Not(z), ir.Imm32(0x40000000)); + const IR::U32 not_c = ir.And(ir.Not(c), ir.Imm32(0x20000000)); + + const IR::U32 new_n = ir.And(ir.LogicalShiftLeft(not_c, ir.Imm8(2)), + ir.LogicalShiftLeft(not_z, ir.Imm8(1))); + const IR::U32 new_z = ir.And(z, ir.LogicalShiftLeft(c, ir.Imm8(1))); + const IR::U32 new_c = ir.Or(c, ir.LogicalShiftRight(z, ir.Imm8(1))); + const IR::U32 new_v = ir.And(ir.LogicalShiftRight(not_c, ir.Imm8(1)), + ir.LogicalShiftRight(z, ir.Imm8(2))); + + const IR::U32 result = ir.Or(ir.Or(ir.Or(new_n, new_z), new_c), new_v); + + ir.SetNZCVRaw(result); + return true; +} + +} // namespace Dynarmic::A64 From 6aed4036ef8abaa950663f0aa0175d27d3228223 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 6 Mar 2019 18:46:30 -0500 Subject: [PATCH 5/5] ir_opt/a64_get_set_elimination_pass: Add handling for NZCV raw get and set operations --- src/ir_opt/a64_get_set_elimination_pass.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ir_opt/a64_get_set_elimination_pass.cpp b/src/ir_opt/a64_get_set_elimination_pass.cpp index 748ed7c4..56da5046 100644 --- a/src/ir_opt/a64_get_set_elimination_pass.cpp +++ b/src/ir_opt/a64_get_set_elimination_pass.cpp @@ -23,7 +23,7 @@ void A64GetSetElimination(IR::Block& block) { enum class TrackingType { W, X, S, D, Q, - SP, NZCV, + SP, NZCV, NZCVRaw, }; struct RegisterInfo { IR::Value register_value; @@ -100,6 +100,10 @@ void A64GetSetElimination(IR::Block& block) { do_get(sp_info, inst, TrackingType::SP); break; } + case IR::Opcode::A64GetNZCVRaw: { + do_get(nzcv_info, inst, TrackingType::NZCVRaw); + break; + } case IR::Opcode::A64SetW: { const size_t index = A64::RegNumber(inst->GetArg(0).GetA64RegRef()); do_set(reg_info.at(index), inst->GetArg(1), inst, TrackingType::W); @@ -133,6 +137,10 @@ void A64GetSetElimination(IR::Block& block) { do_set(nzcv_info, inst->GetArg(0), inst, TrackingType::NZCV); break; } + case IR::Opcode::A64SetNZCVRaw: { + do_set(nzcv_info, inst->GetArg(0), inst, TrackingType::NZCVRaw); + break; + } default: { if (inst->ReadsFromCPSR() || inst->WritesToCPSR()) { nzcv_info = {};