A64: Implement ADD_shifted

This commit is contained in:
MerryMage 2018-01-07 00:11:57 +00:00
parent d1eb757f93
commit d1cef6ffb0
21 changed files with 451 additions and 75 deletions

View file

@ -52,9 +52,14 @@ add_library(dynarmic
frontend/A32/types.cpp
frontend/A32/types.h
frontend/A64/decoder/a64.h
frontend/A64/FPCR.h
frontend/A64/imm.h
frontend/A64/ir_emitter.cpp
frontend/A64/ir_emitter.h
frontend/A64/location_descriptor.cpp
frontend/A64/location_descriptor.h
frontend/A64/translate/impl/data_processing_addsub.cpp
frontend/A64/translate/impl/impl.cpp
frontend/A64/translate/impl/impl.h
frontend/A64/translate/translate.cpp
frontend/A64/translate/translate.h

View file

@ -64,9 +64,11 @@ protected:
// Microinstruction emitters
#define OPCODE(...)
#define A32OPC(name, type, ...) void EmitA32##name(A32EmitContext& ctx, IR::Inst* inst);
#define A64OPC(...)
#include "frontend/ir/opcodes.inc"
#undef OPCODE
#undef A32OPC
#undef A64OPC
// Terminal instruction emitters
void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location) override;

View file

@ -115,11 +115,56 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) {
return block_desc;
}
void A64EmitX64::EmitA64GetW(A64EmitContext& ctx, IR::Inst* inst) {
A64::Reg reg = inst->GetArg(0).GetA64RegRef();
Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
code->mov(result, dword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)]);
ctx.reg_alloc.DefineValue(inst, result);
}
void A64EmitX64::EmitA64GetX(A64EmitContext& ctx, IR::Inst* inst) {
A64::Reg reg = inst->GetArg(0).GetA64RegRef();
Xbyak::Reg64 result = ctx.reg_alloc.ScratchGpr();
code->mov(result, qword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)]);
ctx.reg_alloc.DefineValue(inst, result);
}
void A64EmitX64::EmitA64SetW(A64EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
A64::Reg reg = inst->GetArg(0).GetA64RegRef();
auto addr = dword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)];
if (args[1].IsImmediate()) {
code->mov(addr, args[1].GetImmediateU32());
} else if (args[1].IsInXmm()) {
Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(args[1]);
code->movd(addr, to_store);
} else {
Xbyak::Reg32 to_store = ctx.reg_alloc.UseGpr(args[1]).cvt32();
code->mov(addr, to_store);
}
}
void A64EmitX64::EmitA64SetX(A64EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
A64::Reg reg = inst->GetArg(0).GetA64RegRef();
auto addr = qword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)];
if (args[1].IsImmediate()) {
code->mov(addr, args[1].GetImmediateU64());
} else if (args[1].IsInXmm()) {
Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(args[1]);
code->movq(addr, to_store);
} else {
Xbyak::Reg64 to_store = ctx.reg_alloc.UseGpr(args[1]);
code->mov(addr, to_store);
}
}
void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor) {
code->mov(code->ABI_PARAM1.cvt32(), A64::LocationDescriptor{terminal.next}.PC());
//code->mov(code->ABI_PARAM2, reinterpret_cast<u64>(jit_interface));
//code->mov(code->ABI_PARAM3, reinterpret_cast<u64>(cb.user_arg));
//code->mov(MJitStateReg(A64::Reg::PC), code->ABI_PARAM1.cvt32());
code->mov(code->ABI_PARAM1, A64::LocationDescriptor{terminal.next}.PC());
code->mov(code->ABI_PARAM2.cvt32(), 1);
code->mov(qword[r15 + offsetof(A64JitState, pc)], code->ABI_PARAM1.cvt32());
code->SwitchMxcsrOnExit();
//code->CallFunction(cb.InterpreterFallback);
code->ReturnFromRunCode(true); // TODO: Check cycles
@ -144,7 +189,7 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDesc
code->SwitchToFarCode();
code->align(16);
code->L(dest);
//code->mov(MJitStateReg(A64::Reg::PC), A64::LocationDescriptor{terminal.next}.PC());
code->mov(qword[r15 + offsetof(A64JitState, pc)], A64::LocationDescriptor{terminal.next}.PC());
PushRSBHelper(rax, rbx, terminal.next);
code->ForceReturnFromRunCode();
code->SwitchToNearCode();
@ -176,23 +221,23 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDesc
EmitTerminal(terminal.else_, initial_location);
}
void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& /*target_desc*/, CodePtr target_code_ptr) {
void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {
const CodePtr patch_location = code->getCurr();
if (target_code_ptr) {
code->jg(target_code_ptr);
} else {
//code->mov(MJitStateReg(A64::Reg::PC), A64::LocationDescriptor{target_desc}.PC());
code->mov(qword[r15 + offsetof(A64JitState, pc)], A64::LocationDescriptor{target_desc}.PC());
code->jg(code->GetReturnFromRunCodeAddress());
}
code->EnsurePatchLocationSize(patch_location, 14);
}
void A64EmitX64::EmitPatchJmp(const IR::LocationDescriptor& /*target_desc*/, CodePtr target_code_ptr) {
void A64EmitX64::EmitPatchJmp(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {
const CodePtr patch_location = code->getCurr();
if (target_code_ptr) {
code->jmp(target_code_ptr);
} else {
//code->mov(MJitStateReg(A64::Reg::PC), A64::LocationDescriptor{target_desc}.PC());
code->mov(qword[r15 + offsetof(A64JitState, pc)], A64::LocationDescriptor{target_desc}.PC());
code->jmp(code->GetReturnFromRunCodeAddress());
}
code->EnsurePatchLocationSize(patch_location, 13);

View file

@ -212,7 +212,7 @@ void EmitX64<JST>::EmitIsZero64(EmitContext& ctx, IR::Inst* inst) {
}
template <typename JST>
void EmitX64<JST>::EmitLogicalShiftLeft(EmitContext& ctx, IR::Inst* inst) {
void EmitX64<JST>::EmitLogicalShiftLeft32(EmitContext& ctx, IR::Inst* inst) {
auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@ -313,7 +313,41 @@ void EmitX64<JST>::EmitLogicalShiftLeft(EmitContext& ctx, IR::Inst* inst) {
}
template <typename JST>
void EmitX64<JST>::EmitLogicalShiftRight(EmitContext& ctx, IR::Inst* inst) {
void EmitX64<JST>::EmitLogicalShiftLeft64(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto& operand_arg = args[0];
auto& shift_arg = args[1];
if (shift_arg.IsImmediate()) {
Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg);
u8 shift = shift_arg.GetImmediateU8();
if (shift < 64) {
code->shl(result, shift);
} else {
code->xor_(result.cvt32(), result.cvt32());
}
ctx.reg_alloc.DefineValue(inst, result);
} else {
ctx.reg_alloc.Use(shift_arg, HostLoc::RCX);
Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg);
Xbyak::Reg64 zero = ctx.reg_alloc.ScratchGpr();
// The x64 SHL instruction masks the shift count by 0x1F before performing the shift.
// ARM differs from the behaviour: It does not mask the count, so shifts above 31 result in zeros.
code->shl(result, code->cl);
code->xor_(zero.cvt32(), zero.cvt32());
code->cmp(code->cl, 64);
code->cmovnb(result, zero);
ctx.reg_alloc.DefineValue(inst, result);
}
}
template <typename JST>
void EmitX64<JST>::EmitLogicalShiftRight32(EmitContext& ctx, IR::Inst* inst) {
auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@ -418,19 +452,36 @@ void EmitX64<JST>::EmitLogicalShiftRight64(EmitContext& ctx, IR::Inst* inst) {
auto& operand_arg = args[0];
auto& shift_arg = args[1];
ASSERT_MSG(shift_arg.IsImmediate(), "variable 64 bit shifts are not implemented");
ASSERT_MSG(shift_arg.GetImmediateU8() < 64, "shift width clamping is not implemented");
if (shift_arg.IsImmediate()) {
Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg);
u8 shift = shift_arg.GetImmediateU8();
Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg);
u8 shift = shift_arg.GetImmediateU8();
if (shift < 64) {
code->shr(result, shift);
} else {
code->xor_(result.cvt32(), result.cvt32());
}
code->shr(result.cvt64(), shift);
ctx.reg_alloc.DefineValue(inst, result);
} else {
ctx.reg_alloc.Use(shift_arg, HostLoc::RCX);
Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg);
Xbyak::Reg64 zero = ctx.reg_alloc.ScratchGpr();
ctx.reg_alloc.DefineValue(inst, result);
// The x64 SHR instruction masks the shift count by 0x1F before performing the shift.
// ARM differs from the behaviour: It does not mask the count, so shifts above 31 result in zeros.
code->shr(result, code->cl);
code->xor_(zero.cvt32(), zero.cvt32());
code->cmp(code->cl, 64);
code->cmovnb(result, zero);
ctx.reg_alloc.DefineValue(inst, result);
}
}
template <typename JST>
void EmitX64<JST>::EmitArithmeticShiftRight(EmitContext& ctx, IR::Inst* inst) {
void EmitX64<JST>::EmitArithmeticShiftRight32(EmitContext& ctx, IR::Inst* inst) {
auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@ -519,7 +570,39 @@ void EmitX64<JST>::EmitArithmeticShiftRight(EmitContext& ctx, IR::Inst* inst) {
}
template <typename JST>
void EmitX64<JST>::EmitRotateRight(EmitContext& ctx, IR::Inst* inst) {
void EmitX64<JST>::EmitArithmeticShiftRight64(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto& operand_arg = args[0];
auto& shift_arg = args[1];
if (shift_arg.IsImmediate()) {
u8 shift = shift_arg.GetImmediateU8();
Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg);
code->sar(result, u8(shift < 63 ? shift : 63));
ctx.reg_alloc.DefineValue(inst, result);
} else {
ctx.reg_alloc.UseScratch(shift_arg, HostLoc::RCX);
Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg);
Xbyak::Reg64 const63 = ctx.reg_alloc.ScratchGpr();
// The 64-bit x64 SAR instruction masks the shift count by 0x3F before performing the shift.
// ARM differs from the behaviour: It does not mask the count.
// We note that all shift values above 63 have the same behaviour as 63 does, so we saturate `shift` to 63.
code->mov(const63, 63);
code->movzx(code->ecx, code->cl);
code->cmp(code->ecx, u32(63));
code->cmovg(code->ecx, const63);
code->sar(result, code->cl);
ctx.reg_alloc.DefineValue(inst, result);
}
}
template <typename JST>
void EmitX64<JST>::EmitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@ -598,6 +681,30 @@ void EmitX64<JST>::EmitRotateRight(EmitContext& ctx, IR::Inst* inst) {
}
}
template <typename JST>
void EmitX64<JST>::EmitRotateRight64(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto& operand_arg = args[0];
auto& shift_arg = args[1];
if (shift_arg.IsImmediate()) {
u8 shift = shift_arg.GetImmediateU8();
Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg);
code->ror(result, u8(shift & 0x3F));
ctx.reg_alloc.DefineValue(inst, result);
} else {
ctx.reg_alloc.Use(shift_arg, HostLoc::RCX);
Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg);
// x64 ROR instruction does (shift & 0x3F) for us.
code->ror(result, code->cl);
ctx.reg_alloc.DefineValue(inst, result);
}
}
template <typename JST>
void EmitX64<JST>::EmitRotateRightExtended(EmitContext& ctx, IR::Inst* inst) {
auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);

View file

@ -48,9 +48,9 @@ bool ArmTranslatorVisitor::arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
if (ConditionPassed(cond)) {
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
auto product = ir.Mul64(n64, m64);
auto product = ir.Mul(n64, m64);
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
auto result = ir.Add64(product, addend);
auto result = ir.Add(product, addend);
auto lo = ir.LeastSignificantWord(result);
auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
@ -71,7 +71,7 @@ bool ArmTranslatorVisitor::arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
if (ConditionPassed(cond)) {
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
auto result = ir.Mul64(n64, m64);
auto result = ir.Mul(n64, m64);
auto lo = ir.LeastSignificantWord(result);
auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
@ -94,7 +94,7 @@ bool ArmTranslatorVisitor::arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n)
auto hi64 = ir.ZeroExtendWordToLong(ir.GetRegister(dHi));
auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
auto result = ir.Add64(ir.Add64(ir.Mul64(n64, m64), hi64), lo64);
auto result = ir.Add(ir.Add(ir.Mul(n64, m64), hi64), lo64);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
}
@ -110,7 +110,7 @@ bool ArmTranslatorVisitor::arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
auto result = ir.Add64(ir.Mul64(n64, m64), addend);
auto result = ir.Add(ir.Mul(n64, m64), addend);
auto lo = ir.LeastSignificantWord(result);
auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
@ -131,7 +131,7 @@ bool ArmTranslatorVisitor::arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
if (ConditionPassed(cond)) {
auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
auto result = ir.Mul64(n64, m64);
auto result = ir.Mul(n64, m64);
auto lo = ir.LeastSignificantWord(result);
auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
@ -160,7 +160,7 @@ bool ArmTranslatorVisitor::arm_SMLALxy(Cond cond, Reg dHi, Reg dLo, Reg m, bool
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
auto product = ir.SignExtendWordToLong(ir.Mul(n16, m16));
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
auto result = ir.Add64(product, addend);
auto result = ir.Add(product, addend);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
}
@ -212,7 +212,7 @@ bool ArmTranslatorVisitor::arm_SMLAWy(Cond cond, Reg d, Reg a, Reg m, bool M, Re
if (M)
m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
auto m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)));
auto product = ir.LeastSignificantWord(ir.LogicalShiftRight64(ir.Mul64(n32, m16), ir.Imm8(16)));
auto product = ir.LeastSignificantWord(ir.LogicalShiftRight(ir.Mul(n32, m16), ir.Imm8(16)));
auto result_overflow = ir.AddWithCarry(product, ir.GetRegister(a), ir.Imm1(0));
ir.SetRegister(d, result_overflow.result);
ir.OrQFlag(result_overflow.overflow);
@ -229,7 +229,7 @@ bool ArmTranslatorVisitor::arm_SMULWy(Cond cond, Reg d, Reg m, bool M, Reg n) {
if (M)
m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
auto m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)));
auto result = ir.LogicalShiftRight64(ir.Mul64(n32, m16), ir.Imm8(16));
auto result = ir.LogicalShiftRight(ir.Mul(n32, m16), ir.Imm8(16));
ir.SetRegister(d, ir.LeastSignificantWord(result));
}
return true;
@ -244,7 +244,7 @@ bool ArmTranslatorVisitor::arm_SMMLA(Cond cond, Reg d, Reg a, Reg m, bool R, Reg
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a));
auto temp = ir.Add64(a64, ir.Mul64(n64, m64));
auto temp = ir.Add(a64, ir.Mul(n64, m64));
auto result_carry = ir.MostSignificantWord(temp);
auto result = result_carry.result;
if (R)
@ -261,7 +261,7 @@ bool ArmTranslatorVisitor::arm_SMMLS(Cond cond, Reg d, Reg a, Reg m, bool R, Reg
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a));
auto temp = ir.Sub64(a64, ir.Mul64(n64, m64));
auto temp = ir.Sub(a64, ir.Mul(n64, m64));
auto result_carry = ir.MostSignificantWord(temp);
auto result = result_carry.result;
if (R)
@ -277,7 +277,7 @@ bool ArmTranslatorVisitor::arm_SMMUL(Cond cond, Reg d, Reg m, bool R, Reg n) {
if (ConditionPassed(cond)) {
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
auto product = ir.Mul64(n64, m64);
auto product = ir.Mul(n64, m64);
auto result_carry = ir.MostSignificantWord(product);
auto result = result_carry.result;
if (R)
@ -332,7 +332,7 @@ bool ArmTranslatorVisitor::arm_SMLALD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M
auto product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo));
auto product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi));
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
auto result = ir.Add64(ir.Add64(product_lo, product_hi), addend);
auto result = ir.Add(ir.Add(product_lo, product_hi), addend);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
}
@ -380,7 +380,7 @@ bool ArmTranslatorVisitor::arm_SMLSLD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M
auto product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo));
auto product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi));
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
auto result = ir.Add64(ir.Sub64(product_lo, product_hi), addend);
auto result = ir.Add(ir.Sub(product_lo, product_hi), addend);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
}

View file

@ -373,7 +373,7 @@ std::vector<Matcher<V>> GetDecodeTable() {
//INST(&V::BICS, "BICS (shifted register)", "z1101010ss1mmmmmiiiiiinnnnnddddd"),
// Data Processing - Register - Add/Sub (shifted register)
//INST(&V::ADD_shift, "ADD (shifted register)", "z0001011ss0mmmmmiiiiiinnnnnddddd"),
INST(&V::ADD_shift, "ADD (shifted register)", "z0001011ss0mmmmmiiiiiinnnnnddddd"),
//INST(&V::ADDS_shift, "ADDS (shifted register)", "z0101011ss0mmmmmiiiiiinnnnnddddd"),
//INST(&V::SUB_shift, "SUB (shifted register)", "z1001011ss0mmmmmiiiiiinnnnnddddd"),
//INST(&V::SUBS_shift, "SUBS (shifted register)", "z1101011ss0mmmmmiiiiiinnnnnddddd"),

View file

@ -28,13 +28,13 @@ public:
template <typename T = u32>
T ZeroExtend() const {
static_assert(Common::BitSize<T>() <= bit_size);
static_assert(Common::BitSize<T>() >= bit_size);
return value;
}
template <typename T = s32>
T SignExtend() const {
static_assert(Common::BitSize<T>() <= bit_size);
static_assert(Common::BitSize<T>() >= bit_size);
return static_cast<T>(Common::SignExtend<bit_size>(value));
}

View file

@ -0,0 +1,31 @@
/* 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::ADD_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
size_t datasize = sf ? 64 : 32;
if (shift == 0b11) return ReservedValue();
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.Add(operand1, operand2);
X(datasize, Rd, result);
return true;
}
} // namespace A64
} // namespace Dynarmic

View file

@ -0,0 +1,70 @@
/* 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/ir/terminal.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic {
namespace A64 {
bool TranslatorVisitor::InterpretThisInstruction() {
ir.SetTerm(IR::Term::Interpret(ir.current_location));
return false;
}
bool TranslatorVisitor::UnpredictableInstruction() {
ASSERT_MSG(false, "UNPREDICTABLE");
return false;
}
bool TranslatorVisitor::ReservedValue() {
ASSERT_MSG(false, "RESERVEDVALUE");
return false;
}
IR::U32U64 TranslatorVisitor::X(size_t bitsize, Reg reg) {
switch (bitsize) {
case 32:
return ir.GetW(reg);
case 64:
return ir.GetX(reg);
default:
ASSERT_MSG(false, "X - get: Invalid bitsize");
return {};
}
}
void TranslatorVisitor::X(size_t bitsize, Reg reg, IR::U32U64 value) {
switch (bitsize) {
case 32:
ir.SetW(reg, value);
return;
case 64:
ir.SetX(reg, value);
return;
default:
ASSERT_MSG(false, "X - set: Invalid bitsize");
}
}
IR::U32U64 TranslatorVisitor::ShiftReg(size_t bitsize, Reg reg, Imm<2> shift, IR::U8 amount) {
auto result = X(bitsize, reg);
switch (shift.ZeroExtend()) {
case 0b00:
return ir.LogicalShiftLeft(result, amount);
case 0b01:
return ir.LogicalShiftRight(result, amount);
case 0b10:
return ir.ArithmeticShiftRight(result, amount);
case 0b11:
return ir.RotateRight(result, amount);
}
ASSERT_MSG(false, "Unreachable");
return {};
}
} // namespace A64
} // namespace Dynarmic

View file

@ -23,6 +23,12 @@ struct TranslatorVisitor final {
bool InterpretThisInstruction();
bool UnpredictableInstruction();
bool ReservedValue();
IR::U32U64 X(size_t bitsize, Reg reg);
void X(size_t bitsize, Reg reg, IR::U32U64 value);
IR::U32U64 ShiftReg(size_t bitsize, Reg reg, Imm<2> shift, IR::U8 amount);
// Data processing - Immediate - PC relative addressing
bool ADR(Imm<2> immlo, Imm<19> immhi, Reg Rd);
@ -1040,4 +1046,4 @@ struct TranslatorVisitor final {
};
} // namespace A64
} // namespace Dynarmic
} // namespace Dynarmic

View file

@ -4,6 +4,7 @@
* General Public License version 2 or any later version.
*/
#include "frontend/A64/decoder/a64.h"
#include "frontend/A64/location_descriptor.h"
#include "frontend/A64/translate/impl/impl.h"
#include "frontend/A64/translate/translate.h"
@ -12,9 +13,29 @@
namespace Dynarmic {
namespace A64 {
IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType /*memory_read_code*/) {
// TODO
return IR::Block{descriptor};
IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code) {
TranslatorVisitor visitor{descriptor};
bool should_continue = true;
while (should_continue) {
const u64 pc = visitor.ir.current_location.PC();
const u32 instruction = memory_read_code(pc);
if (auto decoder = Decode<TranslatorVisitor>(instruction)) {
should_continue = decoder->call(visitor, instruction);
} else {
should_continue = visitor.InterpretThisInstruction();
}
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(4);
visitor.ir.block.CycleCount()++;
}
ASSERT_MSG(visitor.ir.block.HasTerminal(), "Terminal has not been set");
visitor.ir.block.SetEndLocation(visitor.ir.current_location);
return std::move(visitor.ir.block);
}
} // namespace A64

View file

@ -14,6 +14,7 @@
#include "common/assert.h"
#include "frontend/A32/types.h"
#include "frontend/A64/types.h"
#include "frontend/ir/basic_block.h"
#include "frontend/ir/opcodes.h"
@ -22,7 +23,7 @@ namespace IR {
void Block::AppendNewInst(Opcode opcode, std::initializer_list<IR::Value> args) {
IR::Inst* inst = new(instruction_alloc_pool->Alloc()) IR::Inst(opcode);
DEBUG_ASSERT(args.size() == inst->NumArgs());
ASSERT(args.size() == inst->NumArgs());
std::for_each(args.begin(), args.end(), [&inst, index = size_t(0)](const auto& arg) mutable {
inst->SetArg(index, arg);
@ -165,6 +166,10 @@ std::string DumpBlock(const IR::Block& block) {
return A32::RegToString(arg.GetA32RegRef());
case Type::A32ExtReg:
return A32::ExtRegToString(arg.GetA32ExtRegRef());
case Type::A64Reg:
return A64::RegToString(arg.GetA64RegRef());
case Type::A64Vec:
return A64::VecToString(arg.GetA64VecRef());
default:
return "<unknown immediate type>";
}

View file

@ -70,29 +70,25 @@ U1 IREmitter::IsZero64(const U64& value) {
}
ResultAndCarry<U32> IREmitter::LogicalShiftLeft(const U32& value_in, const U8& shift_amount, const U1& carry_in) {
auto result = Inst<U32>(Opcode::LogicalShiftLeft, value_in, shift_amount, carry_in);
auto result = Inst<U32>(Opcode::LogicalShiftLeft32, value_in, shift_amount, carry_in);
auto carry_out = Inst<U1>(Opcode::GetCarryFromOp, result);
return {result, carry_out};
}
ResultAndCarry<U32> IREmitter::LogicalShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) {
auto result = Inst<U32>(Opcode::LogicalShiftRight, value_in, shift_amount, carry_in);
auto result = Inst<U32>(Opcode::LogicalShiftRight32, value_in, shift_amount, carry_in);
auto carry_out = Inst<U1>(Opcode::GetCarryFromOp, result);
return {result, carry_out};
}
U64 IREmitter::LogicalShiftRight64(const U64& value_in, const U8& shift_amount) {
return Inst<U64>(Opcode::LogicalShiftRight64, value_in, shift_amount);
}
ResultAndCarry<U32> IREmitter::ArithmeticShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) {
auto result = Inst<U32>(Opcode::ArithmeticShiftRight, value_in, shift_amount, carry_in);
auto result = Inst<U32>(Opcode::ArithmeticShiftRight32, value_in, shift_amount, carry_in);
auto carry_out = Inst<U1>(Opcode::GetCarryFromOp, result);
return {result, carry_out};
}
ResultAndCarry<U32> IREmitter::RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) {
auto result = Inst<U32>(Opcode::RotateRight, value_in, shift_amount, carry_in);
auto result = Inst<U32>(Opcode::RotateRight32, value_in, shift_amount, carry_in);
auto carry_out = Inst<U1>(Opcode::GetCarryFromOp, result);
return {result, carry_out};
}
@ -103,6 +99,42 @@ ResultAndCarry<U32> IREmitter::RotateRightExtended(const U32& value_in, const U1
return {result, carry_out};
}
U64 IREmitter::LogicalShiftRight(const U64& value_in, const U8& shift_amount) {
return Inst<U64>(Opcode::LogicalShiftRight64, value_in, shift_amount);
}
U32U64 IREmitter::LogicalShiftLeft(const U32U64& value_in, const U8& shift_amount) {
if (value_in.GetType() == Type::U32) {
return Inst<U32>(Opcode::LogicalShiftLeft32, value_in, shift_amount, Imm1(0));
} else {
return Inst<U64>(Opcode::LogicalShiftLeft64, value_in, shift_amount);
}
}
U32U64 IREmitter::LogicalShiftRight(const U32U64& value_in, const U8& shift_amount) {
if (value_in.GetType() == Type::U32) {
return Inst<U32>(Opcode::LogicalShiftRight32, value_in, shift_amount, Imm1(0));
} else {
return Inst<U64>(Opcode::LogicalShiftRight64, value_in, shift_amount);
}
}
U32U64 IREmitter::ArithmeticShiftRight(const U32U64& value_in, const U8& shift_amount) {
if (value_in.GetType() == Type::U32) {
return Inst<U32>(Opcode::ArithmeticShiftRight32, value_in, shift_amount, Imm1(0));
} else {
return Inst<U64>(Opcode::ArithmeticShiftRight64, value_in, shift_amount);
}
}
U32U64 IREmitter::RotateRight(const U32U64& value_in, const U8& shift_amount) {
if (value_in.GetType() == Type::U32) {
return Inst<U32>(Opcode::RotateRight32, value_in, shift_amount, Imm1(0));
} else {
return Inst<U64>(Opcode::RotateRight64, value_in, shift_amount);
}
}
ResultAndCarryAndOverflow<U32> IREmitter::AddWithCarry(const Value& a, const Value& b, const U1& carry_in) {
auto result = Inst<U32>(Opcode::AddWithCarry, a, b, carry_in);
auto carry_out = Inst<U1>(Opcode::GetCarryFromOp, result);
@ -114,10 +146,19 @@ U32 IREmitter::Add(const U32& a, const U32& b) {
return Inst<U32>(Opcode::AddWithCarry, a, b, Imm1(0));
}
U64 IREmitter::Add64(const U64& a, const U64& b) {
U64 IREmitter::Add(const U64& a, const U64& b) {
return Inst<U64>(Opcode::Add64, a, b);
}
U32U64 IREmitter::Add(const U32U64& a, const U32U64& b) {
ASSERT(a.GetType() == b.GetType());
if (a.GetType() == Type::U32) {
return Inst<U32>(Opcode::AddWithCarry, a, b, Imm1(0));
} else {
return Inst<U64>(Opcode::Add64, a, b);
}
}
ResultAndCarryAndOverflow<U32> IREmitter::SubWithCarry(const U32& a, const U32& b, const U1& carry_in) {
// This is equivalent to AddWithCarry(a, Not(b), carry_in).
auto result = Inst<U32>(Opcode::SubWithCarry, a, b, carry_in);
@ -130,7 +171,7 @@ U32 IREmitter::Sub(const U32& a, const U32& b) {
return Inst<U32>(Opcode::SubWithCarry, a, b, Imm1(1));
}
U64 IREmitter::Sub64(const U64& a, const U64& b) {
U64 IREmitter::Sub(const U64& a, const U64& b) {
return Inst<U64>(Opcode::Sub64, a, b);
}
@ -138,7 +179,7 @@ U32 IREmitter::Mul(const U32& a, const U32& b) {
return Inst<U32>(Opcode::Mul, a, b);
}
U64 IREmitter::Mul64(const U64& a, const U64& b) {
U64 IREmitter::Mul(const U64& a, const U64& b) {
return Inst<U64>(Opcode::Mul64, a, b);
}

View file

@ -84,18 +84,23 @@ public:
ResultAndCarry<U32> LogicalShiftLeft(const U32& value_in, const U8& shift_amount, const U1& carry_in);
ResultAndCarry<U32> LogicalShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in);
U64 LogicalShiftRight64(const U64& value_in, const U8& shift_amount);
ResultAndCarry<U32> ArithmeticShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in);
ResultAndCarry<U32> RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in);
U64 LogicalShiftRight(const U64& value_in, const U8& shift_amount);
U32U64 LogicalShiftLeft(const U32U64& value_in, const U8& shift_amount);
U32U64 LogicalShiftRight(const U32U64& value_in, const U8& shift_amount);
U32U64 ArithmeticShiftRight(const U32U64& value_in, const U8& shift_amount);
U32U64 RotateRight(const U32U64& value_in, const U8& shift_amount);
ResultAndCarry<U32> RotateRightExtended(const U32& value_in, const U1& carry_in);
ResultAndCarryAndOverflow<U32> AddWithCarry(const Value& a, const Value& b, const U1& carry_in);
U32 Add(const U32& a, const U32& b);
U64 Add64(const U64& a, const U64& b);
U64 Add(const U64& a, const U64& b);
U32U64 Add(const U32U64& a, const U32U64& b);
ResultAndCarryAndOverflow<U32> SubWithCarry(const U32& a, const U32& b, const U1& carry_in);
U32 Sub(const U32& a, const U32& b);
U64 Sub64(const U64& a, const U64& b);
U64 Sub(const U64& a, const U64& b);
U32 Mul(const U32& a, const U32& b);
U64 Mul64(const U64& a, const U64& b);
U64 Mul(const U64& a, const U64& b);
U32 And(const U32& a, const U32& b);
U32 Eor(const U32& a, const U32& b);
U32 Or(const U32& a, const U32& b);

View file

@ -13,18 +13,21 @@ namespace Dynarmic {
namespace IR {
bool Inst::IsArithmeticShift() const {
return op == Opcode::ArithmeticShiftRight;
return op == Opcode::ArithmeticShiftRight32 ||
op == Opcode::ArithmeticShiftRight64;
}
bool Inst::IsCircularShift() const {
return op == Opcode::RotateRight ||
return op == Opcode::RotateRight32 ||
op == Opcode::RotateRight64 ||
op == Opcode::RotateRightExtended;
}
bool Inst::IsLogicalShift() const {
switch (op) {
case Opcode::LogicalShiftLeft:
case Opcode::LogicalShiftRight:
case Opcode::LogicalShiftLeft32:
case Opcode::LogicalShiftLeft64:
case Opcode::LogicalShiftRight32:
case Opcode::LogicalShiftRight64:
return true;
@ -133,6 +136,8 @@ bool Inst::ReadsFromCoreRegister() const {
case Opcode::A32GetRegister:
case Opcode::A32GetExtendedRegister32:
case Opcode::A32GetExtendedRegister64:
case Opcode::A64GetW:
case Opcode::A64GetX:
return true;
default:
@ -146,6 +151,8 @@ bool Inst::WritesToCoreRegister() const {
case Opcode::A32SetExtendedRegister32:
case Opcode::A32SetExtendedRegister64:
case Opcode::A32BXWritePC:
case Opcode::A64SetW:
case Opcode::A64SetX:
return true;
default:

View file

@ -28,9 +28,11 @@ struct Meta {
static const std::map<Opcode, Meta> opcode_info {{
#define OPCODE(name, type, ...) { Opcode::name, { #name, type, { __VA_ARGS__ } } },
#define A32OPC(name, type, ...) { Opcode::A32##name, { #name, type, { __VA_ARGS__ } } },
#define A64OPC(name, type, ...) { Opcode::A64##name, { #name, type, { __VA_ARGS__ } } },
#include "opcodes.inc"
#undef OPCODE
#undef A32OPC
#undef A64OPC
}};
} // namespace OpcodeInfo

View file

@ -18,9 +18,11 @@ namespace IR {
enum class Opcode {
#define OPCODE(name, type, ...) name,
#define A32OPC(name, type, ...) A32##name,
#define A64OPC(name, type, ...) A64##name,
#include "opcodes.inc"
#undef OPCODE
#undef A32OPC
#undef A64OPC
NUM_OPCODE
};

View file

@ -34,6 +34,12 @@ A32OPC(SetFpscr, T::Void, T::U32,
A32OPC(GetFpscrNZCV, T::U32, )
A32OPC(SetFpscrNZCV, T::Void, T::U32, )
// A64 Context getters/setters
A64OPC(GetW, T::U32, T::A64Reg )
A64OPC(GetX, T::U64, T::A64Reg )
A64OPC(SetW, T::Void, T::A64Reg, T::U32 )
A64OPC(SetX, T::Void, T::A64Reg, T::U64 )
// Hints
OPCODE(PushRSB, T::Void, T::U64 )
@ -51,11 +57,14 @@ OPCODE(LeastSignificantByte, T::U8, T::U32
OPCODE(MostSignificantBit, T::U1, T::U32 )
OPCODE(IsZero, T::U1, T::U32 )
OPCODE(IsZero64, T::U1, T::U64 )
OPCODE(LogicalShiftLeft, T::U32, T::U32, T::U8, T::U1 )
OPCODE(LogicalShiftRight, T::U32, T::U32, T::U8, T::U1 )
OPCODE(LogicalShiftLeft32, T::U32, T::U32, T::U8, T::U1 )
OPCODE(LogicalShiftLeft64, T::U64, T::U64, T::U8 )
OPCODE(LogicalShiftRight32, T::U32, T::U32, T::U8, T::U1 )
OPCODE(LogicalShiftRight64, T::U64, T::U64, T::U8 )
OPCODE(ArithmeticShiftRight, T::U32, T::U32, T::U8, T::U1 )
OPCODE(RotateRight, T::U32, T::U32, T::U8, T::U1 )
OPCODE(ArithmeticShiftRight32, T::U32, T::U32, T::U8, T::U1 )
OPCODE(ArithmeticShiftRight64, T::U64, T::U64, T::U8 )
OPCODE(RotateRight32, T::U32, T::U32, T::U8, T::U1 )
OPCODE(RotateRight64, T::U64, T::U64, T::U8 )
OPCODE(RotateRightExtended, T::U32, T::U32, T::U1 )
OPCODE(AddWithCarry, T::U32, T::U32, T::U32, T::U1 )
OPCODE(SubWithCarry, T::U32, T::U32, T::U32, T::U1 )

View file

@ -6,6 +6,8 @@
#pragma once
#include <type_traits>
#include "common/assert.h"
#include "common/common_types.h"
#include "frontend/A32/types.h"
@ -76,9 +78,8 @@ class TypedValue final : public Value {
public:
TypedValue() : Value() {}
template <Type other_type>
template <Type other_type, typename = std::enable_if_t<(other_type & type_) != Type::Void>>
/* implicit */ TypedValue(const TypedValue<other_type>& value) : Value(value) {
static_assert((other_type & type_) != Type::Void);
ASSERT((value.GetType() & type_) != Type::Void);
}
@ -92,6 +93,7 @@ using U8 = TypedValue<Type::U8>;
using U16 = TypedValue<Type::U16>;
using U32 = TypedValue<Type::U32>;
using U64 = TypedValue<Type::U64>;
using U32U64 = TypedValue<Type::U32 | Type::U64>;
using F32 = TypedValue<Type::F32>;
using F64 = TypedValue<Type::F64>;
using F128 = TypedValue<Type::F128>;

View file

@ -23,10 +23,10 @@ void ConstantPropagation(IR::Block& block, const A32::UserCallbacks::Memory& mem
}
break;
}
case IR::Opcode::LogicalShiftLeft:
case IR::Opcode::LogicalShiftRight:
case IR::Opcode::ArithmeticShiftRight:
case IR::Opcode::RotateRight: {
case IR::Opcode::LogicalShiftLeft32:
case IR::Opcode::LogicalShiftRight32:
case IR::Opcode::ArithmeticShiftRight32:
case IR::Opcode::RotateRight32: {
if (!inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)) {
inst.SetArg(2, IR::Value(false));
}

View file

@ -1,5 +1,5 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* 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.
*/
@ -14,6 +14,7 @@
#include "common/common_types.h"
class TestEnv final : public Dynarmic::A64::UserCallbacks {
public:
u64 ticks_left = 0;
std::array<u32, 3000> code_mem{};
@ -22,7 +23,7 @@ class TestEnv final : public Dynarmic::A64::UserCallbacks {
size_t index = vaddr / sizeof(u32);
return code_mem[index];
}
ASSERT_MSG(false, "MemoryReadCode(%llx)", vaddr);
return 0x14000000; // B .
}
std::uint8_t MemoryRead8(u64 vaddr) override { ASSERT_MSG(false, "MemoryRead8(%llx)", vaddr); }
@ -49,10 +50,25 @@ class TestEnv final : public Dynarmic::A64::UserCallbacks {
std::uint64_t GetTicksRemaining() override {
return ticks_left;
}
};
TEST_CASE("A64", "[a64]") {
TEST_CASE("A64: ADD", "[a64]") {
TestEnv env;
Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}};
}
env.code_mem[0] = 0x8b020020; // ADD X0, X1, X2
env.code_mem[1] = 0x14000000; // B .
jit.SetRegister(0, 0);
jit.SetRegister(1, 1);
jit.SetRegister(2, 2);
jit.SetPC(0);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetRegister(0) == 3);
REQUIRE(jit.GetRegister(1) == 1);
REQUIRE(jit.GetRegister(2) == 2);
REQUIRE(jit.GetPC() == 4);
}