A64: Implement ADD_shifted
This commit is contained in:
parent
d1eb757f93
commit
d1cef6ffb0
21 changed files with 451 additions and 75 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
code->shr(result.cvt64(), shift);
|
||||
if (shift < 64) {
|
||||
code->shr(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 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
31
src/frontend/A64/translate/impl/data_processing_addsub.cpp
Normal file
31
src/frontend/A64/translate/impl/data_processing_addsub.cpp
Normal 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
|
70
src/frontend/A64/translate/impl/impl.cpp
Normal file
70
src/frontend/A64/translate/impl/impl.cpp
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>";
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in a new issue