A64: Implement addsub instructions

This commit is contained in:
MerryMage
2018-01-07 11:31:20 +00:00
parent d1cef6ffb0
commit c09e69bb97
15 changed files with 796 additions and 84 deletions

View File

@@ -49,11 +49,17 @@ ResultAndCarry<U32> IREmitter::MostSignificantWord(const U64& value) {
return {result, carry_out};
}
U16 IREmitter::LeastSignificantHalf(const U32& value) {
U16 IREmitter::LeastSignificantHalf(U32U64 value) {
if (value.GetType() == Type::U64) {
value = LeastSignificantWord(value);
}
return Inst<U16>(Opcode::LeastSignificantHalf, value);
}
U8 IREmitter::LeastSignificantByte(const U32& value) {
U8 IREmitter::LeastSignificantByte(U32U64 value) {
if (value.GetType() == Type::U64) {
value = LeastSignificantWord(value);
}
return Inst<U8>(Opcode::LeastSignificantByte, value);
}
@@ -69,6 +75,10 @@ U1 IREmitter::IsZero64(const U64& value) {
return Inst<U1>(Opcode::IsZero64, value);
}
NZCV IREmitter::NZCVFrom(const Value& value) {
return Inst<NZCV>(Opcode::GetNZCVFromOp, value);
}
ResultAndCarry<U32> IREmitter::LogicalShiftLeft(const U32& value_in, const U8& shift_amount, const U1& carry_in) {
auto result = Inst<U32>(Opcode::LogicalShiftLeft32, value_in, shift_amount, carry_in);
auto carry_out = Inst<U1>(Opcode::GetCarryFromOp, result);
@@ -135,48 +145,75 @@ U32U64 IREmitter::RotateRight(const U32U64& value_in, const U8& 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);
ResultAndCarryAndOverflow<U32> IREmitter::AddWithCarry(const U32& a, const U32& b, const U1& carry_in) {
auto result = Inst<U32>(Opcode::Add32, a, b, carry_in);
auto carry_out = Inst<U1>(Opcode::GetCarryFromOp, result);
auto overflow = Inst<U1>(Opcode::GetOverflowFromOp, result);
return {result, carry_out, overflow};
}
U32U64 IREmitter::AddWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in) {
ASSERT(a.GetType() == b.GetType());
if (a.GetType() == Type::U32) {
return Inst<U32>(Opcode::Add32, a, b, carry_in);
} else {
return Inst<U64>(Opcode::Add64, a, b, carry_in);
}
}
U32 IREmitter::Add(const U32& a, const U32& b) {
return Inst<U32>(Opcode::AddWithCarry, a, b, Imm1(0));
return Inst<U32>(Opcode::Add32, a, b, Imm1(0));
}
U64 IREmitter::Add(const U64& a, const U64& b) {
return Inst<U64>(Opcode::Add64, a, b);
return Inst<U64>(Opcode::Add64, a, b, Imm1(0));
}
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));
return Inst<U32>(Opcode::Add32, a, b, Imm1(0));
} else {
return Inst<U64>(Opcode::Add64, a, b);
return Inst<U64>(Opcode::Add64, a, b, Imm1(0));
}
}
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);
auto result = Inst<U32>(Opcode::Sub32, a, b, carry_in);
auto carry_out = Inst<U1>(Opcode::GetCarryFromOp, result);
auto overflow = Inst<U1>(Opcode::GetOverflowFromOp, result);
return {result, carry_out, overflow};
}
U32U64 IREmitter::SubWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in) {
ASSERT(a.GetType() == b.GetType());
if (a.GetType() == Type::U32) {
return Inst<U32>(Opcode::Sub32, a, b, carry_in);
} else {
return Inst<U64>(Opcode::Sub64, a, b, carry_in);
}
}
U32 IREmitter::Sub(const U32& a, const U32& b) {
return Inst<U32>(Opcode::SubWithCarry, a, b, Imm1(1));
return Inst<U32>(Opcode::Sub32, a, b, Imm1(1));
}
U64 IREmitter::Sub(const U64& a, const U64& b) {
return Inst<U64>(Opcode::Sub64, a, b);
return Inst<U64>(Opcode::Sub64, a, b, Imm1(1));
}
U32U64 IREmitter::Sub(const U32U64& a, const U32U64& b) {
ASSERT(a.GetType() == b.GetType());
if (a.GetType() == Type::U32) {
return Inst<U32>(Opcode::Sub32, a, b, Imm1(1));
} else {
return Inst<U64>(Opcode::Sub64, a, b, Imm1(1));
}
}
U32 IREmitter::Mul(const U32& a, const U32& b) {
return Inst<U32>(Opcode::Mul, a, b);
return Inst<U32>(Opcode::Mul32, a, b);
}
U64 IREmitter::Mul(const U64& a, const U64& b) {
@@ -199,6 +236,38 @@ U32 IREmitter::Not(const U32& a) {
return Inst<U32>(Opcode::Not, a);
}
U64 IREmitter::SignExtendToLong(const UAny& a) {
switch (a.GetType()) {
case Type::U8:
return Inst<U64>(Opcode::SignExtendByteToLong, a);
case Type::U16:
return Inst<U64>(Opcode::SignExtendHalfToLong, a);
case Type::U32:
return Inst<U64>(Opcode::SignExtendWordToLong, a);
case Type::U64:
return U64(a);
default:
ASSERT_MSG(false, "Unreachable");
return {};
}
}
U32 IREmitter::SignExtendToWord(const UAny& a) {
switch (a.GetType()) {
case Type::U8:
return Inst<U32>(Opcode::SignExtendByteToWord, a);
case Type::U16:
return Inst<U32>(Opcode::SignExtendHalfToWord, a);
case Type::U32:
return U32(a);
case Type::U64:
return Inst<U32>(Opcode::LeastSignificantWord, a);
default:
ASSERT_MSG(false, "Unreachable");
return {};
}
}
U64 IREmitter::SignExtendWordToLong(const U32& a) {
return Inst<U64>(Opcode::SignExtendWordToLong, a);
}
@@ -211,6 +280,38 @@ U32 IREmitter::SignExtendByteToWord(const U8& a) {
return Inst<U32>(Opcode::SignExtendByteToWord, a);
}
U64 IREmitter::ZeroExtendToLong(const UAny& a) {
switch (a.GetType()) {
case Type::U8:
return Inst<U64>(Opcode::ZeroExtendByteToLong, a);
case Type::U16:
return Inst<U64>(Opcode::ZeroExtendHalfToLong, a);
case Type::U32:
return Inst<U64>(Opcode::ZeroExtendWordToLong, a);
case Type::U64:
return U64(a);
default:
ASSERT_MSG(false, "Unreachable");
return {};
}
}
U32 IREmitter::ZeroExtendToWord(const UAny& a) {
switch (a.GetType()) {
case Type::U8:
return Inst<U32>(Opcode::ZeroExtendByteToWord, a);
case Type::U16:
return Inst<U32>(Opcode::ZeroExtendHalfToWord, a);
case Type::U32:
return U32(a);
case Type::U64:
return Inst<U32>(Opcode::LeastSignificantWord, a);
default:
ASSERT_MSG(false, "Unreachable");
return {};
}
}
U64 IREmitter::ZeroExtendWordToLong(const U32& a) {
return Inst<U64>(Opcode::ZeroExtendWordToLong, a);
}

View File

@@ -76,12 +76,15 @@ public:
U64 Pack2x32To1x64(const U32& lo, const U32& hi);
U32 LeastSignificantWord(const U64& value);
ResultAndCarry<U32> MostSignificantWord(const U64& value);
U16 LeastSignificantHalf(const U32& value);
U8 LeastSignificantByte(const U32& value);
U16 LeastSignificantHalf(U32U64 value);
U8 LeastSignificantByte(U32U64 value);
U1 MostSignificantBit(const U32& value);
U1 IsZero(const U32& value);
U1 IsZero64(const U64& value);
// This pseudo-instruction may only be added to instructions that support it.
NZCV NZCVFrom(const Value& value);
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);
ResultAndCarry<U32> ArithmeticShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in);
@@ -92,25 +95,32 @@ public:
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);
ResultAndCarryAndOverflow<U32> AddWithCarry(const U32& a, const U32& b, const U1& carry_in);
ResultAndCarryAndOverflow<U32> SubWithCarry(const U32& a, const U32& b, const U1& carry_in);
U32U64 AddWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in);
U32U64 SubWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in);
U32 Add(const U32& a, const U32& 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 Sub(const U64& a, const U64& b);
U32U64 Sub(const U32U64& a, const U32U64& b);
U32 Mul(const U32& a, const U32& 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);
U32 Not(const U32& a);
U64 SignExtendWordToLong(const U32& a);
U32 SignExtendHalfToWord(const U16& a);
U32 SignExtendToWord(const UAny& a);
U64 SignExtendToLong(const UAny& a);
U32 SignExtendByteToWord(const U8& a);
U64 ZeroExtendWordToLong(const U32& a);
U32 ZeroExtendHalfToWord(const U16& a);
U32 SignExtendHalfToWord(const U16& a);
U64 SignExtendWordToLong(const U32& a);
U32 ZeroExtendToWord(const UAny& a);
U64 ZeroExtendToLong(const UAny& a);
U32 ZeroExtendByteToWord(const U8& a);
U32 ZeroExtendHalfToWord(const U16& a);
U64 ZeroExtendWordToLong(const U32& a);
U32 ByteReverseWord(const U32& a);
U16 ByteReverseHalf(const U16& a);
U64 ByteReverseDual(const U64& a);

View File

@@ -138,6 +138,7 @@ bool Inst::ReadsFromCoreRegister() const {
case Opcode::A32GetExtendedRegister64:
case Opcode::A64GetW:
case Opcode::A64GetX:
case Opcode::A64GetSP:
return true;
default:
@@ -153,6 +154,7 @@ bool Inst::WritesToCoreRegister() const {
case Opcode::A32BXWritePC:
case Opcode::A64SetW:
case Opcode::A64SetX:
case Opcode::A64SetSP:
return true;
default:
@@ -252,26 +254,55 @@ bool Inst::MayHaveSideEffects() const {
IsCoprocessorInstruction();
}
bool Inst::IsAPseudoOperation() const {
switch (op) {
case Opcode::GetCarryFromOp:
case Opcode::GetOverflowFromOp:
case Opcode::GetGEFromOp:
case Opcode::GetNZCVFromOp:
return true;
default:
return false;
}
}
bool Inst::MayGetNZCVFromOp() const {
switch (op) {
case Opcode::Add32:
case Opcode::Add64:
case Opcode::Sub32:
case Opcode::Sub64:
return true;
default:
return false;
}
}
bool Inst::AreAllArgsImmediates() const {
return std::all_of(args.begin(), args.begin() + NumArgs(), [](const auto& value){ return value.IsImmediate(); });
}
bool Inst::HasAssociatedPseudoOperation() const {
return carry_inst || overflow_inst || ge_inst;
return carry_inst || overflow_inst || ge_inst || nzcv_inst;
}
Inst* Inst::GetAssociatedPseudoOperation(Opcode opcode) {
// This is faster than doing a search through the block.
switch (opcode) {
case IR::Opcode::GetCarryFromOp:
case Opcode::GetCarryFromOp:
ASSERT(!carry_inst || carry_inst->GetOpcode() == Opcode::GetCarryFromOp);
return carry_inst;
case IR::Opcode::GetOverflowFromOp:
case Opcode::GetOverflowFromOp:
ASSERT(!overflow_inst || overflow_inst->GetOpcode() == Opcode::GetOverflowFromOp);
return overflow_inst;
case IR::Opcode::GetGEFromOp:
case Opcode::GetGEFromOp:
ASSERT(!ge_inst || ge_inst->GetOpcode() == Opcode::GetGEFromOp);
return ge_inst;
case Opcode::GetNZCVFromOp:
ASSERT(!nzcv_inst || nzcv_inst->GetOpcode() == Opcode::GetNZCVFromOp);
return nzcv_inst;
default:
break;
}
@@ -345,6 +376,11 @@ void Inst::Use(const Value& value) {
ASSERT_MSG(!value.GetInst()->ge_inst, "Only one of each type of pseudo-op allowed");
value.GetInst()->ge_inst = this;
break;
case Opcode::GetNZCVFromOp:
ASSERT_MSG(!value.GetInst()->nzcv_inst, "Only one of each type of pseudo-op allowed");
ASSERT_MSG(MayGetNZCVFromOp(), "This instruction doesn't support the GetNZCVFromOp pseduo-op");
value.GetInst()->nzcv_inst = this;
break;
default:
break;
}
@@ -366,6 +402,10 @@ void Inst::UndoUse(const Value& value) {
ASSERT(value.GetInst()->ge_inst->GetOpcode() == Opcode::GetGEFromOp);
value.GetInst()->ge_inst = nullptr;
break;
case Opcode::GetNZCVFromOp:
ASSERT(value.GetInst()->nzcv_inst->GetOpcode() == Opcode::GetNZCVFromOp);
value.GetInst()->nzcv_inst = nullptr;
break;
default:
break;
}

View File

@@ -76,6 +76,13 @@ public:
/// Determines whether or not this instruction may have side-effects.
bool MayHaveSideEffects() const;
/// Determines whether or not this instruction is a pseduo-instruction.
/// Pseudo-instructions depend on their parent instructions for their semantics.
bool IsAPseudoOperation() const;
/// Determins whether or not this instruction supports the GetNZCVFromOp pseudo-operation.
bool MayGetNZCVFromOp() const;
/// Determines if all arguments of this instruction are immediates.
bool AreAllArgsImmediates() const;
@@ -116,6 +123,7 @@ private:
Inst* ge_inst;
};
Inst* overflow_inst = nullptr;
Inst* nzcv_inst = nullptr;
};
} // namespace IR

View File

@@ -47,6 +47,7 @@ enum class Type {
F64 = 1 << 11,
F128 = 1 << 12,
CoprocInfo = 1 << 13,
NZCVFlags = 1 << 14,
};
constexpr Type operator|(Type a, Type b) {

View File

@@ -35,10 +35,14 @@ A32OPC(GetFpscrNZCV, T::U32,
A32OPC(SetFpscrNZCV, T::Void, T::U32, )
// A64 Context getters/setters
A64OPC(GetCFlag, T::U1, )
A64OPC(SetNZCV, T::Void, T::NZCVFlags )
A64OPC(GetW, T::U32, T::A64Reg )
A64OPC(GetX, T::U64, T::A64Reg )
A64OPC(GetSP, T::U64, )
A64OPC(SetW, T::Void, T::A64Reg, T::U32 )
A64OPC(SetX, T::Void, T::A64Reg, T::U64 )
A64OPC(SetSP, T::Void, T::U64 )
// Hints
OPCODE(PushRSB, T::Void, T::U64 )
@@ -47,6 +51,7 @@ OPCODE(PushRSB, T::Void, T::U64
OPCODE(GetCarryFromOp, T::U1, T::U32 )
OPCODE(GetOverflowFromOp, T::U1, T::U32 )
OPCODE(GetGEFromOp, T::U32, T::U32 )
OPCODE(GetNZCVFromOp, T::NZCVFlags, T::Opaque )
// Calculations
OPCODE(Pack2x32To1x64, T::U64, T::U32, T::U32 )
@@ -66,22 +71,26 @@ 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 )
OPCODE(Add64, T::U64, T::U64, T::U64 )
OPCODE(Sub64, T::U64, T::U64, T::U64 )
OPCODE(Mul, T::U32, T::U32, T::U32 )
OPCODE(Add32, T::U32, T::U32, T::U32, T::U1 )
OPCODE(Add64, T::U64, T::U64, T::U64, T::U1 )
OPCODE(Sub32, T::U32, T::U32, T::U32, T::U1 )
OPCODE(Sub64, T::U64, T::U64, T::U64, T::U1 )
OPCODE(Mul32, T::U32, T::U32, T::U32 )
OPCODE(Mul64, T::U64, T::U64, T::U64 )
OPCODE(And, T::U32, T::U32, T::U32 )
OPCODE(Eor, T::U32, T::U32, T::U32 )
OPCODE(Or, T::U32, T::U32, T::U32 )
OPCODE(Not, T::U32, T::U32 )
OPCODE(SignExtendWordToLong, T::U64, T::U32 )
OPCODE(SignExtendHalfToWord, T::U32, T::U16 )
OPCODE(SignExtendByteToWord, T::U32, T::U8 )
OPCODE(ZeroExtendWordToLong, T::U64, T::U32 )
OPCODE(ZeroExtendHalfToWord, T::U32, T::U16 )
OPCODE(SignExtendHalfToWord, T::U32, T::U16 )
OPCODE(SignExtendByteToLong, T::U64, T::U8 )
OPCODE(SignExtendHalfToLong, T::U64, T::U16 )
OPCODE(SignExtendWordToLong, T::U64, T::U32 )
OPCODE(ZeroExtendByteToWord, T::U32, T::U8 )
OPCODE(ZeroExtendHalfToWord, T::U32, T::U16 )
OPCODE(ZeroExtendByteToLong, T::U64, T::U8 )
OPCODE(ZeroExtendHalfToLong, T::U64, T::U16 )
OPCODE(ZeroExtendWordToLong, T::U64, T::U32 )
OPCODE(ByteReverseWord, T::U32, T::U32 )
OPCODE(ByteReverseHalf, T::U16, T::U16 )
OPCODE(ByteReverseDual, T::U64, T::U64 )

View File

@@ -94,10 +94,12 @@ using U16 = TypedValue<Type::U16>;
using U32 = TypedValue<Type::U32>;
using U64 = TypedValue<Type::U64>;
using U32U64 = TypedValue<Type::U32 | Type::U64>;
using UAny = TypedValue<Type::U8 | Type::U16 | Type::U32 | Type::U64>;
using F32 = TypedValue<Type::F32>;
using F64 = TypedValue<Type::F64>;
using F128 = TypedValue<Type::F128>;
using F32F64 = TypedValue<Type::F32 | Type::F64>;
using NZCV = TypedValue<Type::NZCVFlags>;
} // namespace IR
} // namespace Dynarmic