A64: Implement logical
This commit is contained in:
parent
5a1d88c5dc
commit
0641445e51
13 changed files with 499 additions and 26 deletions
|
@ -59,6 +59,7 @@ add_library(dynarmic
|
||||||
frontend/A64/location_descriptor.cpp
|
frontend/A64/location_descriptor.cpp
|
||||||
frontend/A64/location_descriptor.h
|
frontend/A64/location_descriptor.h
|
||||||
frontend/A64/translate/impl/data_processing_addsub.cpp
|
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/data_processing_pcrel.cpp
|
||||||
frontend/A64/translate/impl/impl.cpp
|
frontend/A64/translate/impl/impl.cpp
|
||||||
frontend/A64/translate/impl/impl.h
|
frontend/A64/translate/impl/impl.h
|
||||||
|
|
|
@ -150,6 +150,7 @@ private:
|
||||||
// JIT Compile
|
// JIT Compile
|
||||||
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); });
|
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); });
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
Optimization::DeadCodeElimination(ir_block);
|
||||||
|
// printf("%s\n", IR::DumpBlock(ir_block).c_str());
|
||||||
Optimization::VerificationPass(ir_block);
|
Optimization::VerificationPass(ir_block);
|
||||||
return emitter.Emit(ir_block).entrypoint;
|
return emitter.Emit(ir_block).entrypoint;
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,8 +132,31 @@ void EmitX64<JST>::EmitGetGEFromOp(EmitContext&, IR::Inst*) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename JST>
|
template <typename JST>
|
||||||
void EmitX64<JST>::EmitGetNZCVFromOp(EmitContext&, IR::Inst*) {
|
void EmitX64<JST>::EmitGetNZCVFromOp(EmitContext& ctx, IR::Inst* inst) {
|
||||||
ASSERT_MSG(false, "should never happen");
|
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 <typename JST>
|
template <typename JST>
|
||||||
|
@ -933,7 +956,7 @@ void EmitX64<JST>::EmitMul64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename JST>
|
template <typename JST>
|
||||||
void EmitX64<JST>::EmitAnd(EmitContext& ctx, IR::Inst* inst) {
|
void EmitX64<JST>::EmitAnd32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
@ -953,7 +976,27 @@ void EmitX64<JST>::EmitAnd(EmitContext& ctx, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename JST>
|
template <typename JST>
|
||||||
void EmitX64<JST>::EmitEor(EmitContext& ctx, IR::Inst* inst) {
|
void EmitX64<JST>::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 <typename JST>
|
||||||
|
void EmitX64<JST>::EmitEor32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
@ -973,7 +1016,27 @@ void EmitX64<JST>::EmitEor(EmitContext& ctx, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename JST>
|
template <typename JST>
|
||||||
void EmitX64<JST>::EmitOr(EmitContext& ctx, IR::Inst* inst) {
|
void EmitX64<JST>::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 <typename JST>
|
||||||
|
void EmitX64<JST>::EmitOr32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
@ -993,7 +1056,27 @@ void EmitX64<JST>::EmitOr(EmitContext& ctx, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename JST>
|
template <typename JST>
|
||||||
void EmitX64<JST>::EmitNot(EmitContext& ctx, IR::Inst* inst) {
|
void EmitX64<JST>::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 <typename JST>
|
||||||
|
void EmitX64<JST>::EmitNot32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
Xbyak::Reg32 result;
|
Xbyak::Reg32 result;
|
||||||
|
@ -1007,6 +1090,21 @@ void EmitX64<JST>::EmitNot(EmitContext& ctx, IR::Inst* inst) {
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::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 <typename JST>
|
template <typename JST>
|
||||||
void EmitX64<JST>::EmitSignExtendByteToWord(EmitContext& ctx, IR::Inst* inst) {
|
void EmitX64<JST>::EmitSignExtendByteToWord(EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
|
||||||
|
@ -86,5 +87,37 @@ inline size_t BitCount(Integral value) {
|
||||||
return std::bitset<BitSize<Integral>()>(value).count();
|
return std::bitset<BitSize<Integral>()>(value).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline int HighestSetBit(T value) {
|
||||||
|
auto x = static_cast<std::make_unsigned_t<T>>(value);
|
||||||
|
int result = -1;
|
||||||
|
while (x != 0) {
|
||||||
|
x >>= 1;
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T Ones(size_t count) {
|
||||||
|
ASSERT_MSG(count <= BitSize<T>(), "count larger than bitsize of T");
|
||||||
|
return ~(~static_cast<T>(0) << count);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T Replicate(T value, size_t element_size) {
|
||||||
|
ASSERT_MSG(BitSize<T>() % element_size == 0, "bitsize of T not divisible by element_size");
|
||||||
|
if (element_size == BitSize<T>())
|
||||||
|
return value;
|
||||||
|
return Replicate(value | (value << element_size), element_size * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T RotateRight(T value, size_t amount) {
|
||||||
|
amount %= BitSize<T>();
|
||||||
|
auto x = static_cast<std::make_unsigned_t<T>>(value);
|
||||||
|
return static_cast<T>((x >> amount) | (x << (BitSize<T>() - amount)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
} // namespace Dynarmic
|
} // namespace Dynarmic
|
||||||
|
|
|
@ -40,10 +40,10 @@ std::vector<Matcher<V>> GetDecodeTable() {
|
||||||
INST(&V::SUBS_imm, "SUBS (immediate)", "z1110001ssiiiiiiiiiiiinnnnnddddd"),
|
INST(&V::SUBS_imm, "SUBS (immediate)", "z1110001ssiiiiiiiiiiiinnnnnddddd"),
|
||||||
|
|
||||||
// Data processing - Immediate - Logical
|
// Data processing - Immediate - Logical
|
||||||
//INST(&V::AND_imm, "AND (immediate)", "z00100100Nrrrrrrssssssnnnnnddddd"),
|
INST(&V::AND_imm, "AND (immediate)", "z00100100Nrrrrrrssssssnnnnnddddd"),
|
||||||
//INST(&V::ORR_imm, "ORR (immediate)", "z01100100Nrrrrrrssssssnnnnnddddd"),
|
INST(&V::ORR_imm, "ORR (immediate)", "z01100100Nrrrrrrssssssnnnnnddddd"),
|
||||||
//INST(&V::EOR_imm, "EOR (immediate)", "z10100100Nrrrrrrssssssnnnnnddddd"),
|
INST(&V::EOR_imm, "EOR (immediate)", "z10100100Nrrrrrrssssssnnnnnddddd"),
|
||||||
//INST(&V::ANDS_imm, "ANDS (immediate)", "z11100100Nrrrrrrssssssnnnnnddddd"),
|
INST(&V::ANDS_imm, "ANDS (immediate)", "z11100100Nrrrrrrssssssnnnnnddddd"),
|
||||||
|
|
||||||
// Data processing - Immediate - Move Wide
|
// Data processing - Immediate - Move Wide
|
||||||
//INST(&V::MOVN, "MOVN", "z00100101ssiiiiiiiiiiiiiiiiddddd"),
|
//INST(&V::MOVN, "MOVN", "z00100101ssiiiiiiiiiiiiiiiiddddd"),
|
||||||
|
@ -363,14 +363,14 @@ std::vector<Matcher<V>> GetDecodeTable() {
|
||||||
//INST(&V::AUTDB, "AUTDB, AUTDZB", "110110101100000100Z111nnnnnddddd"),
|
//INST(&V::AUTDB, "AUTDB, AUTDZB", "110110101100000100Z111nnnnnddddd"),
|
||||||
|
|
||||||
// Data Processing - Register - Logical (shifted register)
|
// Data Processing - Register - Logical (shifted register)
|
||||||
//INST(&V::AND_shift, "AND (shifted register)", "z0001010ss0mmmmmiiiiiinnnnnddddd"),
|
INST(&V::AND_shift, "AND (shifted register)", "z0001010ss0mmmmmiiiiiinnnnnddddd"),
|
||||||
//INST(&V::BIC_shift, "BIC (shifted register)", "z0001010ss1mmmmmiiiiiinnnnnddddd"),
|
INST(&V::BIC_shift, "BIC (shifted register)", "z0001010ss1mmmmmiiiiiinnnnnddddd"),
|
||||||
//INST(&V::ORR_shift, "ORR (shifted register)", "z0101010ss0mmmmmiiiiiinnnnnddddd"),
|
INST(&V::ORR_shift, "ORR (shifted register)", "z0101010ss0mmmmmiiiiiinnnnnddddd"),
|
||||||
//INST(&V::ORN_shift, "ORN (shifted register)", "z0101010ss1mmmmmiiiiiinnnnnddddd"),
|
INST(&V::ORN_shift, "ORN (shifted register)", "z0101010ss1mmmmmiiiiiinnnnnddddd"),
|
||||||
//INST(&V::EOR_shift, "EOR (shifted register)", "z1001010ss0mmmmmiiiiiinnnnnddddd"),
|
INST(&V::EOR_shift, "EOR (shifted register)", "z1001010ss0mmmmmiiiiiinnnnnddddd"),
|
||||||
//INST(&V::EON, "EON (shifted register)", "z1001010ss1mmmmmiiiiiinnnnnddddd"),
|
INST(&V::EON, "EON (shifted register)", "z1001010ss1mmmmmiiiiiinnnnnddddd"),
|
||||||
//INST(&V::ANDS_shift, "ANDS (shifted register)", "z1101010ss0mmmmmiiiiiinnnnnddddd"),
|
INST(&V::ANDS_shift, "ANDS (shifted register)", "z1101010ss0mmmmmiiiiiinnnnnddddd"),
|
||||||
//INST(&V::BICS, "BICS (shifted register)", "z1101010ss1mmmmmiiiiiinnnnnddddd"),
|
INST(&V::BICS, "BICS (shifted register)", "z1101010ss1mmmmmiiiiiinnnnnddddd"),
|
||||||
|
|
||||||
// Data Processing - Register - Add/Sub (shifted register)
|
// Data Processing - Register - Add/Sub (shifted register)
|
||||||
INST(&V::ADD_shift, "ADD (shifted register)", "z0001011ss0mmmmmiiiiiinnnnnddddd"),
|
INST(&V::ADD_shift, "ADD (shifted register)", "z0001011ss0mmmmmiiiiiinnnnnddddd"),
|
||||||
|
|
216
src/frontend/A64/translate/impl/data_processing_logical.cpp
Normal file
216
src/frontend/A64/translate/impl/data_processing_logical.cpp
Normal file
|
@ -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<u8>();
|
||||||
|
|
||||||
|
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<u8>();
|
||||||
|
|
||||||
|
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<u8>();
|
||||||
|
|
||||||
|
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<u8>();
|
||||||
|
|
||||||
|
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<u8>();
|
||||||
|
|
||||||
|
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<u8>();
|
||||||
|
|
||||||
|
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<u8>();
|
||||||
|
|
||||||
|
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<u8>();
|
||||||
|
|
||||||
|
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
|
|
@ -4,6 +4,7 @@
|
||||||
* General Public License version 2 or any later version.
|
* General Public License version 2 or any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "common/bit_util.h"
|
||||||
#include "frontend/ir/terminal.h"
|
#include "frontend/ir/terminal.h"
|
||||||
#include "frontend/A64/translate/impl/impl.h"
|
#include "frontend/A64/translate/impl/impl.h"
|
||||||
|
|
||||||
|
@ -25,6 +26,29 @@ bool TranslatorVisitor::ReservedValue() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<TranslatorVisitor::BitMasks> 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<size_t>(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<u64>(S + 1);
|
||||||
|
u64 telem = Common::Ones<u64>(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) {
|
IR::U32U64 TranslatorVisitor::I(size_t bitsize, u64 value) {
|
||||||
switch (bitsize) {
|
switch (bitsize) {
|
||||||
case 32:
|
case 32:
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include "frontend/A64/imm.h"
|
#include "frontend/A64/imm.h"
|
||||||
#include "frontend/A64/ir_emitter.h"
|
#include "frontend/A64/ir_emitter.h"
|
||||||
#include "frontend/A64/location_descriptor.h"
|
#include "frontend/A64/location_descriptor.h"
|
||||||
|
@ -25,6 +27,12 @@ struct TranslatorVisitor final {
|
||||||
bool UnpredictableInstruction();
|
bool UnpredictableInstruction();
|
||||||
bool ReservedValue();
|
bool ReservedValue();
|
||||||
|
|
||||||
|
struct BitMasks {
|
||||||
|
u64 wmask, tmask;
|
||||||
|
};
|
||||||
|
|
||||||
|
boost::optional<BitMasks> DecodeBitMasks(bool N, Imm<6> immr, Imm<6> imms, bool immediate);
|
||||||
|
|
||||||
IR::U32U64 I(size_t bitsize, u64 value);
|
IR::U32U64 I(size_t bitsize, u64 value);
|
||||||
IR::U32U64 X(size_t bitsize, Reg reg);
|
IR::U32U64 X(size_t bitsize, Reg reg);
|
||||||
void X(size_t bitsize, Reg reg, IR::U32U64 value);
|
void X(size_t bitsize, Reg reg, IR::U32U64 value);
|
||||||
|
|
|
@ -221,19 +221,54 @@ U64 IREmitter::Mul(const U64& a, const U64& b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
U32 IREmitter::And(const U32& a, const U32& b) {
|
U32 IREmitter::And(const U32& a, const U32& b) {
|
||||||
return Inst<U32>(Opcode::And, a, b);
|
return Inst<U32>(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<U32>(Opcode::And32, a, b);
|
||||||
|
} else {
|
||||||
|
return Inst<U64>(Opcode::And64, a, b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
U32 IREmitter::Eor(const U32& a, const U32& b) {
|
U32 IREmitter::Eor(const U32& a, const U32& b) {
|
||||||
return Inst<U32>(Opcode::Eor, a, b);
|
return Inst<U32>(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<U32>(Opcode::Eor32, a, b);
|
||||||
|
} else {
|
||||||
|
return Inst<U64>(Opcode::Eor64, a, b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
U32 IREmitter::Or(const U32& a, const U32& b) {
|
U32 IREmitter::Or(const U32& a, const U32& b) {
|
||||||
return Inst<U32>(Opcode::Or, a, b);
|
return Inst<U32>(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<U32>(Opcode::Or32, a, b);
|
||||||
|
} else {
|
||||||
|
return Inst<U64>(Opcode::Or64, a, b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
U32 IREmitter::Not(const U32& a) {
|
U32 IREmitter::Not(const U32& a) {
|
||||||
return Inst<U32>(Opcode::Not, a);
|
return Inst<U32>(Opcode::Not32, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
U32U64 IREmitter::Not(const U32U64& a) {
|
||||||
|
if (a.GetType() == Type::U32) {
|
||||||
|
return Inst<U32>(Opcode::Not32, a);
|
||||||
|
} else {
|
||||||
|
return Inst<U64>(Opcode::Not64, a);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
U64 IREmitter::SignExtendToLong(const UAny& a) {
|
U64 IREmitter::SignExtendToLong(const UAny& a) {
|
||||||
|
|
|
@ -108,9 +108,13 @@ public:
|
||||||
U32 Mul(const U32& a, const U32& b);
|
U32 Mul(const U32& a, const U32& b);
|
||||||
U64 Mul(const U64& a, const U64& b);
|
U64 Mul(const U64& a, const U64& b);
|
||||||
U32 And(const U32& a, const U32& 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);
|
U32 Eor(const U32& a, const U32& b);
|
||||||
|
U32U64 Eor(const U32U64& a, const U32U64& b);
|
||||||
U32 Or(const U32& a, const U32& b);
|
U32 Or(const U32& a, const U32& b);
|
||||||
|
U32U64 Or(const U32U64& a, const U32U64& b);
|
||||||
U32 Not(const U32& a);
|
U32 Not(const U32& a);
|
||||||
|
U32U64 Not(const U32U64& a);
|
||||||
U32 SignExtendToWord(const UAny& a);
|
U32 SignExtendToWord(const UAny& a);
|
||||||
U64 SignExtendToLong(const UAny& a);
|
U64 SignExtendToLong(const UAny& a);
|
||||||
U32 SignExtendByteToWord(const U8& a);
|
U32 SignExtendByteToWord(const U8& a);
|
||||||
|
|
|
@ -273,6 +273,14 @@ bool Inst::MayGetNZCVFromOp() const {
|
||||||
case Opcode::Add64:
|
case Opcode::Add64:
|
||||||
case Opcode::Sub32:
|
case Opcode::Sub32:
|
||||||
case Opcode::Sub64:
|
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;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -77,10 +77,14 @@ OPCODE(Sub32, T::U32, T::U32, T::U32,
|
||||||
OPCODE(Sub64, T::U64, T::U64, T::U64, T::U1 )
|
OPCODE(Sub64, T::U64, T::U64, T::U64, T::U1 )
|
||||||
OPCODE(Mul32, T::U32, T::U32, T::U32 )
|
OPCODE(Mul32, T::U32, T::U32, T::U32 )
|
||||||
OPCODE(Mul64, T::U64, T::U64, T::U64 )
|
OPCODE(Mul64, T::U64, T::U64, T::U64 )
|
||||||
OPCODE(And, T::U32, T::U32, T::U32 )
|
OPCODE(And32, T::U32, T::U32, T::U32 )
|
||||||
OPCODE(Eor, T::U32, T::U32, T::U32 )
|
OPCODE(And64, T::U64, T::U64, T::U64 )
|
||||||
OPCODE(Or, T::U32, T::U32, T::U32 )
|
OPCODE(Eor32, T::U32, T::U32, T::U32 )
|
||||||
OPCODE(Not, 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(SignExtendByteToWord, T::U32, T::U8 )
|
||||||
OPCODE(SignExtendHalfToWord, T::U32, T::U16 )
|
OPCODE(SignExtendHalfToWord, T::U32, T::U16 )
|
||||||
OPCODE(SignExtendByteToLong, T::U64, T::U8 )
|
OPCODE(SignExtendByteToLong, T::U64, T::U8 )
|
||||||
|
|
|
@ -72,3 +72,44 @@ TEST_CASE("A64: ADD", "[a64]") {
|
||||||
REQUIRE(jit.GetRegister(2) == 2);
|
REQUIRE(jit.GetRegister(2) == 2);
|
||||||
REQUIRE(jit.GetPC() == 4);
|
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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue