A64: Implement compare and branch
This commit is contained in:
parent
e8bcf72ee5
commit
cb481a3a48
20 changed files with 249 additions and 21 deletions
|
@ -1161,6 +1161,10 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor
|
||||||
EmitTerminal(terminal.then_, initial_location);
|
EmitTerminal(terminal.then_, initial_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void A32EmitX64::EmitTerminalImpl(IR::Term::CheckBit, IR::LocationDescriptor) {
|
||||||
|
ASSERT_MSG(false, "Term::CheckBit should never be emitted by the A32 frontend");
|
||||||
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) {
|
void A32EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) {
|
||||||
code->cmp(code->byte[r15 + offsetof(A32JitState, halt_requested)], u8(0));
|
code->cmp(code->byte[r15 + offsetof(A32JitState, halt_requested)], u8(0));
|
||||||
code->jne(code->GetForceReturnFromRunCodeAddress());
|
code->jne(code->GetForceReturnFromRunCodeAddress());
|
||||||
|
|
|
@ -77,6 +77,7 @@ protected:
|
||||||
void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) override;
|
void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) override;
|
||||||
void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) override;
|
void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) override;
|
||||||
void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) override;
|
void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) override;
|
||||||
|
void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) override;
|
||||||
void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) override;
|
void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) override;
|
||||||
|
|
||||||
// Patching
|
// Patching
|
||||||
|
|
|
@ -115,6 +115,12 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) {
|
||||||
return block_desc;
|
return block_desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void A64EmitX64::EmitA64SetCheckBit(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Reg8 to_store = ctx.reg_alloc.UseGpr(args[0]).cvt8();
|
||||||
|
code->mov(code->byte[r15 + offsetof(A64JitState, check_bit)], to_store);
|
||||||
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64GetCFlag(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64GetCFlag(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
code->mov(result, dword[r15 + offsetof(A64JitState, CPSR_nzcv)]);
|
code->mov(result, dword[r15 + offsetof(A64JitState, CPSR_nzcv)]);
|
||||||
|
@ -267,6 +273,15 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor
|
||||||
EmitTerminal(terminal.then_, initial_location);
|
EmitTerminal(terminal.then_, initial_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void A64EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) {
|
||||||
|
Xbyak::Label fail;
|
||||||
|
code->cmp(code->byte[r15 + offsetof(A64JitState, check_bit)], u8(0));
|
||||||
|
code->jz(fail);
|
||||||
|
EmitTerminal(terminal.then_, initial_location);
|
||||||
|
code->L(fail);
|
||||||
|
EmitTerminal(terminal.else_, initial_location);
|
||||||
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) {
|
void A64EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) {
|
||||||
code->cmp(code->byte[r15 + offsetof(A64JitState, halt_requested)], u8(0));
|
code->cmp(code->byte[r15 + offsetof(A64JitState, halt_requested)], u8(0));
|
||||||
code->jne(code->GetForceReturnFromRunCodeAddress());
|
code->jne(code->GetForceReturnFromRunCodeAddress());
|
||||||
|
|
|
@ -66,6 +66,7 @@ protected:
|
||||||
void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) override;
|
void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) override;
|
||||||
void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) override;
|
void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) override;
|
||||||
void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) override;
|
void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) override;
|
||||||
|
void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) override;
|
||||||
void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) override;
|
void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) override;
|
||||||
|
|
||||||
// Patching
|
// Patching
|
||||||
|
|
|
@ -55,6 +55,7 @@ struct A64JitState {
|
||||||
s64 cycles_to_run = 0;
|
s64 cycles_to_run = 0;
|
||||||
s64 cycles_remaining = 0;
|
s64 cycles_remaining = 0;
|
||||||
bool halt_requested = false;
|
bool halt_requested = false;
|
||||||
|
bool check_bit = false;
|
||||||
|
|
||||||
static constexpr size_t RSBSize = 8; // MUST be a power of 2.
|
static constexpr size_t RSBSize = 8; // MUST be a power of 2.
|
||||||
static constexpr size_t RSBPtrMask = RSBSize - 1;
|
static constexpr size_t RSBPtrMask = RSBSize - 1;
|
||||||
|
|
|
@ -218,7 +218,7 @@ void EmitX64<JST>::EmitMostSignificantBit(EmitContext& ctx, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename JST>
|
template <typename JST>
|
||||||
void EmitX64<JST>::EmitIsZero(EmitContext& ctx, IR::Inst* inst) {
|
void EmitX64<JST>::EmitIsZero32(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();
|
||||||
// TODO: Flag optimization
|
// TODO: Flag optimization
|
||||||
|
@ -239,6 +239,17 @@ void EmitX64<JST>::EmitIsZero64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitTestBit(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(args[0]);
|
||||||
|
ASSERT(args[1].IsImmediate());
|
||||||
|
// TODO: Flag optimization
|
||||||
|
code->bt(result, args[1].GetImmediateU8());
|
||||||
|
code->setc(result.cvt8());
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename JST>
|
template <typename JST>
|
||||||
void EmitX64<JST>::EmitLogicalShiftLeft32(EmitContext& ctx, IR::Inst* inst) {
|
void EmitX64<JST>::EmitLogicalShiftLeft32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
|
auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
|
||||||
|
|
|
@ -92,6 +92,7 @@ protected:
|
||||||
virtual void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) = 0;
|
virtual void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) = 0;
|
||||||
virtual void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) = 0;
|
virtual void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) = 0;
|
||||||
virtual void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) = 0;
|
virtual void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) = 0;
|
||||||
|
virtual void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) = 0;
|
||||||
virtual void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) = 0;
|
virtual void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) = 0;
|
||||||
|
|
||||||
// Patching
|
// Patching
|
||||||
|
|
|
@ -25,7 +25,7 @@ constexpr size_t BitSize() {
|
||||||
/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
|
/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
|
||||||
template<size_t begin_bit, size_t end_bit, typename T>
|
template<size_t begin_bit, size_t end_bit, typename T>
|
||||||
constexpr T Bits(const T value) {
|
constexpr T Bits(const T value) {
|
||||||
static_assert(begin_bit < end_bit,
|
static_assert(begin_bit <= end_bit,
|
||||||
"invalid bit range (position of beginning bit cannot be greater than that of end bit)");
|
"invalid bit range (position of beginning bit cannot be greater than that of end bit)");
|
||||||
static_assert(begin_bit < BitSize<T>(), "begin_bit must be smaller than size of T");
|
static_assert(begin_bit < BitSize<T>(), "begin_bit must be smaller than size of T");
|
||||||
static_assert(end_bit < BitSize<T>(), "begin_bit must be smaller than size of T");
|
static_assert(end_bit < BitSize<T>(), "begin_bit must be smaller than size of T");
|
||||||
|
|
|
@ -57,7 +57,7 @@ bool ArmTranslatorVisitor::arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
|
||||||
ir.SetRegister(dHi, hi);
|
ir.SetRegister(dHi, hi);
|
||||||
if (S) {
|
if (S) {
|
||||||
ir.SetNFlag(ir.MostSignificantBit(hi));
|
ir.SetNFlag(ir.MostSignificantBit(hi));
|
||||||
ir.SetZFlag(ir.IsZero64(result));
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -78,7 +78,7 @@ bool ArmTranslatorVisitor::arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
|
||||||
ir.SetRegister(dHi, hi);
|
ir.SetRegister(dHi, hi);
|
||||||
if (S) {
|
if (S) {
|
||||||
ir.SetNFlag(ir.MostSignificantBit(hi));
|
ir.SetNFlag(ir.MostSignificantBit(hi));
|
||||||
ir.SetZFlag(ir.IsZero64(result));
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -117,7 +117,7 @@ bool ArmTranslatorVisitor::arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
|
||||||
ir.SetRegister(dHi, hi);
|
ir.SetRegister(dHi, hi);
|
||||||
if (S) {
|
if (S) {
|
||||||
ir.SetNFlag(ir.MostSignificantBit(hi));
|
ir.SetNFlag(ir.MostSignificantBit(hi));
|
||||||
ir.SetZFlag(ir.IsZero64(result));
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -138,7 +138,7 @@ bool ArmTranslatorVisitor::arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
|
||||||
ir.SetRegister(dHi, hi);
|
ir.SetRegister(dHi, hi);
|
||||||
if (S) {
|
if (S) {
|
||||||
ir.SetNFlag(ir.MostSignificantBit(hi));
|
ir.SetNFlag(ir.MostSignificantBit(hi));
|
||||||
ir.SetZFlag(ir.IsZero64(result));
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -117,10 +117,10 @@ std::vector<Matcher<V>> GetDecodeTable() {
|
||||||
INST(&V::BL, "BL", "100101iiiiiiiiiiiiiiiiiiiiiiiiii"),
|
INST(&V::BL, "BL", "100101iiiiiiiiiiiiiiiiiiiiiiiiii"),
|
||||||
|
|
||||||
// Compare and branch (immediate)
|
// Compare and branch (immediate)
|
||||||
//INST(&V::CBZ, "CBZ", "z0110100iiiiiiiiiiiiiiiiiiittttt"),
|
INST(&V::CBZ, "CBZ", "z0110100iiiiiiiiiiiiiiiiiiittttt"),
|
||||||
//INST(&V::CBNZ, "CBNZ", "z0110101iiiiiiiiiiiiiiiiiiittttt"),
|
INST(&V::CBNZ, "CBNZ", "z0110101iiiiiiiiiiiiiiiiiiittttt"),
|
||||||
//INST(&V::TBZ, "TBZ", "b0110110bbbbbiiiiiiiiiiiiiittttt"),
|
INST(&V::TBZ, "TBZ", "b0110110bbbbbiiiiiiiiiiiiiittttt"),
|
||||||
//INST(&V::TBNZ, "TBNZ", "b0110111bbbbbiiiiiiiiiiiiiittttt"),
|
INST(&V::TBNZ, "TBNZ", "b0110111bbbbbiiiiiiiiiiiiiittttt"),
|
||||||
|
|
||||||
// Loads and stores - Advanced SIMD Load/Store multiple structures
|
// Loads and stores - Advanced SIMD Load/Store multiple structures
|
||||||
//INST(&V::ST4_mult_1, "ST4 (multiple structures)", "0Q001100000000000000zznnnnnttttt"),
|
//INST(&V::ST4_mult_1, "ST4 (multiple structures)", "0Q001100000000000000zznnnnnttttt"),
|
||||||
|
|
|
@ -22,6 +22,10 @@ u64 IREmitter::AlignPC(size_t alignment) {
|
||||||
return static_cast<u64>(pc - pc % alignment);
|
return static_cast<u64>(pc - pc % alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IREmitter::SetCheckBit(const IR::U1& value) {
|
||||||
|
Inst(Opcode::A64SetCheckBit, value);
|
||||||
|
}
|
||||||
|
|
||||||
IR::U1 IREmitter::GetCFlag() {
|
IR::U1 IREmitter::GetCFlag() {
|
||||||
return Inst<IR::U1>(Opcode::A64GetCFlag);
|
return Inst<IR::U1>(Opcode::A64GetCFlag);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ public:
|
||||||
u64 PC();
|
u64 PC();
|
||||||
u64 AlignPC(size_t alignment);
|
u64 AlignPC(size_t alignment);
|
||||||
|
|
||||||
|
void SetCheckBit(const IR::U1& value);
|
||||||
IR::U1 GetCFlag();
|
IR::U1 GetCFlag();
|
||||||
void SetNZCV(const IR::NZCV& nzcv);
|
void SetNZCV(const IR::NZCV& nzcv);
|
||||||
|
|
||||||
|
|
|
@ -62,5 +62,67 @@ bool TranslatorVisitor::RET(Reg Rn) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TranslatorVisitor::CBZ(bool sf, Imm<19> imm19, Reg Rt) {
|
||||||
|
size_t datasize = sf ? 64 : 32;
|
||||||
|
s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
|
||||||
|
|
||||||
|
auto operand1 = X(datasize, Rt);
|
||||||
|
|
||||||
|
ir.SetCheckBit(ir.IsZero(operand1));
|
||||||
|
|
||||||
|
u64 target = ir.PC() + offset;
|
||||||
|
auto cond_pass = IR::Term::LinkBlock{ir.current_location.SetPC(target)};
|
||||||
|
auto cond_fail = IR::Term::LinkBlock{ir.current_location.AdvancePC(4)};
|
||||||
|
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TranslatorVisitor::CBNZ(bool sf, Imm<19> imm19, Reg Rt) {
|
||||||
|
size_t datasize = sf ? 64 : 32;
|
||||||
|
s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
|
||||||
|
|
||||||
|
auto operand1 = X(datasize, Rt);
|
||||||
|
|
||||||
|
ir.SetCheckBit(ir.IsZero(operand1));
|
||||||
|
|
||||||
|
u64 target = ir.PC() + offset;
|
||||||
|
auto cond_pass = IR::Term::LinkBlock{ir.current_location.AdvancePC(4)};
|
||||||
|
auto cond_fail = IR::Term::LinkBlock{ir.current_location.SetPC(target)};
|
||||||
|
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TranslatorVisitor::TBZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
|
||||||
|
size_t datasize = b5 == 1 ? 64 : 32;
|
||||||
|
size_t bit_pos = concatenate(b5, b40).ZeroExtend<size_t>();
|
||||||
|
s64 offset = concatenate(imm14, Imm<2>{0}).SignExtend<s64>();
|
||||||
|
|
||||||
|
auto operand = X(datasize, Rt);
|
||||||
|
|
||||||
|
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
|
||||||
|
|
||||||
|
u64 target = ir.PC() + offset;
|
||||||
|
auto cond_1 = IR::Term::LinkBlock{ir.current_location.AdvancePC(4)};
|
||||||
|
auto cond_0 = IR::Term::LinkBlock{ir.current_location.SetPC(target)};
|
||||||
|
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TranslatorVisitor::TBNZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
|
||||||
|
size_t datasize = b5 == 1 ? 64 : 32;
|
||||||
|
size_t bit_pos = concatenate(b5, b40).ZeroExtend<size_t>();
|
||||||
|
s64 offset = concatenate(imm14, Imm<2>{0}).SignExtend<s64>();
|
||||||
|
|
||||||
|
auto operand = X(datasize, Rt);
|
||||||
|
|
||||||
|
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
|
||||||
|
|
||||||
|
u64 target = ir.PC() + offset;
|
||||||
|
auto cond_1 = IR::Term::LinkBlock{ir.current_location.SetPC(target)};
|
||||||
|
auto cond_0 = IR::Term::LinkBlock{ir.current_location.AdvancePC(4)};
|
||||||
|
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace A64
|
} // namespace A64
|
||||||
} // namespace Dynarmic
|
} // namespace Dynarmic
|
||||||
|
|
|
@ -127,6 +127,10 @@ static std::string TerminalToString(const Terminal& terminal_variant) {
|
||||||
return fmt::format("If{{{}, {}, {}}}", A32::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
return fmt::format("If{{{}, {}, {}}}", A32::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||||
}
|
}
|
||||||
case 7: {
|
case 7: {
|
||||||
|
auto terminal = boost::get<IR::Term::CheckBit>(terminal_variant);
|
||||||
|
return fmt::format("CheckBit{{{}, {}}}", TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||||
|
}
|
||||||
|
case 8: {
|
||||||
auto terminal = boost::get<IR::Term::CheckHalt>(terminal_variant);
|
auto terminal = boost::get<IR::Term::CheckHalt>(terminal_variant);
|
||||||
return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_));
|
return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_));
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,13 +68,29 @@ U1 IREmitter::MostSignificantBit(const U32& value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
U1 IREmitter::IsZero(const U32& value) {
|
U1 IREmitter::IsZero(const U32& value) {
|
||||||
return Inst<U1>(Opcode::IsZero, value);
|
return Inst<U1>(Opcode::IsZero32, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
U1 IREmitter::IsZero64(const U64& value) {
|
U1 IREmitter::IsZero(const U64& value) {
|
||||||
return Inst<U1>(Opcode::IsZero64, value);
|
return Inst<U1>(Opcode::IsZero64, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
U1 IREmitter::IsZero(const U32U64& value) {
|
||||||
|
if (value.GetType() == Type::U32) {
|
||||||
|
return Inst<U1>(Opcode::IsZero32, value);
|
||||||
|
} else {
|
||||||
|
return Inst<U1>(Opcode::IsZero64, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
U1 IREmitter::TestBit(const U32U64& value, const U8& bit) {
|
||||||
|
if (value.GetType() == Type::U32) {
|
||||||
|
return Inst<U1>(Opcode::TestBit, IndeterminateExtendToLong(value), bit);
|
||||||
|
} else {
|
||||||
|
return Inst<U1>(Opcode::TestBit, value, bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NZCV IREmitter::NZCVFrom(const Value& value) {
|
NZCV IREmitter::NZCVFrom(const Value& value) {
|
||||||
return Inst<NZCV>(Opcode::GetNZCVFromOp, value);
|
return Inst<NZCV>(Opcode::GetNZCVFromOp, value);
|
||||||
}
|
}
|
||||||
|
@ -359,6 +375,16 @@ U32 IREmitter::ZeroExtendByteToWord(const U8& a) {
|
||||||
return Inst<U32>(Opcode::ZeroExtendByteToWord, a);
|
return Inst<U32>(Opcode::ZeroExtendByteToWord, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
U32 IREmitter::IndeterminateExtendToWord(const UAny& a) {
|
||||||
|
// TODO: Implement properly
|
||||||
|
return ZeroExtendToWord(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
U64 IREmitter::IndeterminateExtendToLong(const UAny& a) {
|
||||||
|
// TODO: Implement properly
|
||||||
|
return ZeroExtendToLong(a);
|
||||||
|
}
|
||||||
|
|
||||||
U32 IREmitter::ByteReverseWord(const U32& a) {
|
U32 IREmitter::ByteReverseWord(const U32& a) {
|
||||||
return Inst<U32>(Opcode::ByteReverseWord, a);
|
return Inst<U32>(Opcode::ByteReverseWord, a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,9 @@ public:
|
||||||
U8 LeastSignificantByte(U32U64 value);
|
U8 LeastSignificantByte(U32U64 value);
|
||||||
U1 MostSignificantBit(const U32& value);
|
U1 MostSignificantBit(const U32& value);
|
||||||
U1 IsZero(const U32& value);
|
U1 IsZero(const U32& value);
|
||||||
U1 IsZero64(const U64& value);
|
U1 IsZero(const U64& value);
|
||||||
|
U1 IsZero(const U32U64& value);
|
||||||
|
U1 TestBit(const U32U64& value, const U8& bit);
|
||||||
|
|
||||||
// This pseudo-instruction may only be added to instructions that support it.
|
// This pseudo-instruction may only be added to instructions that support it.
|
||||||
NZCV NZCVFrom(const Value& value);
|
NZCV NZCVFrom(const Value& value);
|
||||||
|
@ -125,6 +127,8 @@ public:
|
||||||
U32 ZeroExtendByteToWord(const U8& a);
|
U32 ZeroExtendByteToWord(const U8& a);
|
||||||
U32 ZeroExtendHalfToWord(const U16& a);
|
U32 ZeroExtendHalfToWord(const U16& a);
|
||||||
U64 ZeroExtendWordToLong(const U32& a);
|
U64 ZeroExtendWordToLong(const U32& a);
|
||||||
|
U32 IndeterminateExtendToWord(const UAny& a);
|
||||||
|
U64 IndeterminateExtendToLong(const UAny& a);
|
||||||
U32 ByteReverseWord(const U32& a);
|
U32 ByteReverseWord(const U32& a);
|
||||||
U16 ByteReverseHalf(const U16& a);
|
U16 ByteReverseHalf(const U16& a);
|
||||||
U64 ByteReverseDual(const U64& a);
|
U64 ByteReverseDual(const U64& a);
|
||||||
|
|
|
@ -246,13 +246,14 @@ bool Inst::IsCoprocessorInstruction() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Inst::MayHaveSideEffects() const {
|
bool Inst::MayHaveSideEffects() const {
|
||||||
return op == Opcode::PushRSB ||
|
return op == Opcode::PushRSB ||
|
||||||
CausesCPUException() ||
|
op == Opcode::A64SetCheckBit ||
|
||||||
WritesToCoreRegister() ||
|
CausesCPUException() ||
|
||||||
WritesToCPSR() ||
|
WritesToCoreRegister() ||
|
||||||
WritesToFPSCR() ||
|
WritesToCPSR() ||
|
||||||
AltersExclusiveState() ||
|
WritesToFPSCR() ||
|
||||||
IsMemoryWrite() ||
|
AltersExclusiveState() ||
|
||||||
|
IsMemoryWrite() ||
|
||||||
IsCoprocessorInstruction();
|
IsCoprocessorInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ A32OPC(GetFpscrNZCV, T::U32,
|
||||||
A32OPC(SetFpscrNZCV, T::Void, T::U32, )
|
A32OPC(SetFpscrNZCV, T::Void, T::U32, )
|
||||||
|
|
||||||
// A64 Context getters/setters
|
// A64 Context getters/setters
|
||||||
|
A64OPC(SetCheckBit, T::Void, T::U1 )
|
||||||
A64OPC(GetCFlag, T::U1, )
|
A64OPC(GetCFlag, T::U1, )
|
||||||
A64OPC(SetNZCV, T::Void, T::NZCVFlags )
|
A64OPC(SetNZCV, T::Void, T::NZCVFlags )
|
||||||
A64OPC(GetW, T::U32, T::A64Reg )
|
A64OPC(GetW, T::U32, T::A64Reg )
|
||||||
|
@ -61,8 +62,9 @@ OPCODE(MostSignificantWord, T::U32, T::U64
|
||||||
OPCODE(LeastSignificantHalf, T::U16, T::U32 )
|
OPCODE(LeastSignificantHalf, T::U16, T::U32 )
|
||||||
OPCODE(LeastSignificantByte, T::U8, T::U32 )
|
OPCODE(LeastSignificantByte, T::U8, T::U32 )
|
||||||
OPCODE(MostSignificantBit, T::U1, T::U32 )
|
OPCODE(MostSignificantBit, T::U1, T::U32 )
|
||||||
OPCODE(IsZero, T::U1, T::U32 )
|
OPCODE(IsZero32, T::U1, T::U32 )
|
||||||
OPCODE(IsZero64, T::U1, T::U64 )
|
OPCODE(IsZero64, T::U1, T::U64 )
|
||||||
|
OPCODE(TestBit, T::U1, T::U64, T::U8 )
|
||||||
OPCODE(LogicalShiftLeft32, 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(LogicalShiftLeft64, T::U64, T::U64, T::U8 )
|
||||||
OPCODE(LogicalShiftRight32, T::U32, T::U32, T::U8, T::U1 )
|
OPCODE(LogicalShiftRight32, T::U32, T::U32, T::U8, T::U1 )
|
||||||
|
|
|
@ -66,6 +66,7 @@ struct LinkBlockFast {
|
||||||
struct PopRSBHint {};
|
struct PopRSBHint {};
|
||||||
|
|
||||||
struct If;
|
struct If;
|
||||||
|
struct CheckBit;
|
||||||
struct CheckHalt;
|
struct CheckHalt;
|
||||||
/// A Terminal is the terminal instruction in a MicroBlock.
|
/// A Terminal is the terminal instruction in a MicroBlock.
|
||||||
using Terminal = boost::variant<
|
using Terminal = boost::variant<
|
||||||
|
@ -76,6 +77,7 @@ using Terminal = boost::variant<
|
||||||
LinkBlockFast,
|
LinkBlockFast,
|
||||||
PopRSBHint,
|
PopRSBHint,
|
||||||
boost::recursive_wrapper<If>,
|
boost::recursive_wrapper<If>,
|
||||||
|
boost::recursive_wrapper<CheckBit>,
|
||||||
boost::recursive_wrapper<CheckHalt>
|
boost::recursive_wrapper<CheckHalt>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -90,6 +92,17 @@ struct If {
|
||||||
Terminal else_;
|
Terminal else_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction conditionally executes one terminal or another depending
|
||||||
|
* on the run-time state of the check bit.
|
||||||
|
* then_ is executed if the check bit is non-zero, otherwise else_ is executed.
|
||||||
|
*/
|
||||||
|
struct CheckBit {
|
||||||
|
CheckBit(Terminal then_, Terminal else_) : then_(then_), else_(else_) {}
|
||||||
|
Terminal then_;
|
||||||
|
Terminal else_;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This terminal instruction checks if a halt was requested. If it wasn't, else_ is
|
* This terminal instruction checks if a halt was requested. If it wasn't, else_ is
|
||||||
* executed.
|
* executed.
|
||||||
|
|
|
@ -168,3 +168,80 @@ TEST_CASE("A64: ANDS NZCV", "[a64]") {
|
||||||
REQUIRE((jit.GetPstate() & 0xF0000000) == 0x00000000);
|
REQUIRE((jit.GetPstate() & 0xF0000000) == 0x00000000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("A64: CBZ", "[a64]") {
|
||||||
|
TestEnv env;
|
||||||
|
Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}};
|
||||||
|
|
||||||
|
env.code_mem[0] = 0x34000060; // CBZ X0, label
|
||||||
|
env.code_mem[1] = 0x320003e2; // MOV X2, 1
|
||||||
|
env.code_mem[2] = 0x14000000; // B.
|
||||||
|
env.code_mem[3] = 0x321f03e2; // label: MOV X2, 2
|
||||||
|
env.code_mem[4] = 0x14000000; // B .
|
||||||
|
|
||||||
|
SECTION("no branch") {
|
||||||
|
jit.SetPC(0);
|
||||||
|
jit.SetRegister(0, 1);
|
||||||
|
|
||||||
|
env.ticks_left = 4;
|
||||||
|
jit.Run();
|
||||||
|
|
||||||
|
REQUIRE(jit.GetRegister(2) == 1);
|
||||||
|
REQUIRE(jit.GetPC() == 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("branch") {
|
||||||
|
jit.SetPC(0);
|
||||||
|
jit.SetRegister(0, 0);
|
||||||
|
|
||||||
|
env.ticks_left = 4;
|
||||||
|
jit.Run();
|
||||||
|
|
||||||
|
REQUIRE(jit.GetRegister(2) == 2);
|
||||||
|
REQUIRE(jit.GetPC() == 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("A64: TBZ", "[a64]") {
|
||||||
|
TestEnv env;
|
||||||
|
Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}};
|
||||||
|
|
||||||
|
env.code_mem[0] = 0x36180060; // TBZ X0, 3, label
|
||||||
|
env.code_mem[1] = 0x320003e2; // MOV X2, 1
|
||||||
|
env.code_mem[2] = 0x14000000; // B .
|
||||||
|
env.code_mem[3] = 0x321f03e2; // label: MOV X2, 2
|
||||||
|
env.code_mem[4] = 0x14000000; // B .
|
||||||
|
|
||||||
|
SECTION("no branch") {
|
||||||
|
jit.SetPC(0);
|
||||||
|
jit.SetRegister(0, 0xFF);
|
||||||
|
|
||||||
|
env.ticks_left = 4;
|
||||||
|
jit.Run();
|
||||||
|
|
||||||
|
REQUIRE(jit.GetRegister(2) == 1);
|
||||||
|
REQUIRE(jit.GetPC() == 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("branch with zero") {
|
||||||
|
jit.SetPC(0);
|
||||||
|
jit.SetRegister(0, 0);
|
||||||
|
|
||||||
|
env.ticks_left = 4;
|
||||||
|
jit.Run();
|
||||||
|
|
||||||
|
REQUIRE(jit.GetRegister(2) == 2);
|
||||||
|
REQUIRE(jit.GetPC() == 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("branch with non-zero") {
|
||||||
|
jit.SetPC(0);
|
||||||
|
jit.SetRegister(0, 1);
|
||||||
|
|
||||||
|
env.ticks_left = 4;
|
||||||
|
jit.Run();
|
||||||
|
|
||||||
|
REQUIRE(jit.GetRegister(2) == 2);
|
||||||
|
REQUIRE(jit.GetPC() == 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue