From 0641445e517f9d9f6635cbb80dd8b6bf4bf2739c Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 7 Jan 2018 12:52:12 +0000 Subject: [PATCH] A64: Implement logical --- src/CMakeLists.txt | 1 + src/backend_x64/a64_interface.cpp | 1 + src/backend_x64/emit_x64.cpp | 110 ++++++++- src/common/bit_util.h | 33 +++ src/frontend/A64/decoder/a64.h | 24 +- .../impl/data_processing_logical.cpp | 216 ++++++++++++++++++ src/frontend/A64/translate/impl/impl.cpp | 24 ++ src/frontend/A64/translate/impl/impl.h | 8 + src/frontend/ir/ir_emitter.cpp | 43 +++- src/frontend/ir/ir_emitter.h | 4 + src/frontend/ir/microinstruction.cpp | 8 + src/frontend/ir/opcodes.inc | 12 +- tests/A64/a64.cpp | 41 ++++ 13 files changed, 499 insertions(+), 26 deletions(-) create mode 100644 src/frontend/A64/translate/impl/data_processing_logical.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 073eacc7..9678890b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,6 +59,7 @@ add_library(dynarmic frontend/A64/location_descriptor.cpp frontend/A64/location_descriptor.h frontend/A64/translate/impl/data_processing_addsub.cpp + frontend/A64/translate/impl/data_processing_logical.cpp frontend/A64/translate/impl/data_processing_pcrel.cpp frontend/A64/translate/impl/impl.cpp frontend/A64/translate/impl/impl.h diff --git a/src/backend_x64/a64_interface.cpp b/src/backend_x64/a64_interface.cpp index a70bb398..c41c06bd 100644 --- a/src/backend_x64/a64_interface.cpp +++ b/src/backend_x64/a64_interface.cpp @@ -150,6 +150,7 @@ private: // JIT Compile IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); }); Optimization::DeadCodeElimination(ir_block); + // printf("%s\n", IR::DumpBlock(ir_block).c_str()); Optimization::VerificationPass(ir_block); return emitter.Emit(ir_block).entrypoint; } diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 4ce7b463..27b94587 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -132,8 +132,31 @@ void EmitX64::EmitGetGEFromOp(EmitContext&, IR::Inst*) { } template -void EmitX64::EmitGetNZCVFromOp(EmitContext&, IR::Inst*) { - ASSERT_MSG(false, "should never happen"); +void EmitX64::EmitGetNZCVFromOp(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + const size_t bitsize = [&]{ + switch (args[0].GetType()) { + case IR::Type::U8: + return 8; + case IR::Type::U16: + return 16; + case IR::Type::U32: + return 32; + case IR::Type::U64: + return 64; + default: + ASSERT_MSG(false, "Unreachable"); + return 0; + } + }(); + + Xbyak::Reg64 nzcv = ctx.reg_alloc.ScratchGpr({HostLoc::RAX}); + Xbyak::Reg value = ctx.reg_alloc.UseGpr(args[0]).changeBit(bitsize); + code->cmp(value, 0); + code->lahf(); + code->seto(code->al); + ctx.reg_alloc.DefineValue(inst, nzcv); } template @@ -933,7 +956,7 @@ void EmitX64::EmitMul64(EmitContext& ctx, IR::Inst* inst) { } template -void EmitX64::EmitAnd(EmitContext& ctx, IR::Inst* inst) { +void EmitX64::EmitAnd32(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32(); @@ -953,7 +976,27 @@ void EmitX64::EmitAnd(EmitContext& ctx, IR::Inst* inst) { } template -void EmitX64::EmitEor(EmitContext& ctx, IR::Inst* inst) { +void EmitX64::EmitAnd64(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(args[0]); + + if (args[1].FitsInImmediateU32()) { + u32 op_arg = args[1].GetImmediateU32(); + + code->and_(result, op_arg); + } else { + OpArg op_arg = ctx.reg_alloc.UseOpArg(args[1]); + op_arg.setBit(64); + + code->and_(result, *op_arg); + } + + ctx.reg_alloc.DefineValue(inst, result); +} + +template +void EmitX64::EmitEor32(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32(); @@ -973,7 +1016,27 @@ void EmitX64::EmitEor(EmitContext& ctx, IR::Inst* inst) { } template -void EmitX64::EmitOr(EmitContext& ctx, IR::Inst* inst) { +void EmitX64::EmitEor64(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(args[0]); + + if (args[1].FitsInImmediateU32()) { + u32 op_arg = args[1].GetImmediateU32(); + + code->xor_(result, op_arg); + } else { + OpArg op_arg = ctx.reg_alloc.UseOpArg(args[1]); + op_arg.setBit(64); + + code->xor_(result, *op_arg); + } + + ctx.reg_alloc.DefineValue(inst, result); +} + +template +void EmitX64::EmitOr32(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32(); @@ -993,7 +1056,27 @@ void EmitX64::EmitOr(EmitContext& ctx, IR::Inst* inst) { } template -void EmitX64::EmitNot(EmitContext& ctx, IR::Inst* inst) { +void EmitX64::EmitOr64(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(args[0]); + + if (args[1].FitsInImmediateU32()) { + u32 op_arg = args[1].GetImmediateU32(); + + code->or_(result, op_arg); + } else { + OpArg op_arg = ctx.reg_alloc.UseOpArg(args[1]); + op_arg.setBit(64); + + code->or_(result, *op_arg); + } + + ctx.reg_alloc.DefineValue(inst, result); +} + +template +void EmitX64::EmitNot32(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); Xbyak::Reg32 result; @@ -1007,6 +1090,21 @@ void EmitX64::EmitNot(EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.DefineValue(inst, result); } +template +void EmitX64::EmitNot64(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + Xbyak::Reg64 result; + if (args[0].IsImmediate()) { + result = ctx.reg_alloc.ScratchGpr(); + code->mov(result, ~args[0].GetImmediateU64()); + } else { + result = ctx.reg_alloc.UseScratchGpr(args[0]); + code->not_(result); + } + ctx.reg_alloc.DefineValue(inst, result); +} + template void EmitX64::EmitSignExtendByteToWord(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); diff --git a/src/common/bit_util.h b/src/common/bit_util.h index fb1c993d..7bd09fff 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "common/assert.h" @@ -86,5 +87,37 @@ inline size_t BitCount(Integral value) { return std::bitset()>(value).count(); } +template +inline int HighestSetBit(T value) { + auto x = static_cast>(value); + int result = -1; + while (x != 0) { + x >>= 1; + result++; + } + return result; +} + +template +inline T Ones(size_t count) { + ASSERT_MSG(count <= BitSize(), "count larger than bitsize of T"); + return ~(~static_cast(0) << count); +} + +template +inline T Replicate(T value, size_t element_size) { + ASSERT_MSG(BitSize() % element_size == 0, "bitsize of T not divisible by element_size"); + if (element_size == BitSize()) + return value; + return Replicate(value | (value << element_size), element_size * 2); +} + +template +inline T RotateRight(T value, size_t amount) { + amount %= BitSize(); + auto x = static_cast>(value); + return static_cast((x >> amount) | (x << (BitSize() - amount))); +} + } // namespace Common } // namespace Dynarmic diff --git a/src/frontend/A64/decoder/a64.h b/src/frontend/A64/decoder/a64.h index bc0e7f97..12d7b112 100644 --- a/src/frontend/A64/decoder/a64.h +++ b/src/frontend/A64/decoder/a64.h @@ -40,10 +40,10 @@ std::vector> GetDecodeTable() { INST(&V::SUBS_imm, "SUBS (immediate)", "z1110001ssiiiiiiiiiiiinnnnnddddd"), // Data processing - Immediate - Logical - //INST(&V::AND_imm, "AND (immediate)", "z00100100Nrrrrrrssssssnnnnnddddd"), - //INST(&V::ORR_imm, "ORR (immediate)", "z01100100Nrrrrrrssssssnnnnnddddd"), - //INST(&V::EOR_imm, "EOR (immediate)", "z10100100Nrrrrrrssssssnnnnnddddd"), - //INST(&V::ANDS_imm, "ANDS (immediate)", "z11100100Nrrrrrrssssssnnnnnddddd"), + INST(&V::AND_imm, "AND (immediate)", "z00100100Nrrrrrrssssssnnnnnddddd"), + INST(&V::ORR_imm, "ORR (immediate)", "z01100100Nrrrrrrssssssnnnnnddddd"), + INST(&V::EOR_imm, "EOR (immediate)", "z10100100Nrrrrrrssssssnnnnnddddd"), + INST(&V::ANDS_imm, "ANDS (immediate)", "z11100100Nrrrrrrssssssnnnnnddddd"), // Data processing - Immediate - Move Wide //INST(&V::MOVN, "MOVN", "z00100101ssiiiiiiiiiiiiiiiiddddd"), @@ -363,14 +363,14 @@ std::vector> GetDecodeTable() { //INST(&V::AUTDB, "AUTDB, AUTDZB", "110110101100000100Z111nnnnnddddd"), // Data Processing - Register - Logical (shifted register) - //INST(&V::AND_shift, "AND (shifted register)", "z0001010ss0mmmmmiiiiiinnnnnddddd"), - //INST(&V::BIC_shift, "BIC (shifted register)", "z0001010ss1mmmmmiiiiiinnnnnddddd"), - //INST(&V::ORR_shift, "ORR (shifted register)", "z0101010ss0mmmmmiiiiiinnnnnddddd"), - //INST(&V::ORN_shift, "ORN (shifted register)", "z0101010ss1mmmmmiiiiiinnnnnddddd"), - //INST(&V::EOR_shift, "EOR (shifted register)", "z1001010ss0mmmmmiiiiiinnnnnddddd"), - //INST(&V::EON, "EON (shifted register)", "z1001010ss1mmmmmiiiiiinnnnnddddd"), - //INST(&V::ANDS_shift, "ANDS (shifted register)", "z1101010ss0mmmmmiiiiiinnnnnddddd"), - //INST(&V::BICS, "BICS (shifted register)", "z1101010ss1mmmmmiiiiiinnnnnddddd"), + INST(&V::AND_shift, "AND (shifted register)", "z0001010ss0mmmmmiiiiiinnnnnddddd"), + INST(&V::BIC_shift, "BIC (shifted register)", "z0001010ss1mmmmmiiiiiinnnnnddddd"), + INST(&V::ORR_shift, "ORR (shifted register)", "z0101010ss0mmmmmiiiiiinnnnnddddd"), + INST(&V::ORN_shift, "ORN (shifted register)", "z0101010ss1mmmmmiiiiiinnnnnddddd"), + INST(&V::EOR_shift, "EOR (shifted register)", "z1001010ss0mmmmmiiiiiinnnnnddddd"), + INST(&V::EON, "EON (shifted register)", "z1001010ss1mmmmmiiiiiinnnnnddddd"), + INST(&V::ANDS_shift, "ANDS (shifted register)", "z1101010ss0mmmmmiiiiiinnnnnddddd"), + INST(&V::BICS, "BICS (shifted register)", "z1101010ss1mmmmmiiiiiinnnnnddddd"), // Data Processing - Register - Add/Sub (shifted register) INST(&V::ADD_shift, "ADD (shifted register)", "z0001011ss0mmmmmiiiiiinnnnnddddd"), diff --git a/src/frontend/A64/translate/impl/data_processing_logical.cpp b/src/frontend/A64/translate/impl/data_processing_logical.cpp new file mode 100644 index 00000000..8e6bb813 --- /dev/null +++ b/src/frontend/A64/translate/impl/data_processing_logical.cpp @@ -0,0 +1,216 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 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 { +namespace A64 { + +bool TranslatorVisitor::AND_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && N) return ReservedValue(); + u64 imm; + if (auto masks = DecodeBitMasks(N, imms, immr, true)) { + imm = masks->wmask; + } else { + return ReservedValue(); + } + + auto operand1 = X(datasize, Rn); + + auto result = ir.And(operand1, I(datasize, imm)); + if (Rd == Reg::SP) { + SP(datasize, result); + } else { + X(datasize, Rd, result); + } + + return true; +} + +bool TranslatorVisitor::ORR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && N) return ReservedValue(); + u64 imm; + if (auto masks = DecodeBitMasks(N, imms, immr, true)) { + imm = masks->wmask; + } else { + return ReservedValue(); + } + + auto operand1 = X(datasize, Rn); + + auto result = ir.Or(operand1, I(datasize, imm)); + if (Rd == Reg::SP) { + SP(datasize, result); + } else { + X(datasize, Rd, result); + } + + return true; +} + +bool TranslatorVisitor::EOR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && N) return ReservedValue(); + u64 imm; + if (auto masks = DecodeBitMasks(N, imms, immr, true)) { + imm = masks->wmask; + } else { + return ReservedValue(); + } + + auto operand1 = X(datasize, Rn); + + auto result = ir.Eor(operand1, I(datasize, imm)); + if (Rd == Reg::SP) { + SP(datasize, result); + } else { + X(datasize, Rd, result); + } + + return true; +} + +bool TranslatorVisitor::ANDS_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && N) return ReservedValue(); + u64 imm; + if (auto masks = DecodeBitMasks(N, imms, immr, true)) { + imm = masks->wmask; + } else { + return ReservedValue(); + } + + auto operand1 = X(datasize, Rn); + + auto result = ir.And(operand1, I(datasize, imm)); + ir.SetNZCV(ir.NZCVFrom(result)); + X(datasize, Rd, result); + + return true; +} + +bool TranslatorVisitor::AND_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && imm6.Bit<5>()) return ReservedValue(); + u8 shift_amount = imm6.ZeroExtend(); + + auto operand1 = X(datasize, Rn); + auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)); + + auto result = ir.And(operand1, operand2); + X(datasize, Rd, result); + + return true; +} + +bool TranslatorVisitor::BIC_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && imm6.Bit<5>()) return ReservedValue(); + u8 shift_amount = imm6.ZeroExtend(); + + auto operand1 = X(datasize, Rn); + auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)); + + operand2 = ir.Not(operand2); + auto result = ir.And(operand1, operand2); + X(datasize, Rd, result); + + return true; +} + +bool TranslatorVisitor::ORR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && imm6.Bit<5>()) return ReservedValue(); + u8 shift_amount = imm6.ZeroExtend(); + + auto operand1 = X(datasize, Rn); + auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)); + + auto result = ir.Or(operand1, operand2); + X(datasize, Rd, result); + + return true; +} + +bool TranslatorVisitor::ORN_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && imm6.Bit<5>()) return ReservedValue(); + u8 shift_amount = imm6.ZeroExtend(); + + auto operand1 = X(datasize, Rn); + auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)); + + operand2 = ir.Not(operand2); + auto result = ir.Or(operand1, operand2); + X(datasize, Rd, result); + + return true; +} + +bool TranslatorVisitor::EOR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && imm6.Bit<5>()) return ReservedValue(); + u8 shift_amount = imm6.ZeroExtend(); + + auto operand1 = X(datasize, Rn); + auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)); + + auto result = ir.Eor(operand1, operand2); + X(datasize, Rd, result); + + return true; +} + +bool TranslatorVisitor::EON(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && imm6.Bit<5>()) return ReservedValue(); + u8 shift_amount = imm6.ZeroExtend(); + + auto operand1 = X(datasize, Rn); + auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)); + + operand2 = ir.Not(operand2); + auto result = ir.Eor(operand1, operand2); + X(datasize, Rd, result); + + return true; +} + +bool TranslatorVisitor::ANDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && imm6.Bit<5>()) return ReservedValue(); + u8 shift_amount = imm6.ZeroExtend(); + + auto operand1 = X(datasize, Rn); + auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)); + + auto result = ir.And(operand1, operand2); + ir.SetNZCV(ir.NZCVFrom(result)); + X(datasize, Rd, result); + + return true; +} + +bool TranslatorVisitor::BICS(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + if (!sf && imm6.Bit<5>()) return ReservedValue(); + u8 shift_amount = imm6.ZeroExtend(); + + auto operand1 = X(datasize, Rn); + auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)); + + operand2 = ir.Not(operand2); + auto result = ir.And(operand1, operand2); + ir.SetNZCV(ir.NZCVFrom(result)); + X(datasize, Rd, result); + + return true; +} + +} // namespace A64 +} // namespace Dynarmic diff --git a/src/frontend/A64/translate/impl/impl.cpp b/src/frontend/A64/translate/impl/impl.cpp index fbcb7150..292bc2fc 100644 --- a/src/frontend/A64/translate/impl/impl.cpp +++ b/src/frontend/A64/translate/impl/impl.cpp @@ -4,6 +4,7 @@ * General Public License version 2 or any later version. */ +#include "common/bit_util.h" #include "frontend/ir/terminal.h" #include "frontend/A64/translate/impl/impl.h" @@ -25,6 +26,29 @@ bool TranslatorVisitor::ReservedValue() { return false; } +boost::optional TranslatorVisitor::DecodeBitMasks(bool immN, Imm<6> imms, Imm<6> immr, bool immediate) { + int len = Common::HighestSetBit((immN ? 1 << 6 : 0) | (imms.ZeroExtend() ^ 0b111111)); + if (len < 1) + return boost::none; + + size_t levels = Common::Ones(len); + + if (immediate && (imms.ZeroExtend() & levels) == levels) + return boost::none; + + s32 S = s32(imms.ZeroExtend() & levels); + s32 R = s32(immr.ZeroExtend() & levels); + u64 d = u64(S - R) & levels; + + size_t esize = 1 << len; + u64 welem = Common::Ones(S + 1); + u64 telem = Common::Ones(d + 1); + u64 wmask = Common::RotateRight(Common::Replicate(welem, esize), R); + u64 tmask = Common::Replicate(telem, esize); + + return BitMasks{wmask, tmask}; +} + IR::U32U64 TranslatorVisitor::I(size_t bitsize, u64 value) { switch (bitsize) { case 32: diff --git a/src/frontend/A64/translate/impl/impl.h b/src/frontend/A64/translate/impl/impl.h index 8a141df1..81c84d91 100644 --- a/src/frontend/A64/translate/impl/impl.h +++ b/src/frontend/A64/translate/impl/impl.h @@ -6,6 +6,8 @@ #pragma once +#include + #include "frontend/A64/imm.h" #include "frontend/A64/ir_emitter.h" #include "frontend/A64/location_descriptor.h" @@ -25,6 +27,12 @@ struct TranslatorVisitor final { bool UnpredictableInstruction(); bool ReservedValue(); + struct BitMasks { + u64 wmask, tmask; + }; + + boost::optional DecodeBitMasks(bool N, Imm<6> immr, Imm<6> imms, bool immediate); + IR::U32U64 I(size_t bitsize, u64 value); IR::U32U64 X(size_t bitsize, Reg reg); void X(size_t bitsize, Reg reg, IR::U32U64 value); diff --git a/src/frontend/ir/ir_emitter.cpp b/src/frontend/ir/ir_emitter.cpp index 29b80275..88e8044d 100644 --- a/src/frontend/ir/ir_emitter.cpp +++ b/src/frontend/ir/ir_emitter.cpp @@ -221,19 +221,54 @@ U64 IREmitter::Mul(const U64& a, const U64& b) { } U32 IREmitter::And(const U32& a, const U32& b) { - return Inst(Opcode::And, a, b); + return Inst(Opcode::And32, a, b); +} + +U32U64 IREmitter::And(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::And32, a, b); + } else { + return Inst(Opcode::And64, a, b); + } } U32 IREmitter::Eor(const U32& a, const U32& b) { - return Inst(Opcode::Eor, a, b); + return Inst(Opcode::Eor32, a, b); +} + +U32U64 IREmitter::Eor(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Eor32, a, b); + } else { + return Inst(Opcode::Eor64, a, b); + } } U32 IREmitter::Or(const U32& a, const U32& b) { - return Inst(Opcode::Or, a, b); + return Inst(Opcode::Or32, a, b); +} + +U32U64 IREmitter::Or(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Or32, a, b); + } else { + return Inst(Opcode::Or64, a, b); + } } U32 IREmitter::Not(const U32& a) { - return Inst(Opcode::Not, a); + return Inst(Opcode::Not32, a); +} + +U32U64 IREmitter::Not(const U32U64& a) { + if (a.GetType() == Type::U32) { + return Inst(Opcode::Not32, a); + } else { + return Inst(Opcode::Not64, a); + } } U64 IREmitter::SignExtendToLong(const UAny& a) { diff --git a/src/frontend/ir/ir_emitter.h b/src/frontend/ir/ir_emitter.h index 64b573b3..8a831d2b 100644 --- a/src/frontend/ir/ir_emitter.h +++ b/src/frontend/ir/ir_emitter.h @@ -108,9 +108,13 @@ public: U32 Mul(const U32& a, const U32& b); U64 Mul(const U64& a, const U64& b); U32 And(const U32& a, const U32& b); + U32U64 And(const U32U64& a, const U32U64& b); U32 Eor(const U32& a, const U32& b); + U32U64 Eor(const U32U64& a, const U32U64& b); U32 Or(const U32& a, const U32& b); + U32U64 Or(const U32U64& a, const U32U64& b); U32 Not(const U32& a); + U32U64 Not(const U32U64& a); U32 SignExtendToWord(const UAny& a); U64 SignExtendToLong(const UAny& a); U32 SignExtendByteToWord(const U8& a); diff --git a/src/frontend/ir/microinstruction.cpp b/src/frontend/ir/microinstruction.cpp index 0f4636a1..46a806c5 100644 --- a/src/frontend/ir/microinstruction.cpp +++ b/src/frontend/ir/microinstruction.cpp @@ -273,6 +273,14 @@ bool Inst::MayGetNZCVFromOp() const { case Opcode::Add64: case Opcode::Sub32: case Opcode::Sub64: + case Opcode::And32: + case Opcode::And64: + case Opcode::Eor32: + case Opcode::Eor64: + case Opcode::Or32: + case Opcode::Or64: + case Opcode::Not32: + case Opcode::Not64: return true; default: diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index 39a71cb4..9cdc3c78 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -77,10 +77,14 @@ OPCODE(Sub32, T::U32, T::U32, T::U32, OPCODE(Sub64, T::U64, T::U64, T::U64, T::U1 ) OPCODE(Mul32, T::U32, T::U32, T::U32 ) OPCODE(Mul64, T::U64, T::U64, T::U64 ) -OPCODE(And, T::U32, T::U32, T::U32 ) -OPCODE(Eor, T::U32, T::U32, T::U32 ) -OPCODE(Or, T::U32, T::U32, T::U32 ) -OPCODE(Not, T::U32, T::U32 ) +OPCODE(And32, T::U32, T::U32, T::U32 ) +OPCODE(And64, T::U64, T::U64, T::U64 ) +OPCODE(Eor32, T::U32, T::U32, T::U32 ) +OPCODE(Eor64, T::U64, T::U64, T::U64 ) +OPCODE(Or32, T::U32, T::U32, T::U32 ) +OPCODE(Or64, T::U64, T::U64, T::U64 ) +OPCODE(Not32, T::U32, T::U32 ) +OPCODE(Not64, T::U64, T::U64 ) OPCODE(SignExtendByteToWord, T::U32, T::U8 ) OPCODE(SignExtendHalfToWord, T::U32, T::U16 ) OPCODE(SignExtendByteToLong, T::U64, T::U8 ) diff --git a/tests/A64/a64.cpp b/tests/A64/a64.cpp index f9a555b8..809a9264 100644 --- a/tests/A64/a64.cpp +++ b/tests/A64/a64.cpp @@ -72,3 +72,44 @@ TEST_CASE("A64: ADD", "[a64]") { REQUIRE(jit.GetRegister(2) == 2); REQUIRE(jit.GetPC() == 4); } + +TEST_CASE("A64: AND", "[a64]") { + TestEnv env; + Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; + + env.code_mem[0] = 0x8a020020; // AND X0, X1, X2 + env.code_mem[1] = 0x14000000; // B . + + jit.SetRegister(0, 0); + jit.SetRegister(1, 1); + jit.SetRegister(2, 3); + jit.SetPC(0); + + env.ticks_left = 2; + jit.Run(); + + REQUIRE(jit.GetRegister(0) == 1); + REQUIRE(jit.GetRegister(1) == 1); + REQUIRE(jit.GetRegister(2) == 3); + REQUIRE(jit.GetPC() == 4); +} + +TEST_CASE("A64: Bitmasks", "[a64]") { + TestEnv env; + Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; + + env.code_mem[0] = 0x3200c3e0; // ORR W0, WZR, #0x01010101 + env.code_mem[1] = 0x320c8fe1; // ORR W1, WZR, #0x00F000F0 + env.code_mem[2] = 0x320003e2; // ORR W2, WZR, #1 + env.code_mem[3] = 0x14000000; // B . + + jit.SetPC(0); + + env.ticks_left = 4; + jit.Run(); + + REQUIRE(jit.GetRegister(0) == 0x01010101); + REQUIRE(jit.GetRegister(1) == 0x00F000F0); + REQUIRE(jit.GetRegister(2) == 1); + REQUIRE(jit.GetPC() == 12); +}