IR: Adds support for GPRPair to Select IR op

This IR operation was limited to GPR only previously for the values
getting compared.

This adds support for GPRPair (and technically FPR) so that it can be
used directly with GPR pairs. I say technically FPR because the
IREmitter disallowed FPRs for the comparison, but this was already
supported in all of the backends, we just didn't ever use it.

Some minor changes to the constant prop pass to ensure that we don't try
to propagate a select in to a CondJump, otherwise pair comparisons would
be duplicated. This code is expecting to be able to merge a simple
comparison in to a `cbnz`, which doesn't happen with GPR pairs.
This commit is contained in:
Ryan Houdek committed 2023-08-01 19:50:15 -07:00
1 parent d196709162
commit 6834fe32e4
11 files changed
+85 -14

No files matched your search

@@ -885,12 +885,8 @@ DEF_OP(Select) {
auto Op = IROp->C<IR::IROp_Select>();
const uint8_t OpSize = IROp->Size;
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp1);
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp2);
uint64_t ArgTrue;
uint64_t ArgFalse;
if (OpSize == 4) {
ArgTrue = *GetSrc<uint32_t*>(Data->SSAData, Op->TrueVal);
ArgFalse = *GetSrc<uint32_t*>(Data->SSAData, Op->FalseVal);
@@ -901,10 +897,25 @@ DEF_OP(Select) {
bool CompResult;
if (Op->CompareSize == 4)
if (Op->CompareSize == 4) {
const auto Src1 = *GetSrc<uint32_t*>(Data->SSAData, Op->Cmp1);
const auto Src2 = *GetSrc<uint32_t*>(Data->SSAData, Op->Cmp2);
CompResult = IsConditionTrue<uint32_t, int32_t, float>(Op->Cond.Val, Src1, Src2);
else
}
else if (Op->CompareSize == 8) {
const auto Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp1);
const auto Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp2);
CompResult = IsConditionTrue<uint64_t, int64_t, double>(Op->Cond.Val, Src1, Src2);
}
else if (Op->CompareSize == 16) {
const auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Cmp1);
const auto Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Cmp2);
CompResult = IsConditionTrue<__uint128_t, __int128_t, double>(Op->Cond.Val, Src1, Src2);
}
else {
LOGMAN_MSG_A_FMT("Unknown select size: {}", Op->CompareSize);
FEX_UNREACHABLE;
}
GD = CompResult ? ArgTrue : ArgFalse;
}
@@ -350,6 +350,15 @@ namespace FEXCore::CPU {
template<typename unsigned_type, typename signed_type, typename float_type>
[[nodiscard]] static bool IsConditionTrue(uint8_t Cond, uint64_t Src1, uint64_t Src2) {
bool CompResult = false;
if constexpr (sizeof(unsigned_type) == 16) {
LOGMAN_THROW_A_FMT(Cond != FEXCore::IR::COND_FLU &&
Cond != FEXCore::IR::COND_FGE &&
Cond != FEXCore::IR::COND_FLEU &&
Cond != FEXCore::IR::COND_FGT &&
Cond != FEXCore::IR::COND_FU &&
Cond != FEXCore::IR::COND_FNU, "Unsupported comparison for 128-bit floats");
}
switch (Cond) {
case FEXCore::IR::COND_EQ:
CompResult = static_cast<unsigned_type>(Src1) == static_cast<unsigned_type>(Src2);
@@ -1129,6 +1129,7 @@ DEF_OP(Select) {
const auto CompareEmitSize = Op->CompareSize == 8 ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
uint64_t Const;
auto cc = MapSelectCC(Op->Cond);
if (IsGPR(Op->Cmp1.ID())) {
const auto Src1 = GetReg(Op->Cmp1.ID());
@@ -1139,7 +1140,14 @@ DEF_OP(Select) {
const auto Src2 = GetReg(Op->Cmp2.ID());
cmp(CompareEmitSize, Src1, Src2);
}
} else if (IsFPR(Op->Cmp1.ID())) {
}
else if (IsGPRPair(Op->Cmp1.ID())) {
const auto Src1 = GetRegPair(Op->Cmp1.ID());
const auto Src2 = GetRegPair(Op->Cmp2.ID());
cmp(EmitSize, Src1.first, Src2.first);
ccmp(EmitSize, Src1.second, Src2.second, ARMEmitter::StatusFlags::None, cc);
}
else if (IsFPR(Op->Cmp1.ID())) {
const auto Src1 = GetVReg(Op->Cmp1.ID());
const auto Src2 = GetVReg(Op->Cmp2.ID());
fcmp(Op->CompareSize == 8 ? ARMEmitter::ScalarRegSize::i64Bit : ARMEmitter::ScalarRegSize::i32Bit, Src1, Src2);
@@ -1147,8 +1155,6 @@ DEF_OP(Select) {
LOGMAN_MSG_A_FMT("Select: Expected GPR or FPR");
}
auto cc = MapSelectCC(Op->Cond);
uint64_t const_true, const_false;
bool is_const_true = IsInlineConstant(Op->TrueVal, &const_true);
bool is_const_false = IsInlineConstant(Op->FalseVal, &const_false);
@@ -724,6 +724,12 @@ bool Arm64JITCore::IsGPR(IR::NodeID Node) const {
return Class == IR::GPRClass || Class == IR::GPRFixedClass;
}
bool Arm64JITCore::IsGPRPair(IR::NodeID Node) const {
auto Class = GetRegClass(Node);
return Class == IR::GPRPairClass;
}
CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry,
FEXCore::IR::IRListView const *IR,
FEXCore::Core::DebugData *DebugData,
@@ -112,6 +112,7 @@ private:
[[nodiscard]] bool IsFPR(IR::NodeID Node) const;
[[nodiscard]] bool IsGPR(IR::NodeID Node) const;
[[nodiscard]] bool IsGPRPair(IR::NodeID Node) const;
[[nodiscard]] FEXCore::ARMEmitter::ExtendedMemOperand GenerateMemOperand(uint8_t AccessSize,
FEXCore::ARMEmitter::Register Base,
@@ -1140,7 +1140,28 @@ DEF_OP(Select) {
} else {
cmp(GRCMP(Op->Cmp1.ID()), GRCMP(Op->Cmp2.ID()));
}
} else if (IsFPR(Op->Cmp1.ID())) {
}
else if (IsGPRPair(Op->Cmp1.ID())) {
if (Op->CompareSize == 4) {
const auto Src1 = GetSrcPair<RA_32>(Op->Cmp1.ID());
const auto Src2 = GetSrcPair<RA_32>(Op->Cmp2.ID());
mov (TMP1.cvt32(), Src1.first);
mov (TMP2.cvt32(), Src1.second);
xor_(TMP1.cvt32(), Src2.first);
xor_(TMP2.cvt32(), Src2.second);
or_(TMP1.cvt32(), TMP2.cvt32());
}
else {
const auto Src1 = GetSrcPair<RA_64>(Op->Cmp1.ID());
const auto Src2 = GetSrcPair<RA_64>(Op->Cmp2.ID());
mov (TMP1, Src1.first);
mov (TMP2, Src1.second);
xor_(TMP1, Src2.first);
xor_(TMP2, Src2.second);
or_(TMP1, TMP2);
}
}
else if (IsFPR(Op->Cmp1.ID())) {
if (Op->CompareSize == 4)
ucomiss(GetSrc(Op->Cmp1.ID()), GetSrc(Op->Cmp2.ID()));
else
+6 -2
View File
@@ -484,11 +484,15 @@ IR::PhysicalRegister X86JITCore::GetPhys(IR::NodeID Node) const {
}
bool X86JITCore::IsFPR(IR::NodeID Node) const {
return RAData->GetNodeRegister(Node).Class == IR::FPRClass.Val;
return RAData->GetNodeRegister(Node).Class == IR::FPRClass;
}
bool X86JITCore::IsGPR(IR::NodeID Node) const {
return RAData->GetNodeRegister(Node).Class == IR::GPRClass.Val;
return RAData->GetNodeRegister(Node).Class == IR::GPRClass;
}
bool X86JITCore::IsGPRPair(IR::NodeID Node) const {
return RAData->GetNodeRegister(Node).Class == IR::GPRPairClass;
}
template<uint8_t RAType>
@@ -169,6 +169,7 @@ private:
[[nodiscard]] bool IsFPR(IR::NodeID Node) const;
[[nodiscard]] bool IsGPR(IR::NodeID Node) const;
[[nodiscard]] bool IsGPRPair(IR::NodeID Node) const;
template<uint8_t RAType>
[[nodiscard]] Xbyak::Reg GetSrc(IR::NodeID Node) const;
+5 -2
View File
@@ -885,12 +885,15 @@
],
"DestSize": "8"
},
"GPR = Select CondClass:$Cond, GPR:$Cmp1, GPR:$Cmp2, GPR:$TrueVal, GPR:$FalseVal, u8:$CompareSize": {
"GPR = Select CondClass:$Cond, SSA:$Cmp1, SSA:$Cmp2, GPR:$TrueVal, GPR:$FalseVal, u8:$CompareSize": {
"Desc": ["Ternary selection of GPRs",
"op:",
"Dest = Cmp1 <Cond> Cmp2 ? TrueVal : FalseVal"
],
"DestSize": "std::max<uint8_t>(4, std::max<uint8_t>(GetOpSize(_TrueVal), GetOpSize(_FalseVal)))"
"DestSize": "std::max<uint8_t>(4, std::max<uint8_t>(GetOpSize(_TrueVal), GetOpSize(_FalseVal)))",
"EmitValidation": [
"WalkFindRegClass($Cmp1) == WalkFindRegClass($Cmp2)"
]
},
"GPR = Extr GPR:$Upper, GPR:$Lower, u8:$LSB": {
"Desc": ["Concats the two GPRs to create a value that is the size of the full two GPRs",
@@ -906,6 +906,11 @@ bool ConstProp::ConstantPropagation(IREmitter *IREmit, const IRListView& Current
// Fold the select into the CondJump if possible. Could handle more complex cases, too.
if (Op->Cond.Val == COND_NEQ && IREmit->IsValueConstant(Op->Cmp2, &Constant) && Constant == 0 && Select->Op == OP_SELECT) {
const auto SelectCmpClass = IREmit->WalkFindRegClass(Select->Args[0]);
if (SelectCmpClass == GPRPairClass) {
// If the comparison class is a GPRPair then don't fold the select since it isn't free.
break;
}
uint64_t Constant1{};
uint64_t Constant2{};
+4
View File
@@ -171,6 +171,10 @@ friend class FEXCore::IR::PassManager;
}
/** @} */
FEXCore::IR::RegisterClassType WalkFindRegClass(OrderedNodeWrapper ssa) {
OrderedNode *RealNode = ssa.GetNode(DualListData.ListBegin());
return WalkFindRegClass(RealNode);
}
bool IsValueConstant(OrderedNodeWrapper ssa, uint64_t *Constant = nullptr) {
OrderedNode *RealNode = ssa.GetNode(DualListData.ListBegin());