mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-14 11:01:01 +02:00
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:
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
|
||||
|
||||
@@ -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
@@ -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{};
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in new issue
Block a user