A64: Implement compare and branch

This commit is contained in:
MerryMage
2018-01-07 16:33:02 +00:00
parent e8bcf72ee5
commit cb481a3a48
20 changed files with 249 additions and 21 deletions

View File

@@ -57,7 +57,7 @@ bool ArmTranslatorVisitor::arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero64(result));
ir.SetZFlag(ir.IsZero(result));
}
}
return true;
@@ -78,7 +78,7 @@ bool ArmTranslatorVisitor::arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero64(result));
ir.SetZFlag(ir.IsZero(result));
}
}
return true;
@@ -117,7 +117,7 @@ bool ArmTranslatorVisitor::arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero64(result));
ir.SetZFlag(ir.IsZero(result));
}
}
return true;
@@ -138,7 +138,7 @@ bool ArmTranslatorVisitor::arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m,
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero64(result));
ir.SetZFlag(ir.IsZero(result));
}
}
return true;

View File

@@ -117,10 +117,10 @@ std::vector<Matcher<V>> GetDecodeTable() {
INST(&V::BL, "BL", "100101iiiiiiiiiiiiiiiiiiiiiiiiii"),
// Compare and branch (immediate)
//INST(&V::CBZ, "CBZ", "z0110100iiiiiiiiiiiiiiiiiiittttt"),
//INST(&V::CBNZ, "CBNZ", "z0110101iiiiiiiiiiiiiiiiiiittttt"),
//INST(&V::TBZ, "TBZ", "b0110110bbbbbiiiiiiiiiiiiiittttt"),
//INST(&V::TBNZ, "TBNZ", "b0110111bbbbbiiiiiiiiiiiiiittttt"),
INST(&V::CBZ, "CBZ", "z0110100iiiiiiiiiiiiiiiiiiittttt"),
INST(&V::CBNZ, "CBNZ", "z0110101iiiiiiiiiiiiiiiiiiittttt"),
INST(&V::TBZ, "TBZ", "b0110110bbbbbiiiiiiiiiiiiiittttt"),
INST(&V::TBNZ, "TBNZ", "b0110111bbbbbiiiiiiiiiiiiiittttt"),
// Loads and stores - Advanced SIMD Load/Store multiple structures
//INST(&V::ST4_mult_1, "ST4 (multiple structures)", "0Q001100000000000000zznnnnnttttt"),

View File

@@ -22,6 +22,10 @@ u64 IREmitter::AlignPC(size_t alignment) {
return static_cast<u64>(pc - pc % alignment);
}
void IREmitter::SetCheckBit(const IR::U1& value) {
Inst(Opcode::A64SetCheckBit, value);
}
IR::U1 IREmitter::GetCFlag() {
return Inst<IR::U1>(Opcode::A64GetCFlag);
}

View File

@@ -31,6 +31,7 @@ public:
u64 PC();
u64 AlignPC(size_t alignment);
void SetCheckBit(const IR::U1& value);
IR::U1 GetCFlag();
void SetNZCV(const IR::NZCV& nzcv);

View File

@@ -62,5 +62,67 @@ bool TranslatorVisitor::RET(Reg Rn) {
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 Dynarmic

View File

@@ -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_));
}
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);
return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_));
}

View File

@@ -68,13 +68,29 @@ U1 IREmitter::MostSignificantBit(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);
}
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) {
return Inst<NZCV>(Opcode::GetNZCVFromOp, value);
}
@@ -359,6 +375,16 @@ U32 IREmitter::ZeroExtendByteToWord(const U8& 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) {
return Inst<U32>(Opcode::ByteReverseWord, a);
}

View File

@@ -80,7 +80,9 @@ public:
U8 LeastSignificantByte(U32U64 value);
U1 MostSignificantBit(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.
NZCV NZCVFrom(const Value& value);
@@ -125,6 +127,8 @@ public:
U32 ZeroExtendByteToWord(const U8& a);
U32 ZeroExtendHalfToWord(const U16& a);
U64 ZeroExtendWordToLong(const U32& a);
U32 IndeterminateExtendToWord(const UAny& a);
U64 IndeterminateExtendToLong(const UAny& a);
U32 ByteReverseWord(const U32& a);
U16 ByteReverseHalf(const U16& a);
U64 ByteReverseDual(const U64& a);

View File

@@ -246,13 +246,14 @@ bool Inst::IsCoprocessorInstruction() const {
}
bool Inst::MayHaveSideEffects() const {
return op == Opcode::PushRSB ||
CausesCPUException() ||
WritesToCoreRegister() ||
WritesToCPSR() ||
WritesToFPSCR() ||
AltersExclusiveState() ||
IsMemoryWrite() ||
return op == Opcode::PushRSB ||
op == Opcode::A64SetCheckBit ||
CausesCPUException() ||
WritesToCoreRegister() ||
WritesToCPSR() ||
WritesToFPSCR() ||
AltersExclusiveState() ||
IsMemoryWrite() ||
IsCoprocessorInstruction();
}

View File

@@ -35,6 +35,7 @@ A32OPC(GetFpscrNZCV, T::U32,
A32OPC(SetFpscrNZCV, T::Void, T::U32, )
// A64 Context getters/setters
A64OPC(SetCheckBit, T::Void, T::U1 )
A64OPC(GetCFlag, T::U1, )
A64OPC(SetNZCV, T::Void, T::NZCVFlags )
A64OPC(GetW, T::U32, T::A64Reg )
@@ -61,8 +62,9 @@ OPCODE(MostSignificantWord, T::U32, T::U64
OPCODE(LeastSignificantHalf, T::U16, T::U32 )
OPCODE(LeastSignificantByte, T::U8, 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(TestBit, T::U1, T::U64, T::U8 )
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 )

View File

@@ -66,6 +66,7 @@ struct LinkBlockFast {
struct PopRSBHint {};
struct If;
struct CheckBit;
struct CheckHalt;
/// A Terminal is the terminal instruction in a MicroBlock.
using Terminal = boost::variant<
@@ -76,6 +77,7 @@ using Terminal = boost::variant<
LinkBlockFast,
PopRSBHint,
boost::recursive_wrapper<If>,
boost::recursive_wrapper<CheckBit>,
boost::recursive_wrapper<CheckHalt>
>;
@@ -90,6 +92,17 @@ struct If {
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
* executed.