Skip to content

Commit 3bc4f0e

Browse files
authored
JIT: Lift remaining cmov restrictions by introducing GT_SELECTCC (#82235)
This introduces GT_SELECTCC and unifies its handling with GT_JCC. We no longer use containment for GT_SELECT conditions in the xarch backend. Additionally teaches liveness DCE about GT_SETCC and GT_SELECTCC by allowing it to remove GTF_SET_FLAGS from the previous node when they are unused. Minor diffs expected; the additional cases are really not that common. The main benefit is that GT_SELECT is now fully on par with GT_JTRUE, and does not have any odd limitations. The code to handle conditions is completely shared.
1 parent 71753c4 commit 3bc4f0e

File tree

14 files changed

+309
-284
lines changed

14 files changed

+309
-284
lines changed

src/coreclr/jit/codegen.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
904904
void genCompareInt(GenTree* treeNode);
905905
#ifdef TARGET_XARCH
906906
bool genCanAvoidEmittingCompareAgainstZero(GenTree* tree, var_types opType);
907-
GenTreeCC* genTryFindFlagsConsumer(GenTree* flagsProducer);
907+
GenTree* genTryFindFlagsConsumer(GenTree* flagsProducer, GenCondition** condition);
908908
#endif
909909

910910
#ifdef FEATURE_SIMD

src/coreclr/jit/codegenxarch.cpp

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,18 +1356,14 @@ instruction CodeGen::JumpKindToCmov(emitJumpKind condition)
13561356
}
13571357

13581358
//------------------------------------------------------------------------
1359-
// genCodeForCompare: Produce code for a GT_SELECT/GT_SELECT_HI node.
1359+
// genCodeForCompare: Produce code for a GT_SELECT/GT_SELECTCC node.
13601360
//
13611361
// Arguments:
13621362
// select - the node
13631363
//
13641364
void CodeGen::genCodeForSelect(GenTreeOp* select)
13651365
{
1366-
#ifdef TARGET_X86
1367-
assert(select->OperIs(GT_SELECT, GT_SELECT_HI));
1368-
#else
1369-
assert(select->OperIs(GT_SELECT));
1370-
#endif
1366+
assert(select->OperIs(GT_SELECT, GT_SELECTCC));
13711367

13721368
if (select->OperIs(GT_SELECT))
13731369
{
@@ -1385,25 +1381,13 @@ void CodeGen::genCodeForSelect(GenTreeOp* select)
13851381

13861382
if (select->OperIs(GT_SELECT))
13871383
{
1388-
GenTree* cond = select->AsConditional()->gtCond;
1389-
if (cond->isContained())
1390-
{
1391-
assert(cond->OperIsCompare());
1392-
genCodeForCompare(cond->AsOp());
1393-
cc = GenCondition::FromRelop(cond);
1394-
1395-
if (cc.PreferSwap())
1396-
{
1397-
// genCodeForCompare generated the compare with swapped
1398-
// operands because this swap requires fewer branches/cmovs.
1399-
cc = GenCondition::Swap(cc);
1400-
}
1401-
}
1402-
else
1403-
{
1404-
regNumber condReg = cond->GetRegNum();
1405-
GetEmitter()->emitIns_R_R(INS_test, EA_4BYTE, condReg, condReg);
1406-
}
1384+
GenTree* cond = select->AsConditional()->gtCond;
1385+
regNumber condReg = cond->GetRegNum();
1386+
GetEmitter()->emitIns_R_R(INS_test, emitActualTypeSize(cond), condReg, condReg);
1387+
}
1388+
else
1389+
{
1390+
cc = select->AsOpCC()->gtCondition;
14071391
}
14081392

14091393
// The usual codegen will be
@@ -1875,11 +1859,9 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
18751859
genCodeForSelect(treeNode->AsConditional());
18761860
break;
18771861

1878-
#ifdef TARGET_X86
1879-
case GT_SELECT_HI:
1862+
case GT_SELECTCC:
18801863
genCodeForSelect(treeNode->AsOp());
18811864
break;
1882-
#endif
18831865

18841866
case GT_RETURNTRAP:
18851867
genCodeForReturnTrap(treeNode->AsOp());
@@ -6809,22 +6791,23 @@ bool CodeGen::genCanAvoidEmittingCompareAgainstZero(GenTree* tree, var_types opT
68096791
return false;
68106792
}
68116793

6812-
GenTreeCC* cc = nullptr;
6813-
GenCondition cond;
6794+
GenTree* consumer = nullptr;
6795+
GenCondition* mutableCond = nullptr;
6796+
GenCondition cond;
68146797

68156798
if (tree->OperIsCompare())
68166799
{
68176800
cond = GenCondition::FromIntegralRelop(tree);
68186801
}
68196802
else
68206803
{
6821-
cc = genTryFindFlagsConsumer(tree);
6822-
if (cc == nullptr)
6804+
consumer = genTryFindFlagsConsumer(tree, &mutableCond);
6805+
if (consumer == nullptr)
68236806
{
68246807
return false;
68256808
}
68266809

6827-
cond = cc->gtCondition;
6810+
cond = *mutableCond;
68286811
}
68296812

68306813
if (GetEmitter()->AreFlagsSetToZeroCmp(op1->GetRegNum(), emitTypeSize(opType), cond))
@@ -6833,11 +6816,12 @@ bool CodeGen::genCanAvoidEmittingCompareAgainstZero(GenTree* tree, var_types opT
68336816
return true;
68346817
}
68356818

6836-
if ((cc != nullptr) && GetEmitter()->AreFlagsSetForSignJumpOpt(op1->GetRegNum(), emitTypeSize(opType), cond))
6819+
if ((mutableCond != nullptr) &&
6820+
GetEmitter()->AreFlagsSetForSignJumpOpt(op1->GetRegNum(), emitTypeSize(opType), cond))
68376821
{
68386822
JITDUMP("Not emitting compare due to sign being already set; modifying [%06u] to check sign flag\n",
6839-
Compiler::dspTreeID(cc));
6840-
cc->gtCondition =
6823+
Compiler::dspTreeID(consumer));
6824+
*mutableCond =
68416825
(cond.GetCode() == GenCondition::SLT) ? GenCondition(GenCondition::S) : GenCondition(GenCondition::NS);
68426826
return true;
68436827
}
@@ -6851,11 +6835,12 @@ bool CodeGen::genCanAvoidEmittingCompareAgainstZero(GenTree* tree, var_types opT
68516835
//
68526836
// Parameters:
68536837
// producer - the node that produces CPU flags
6838+
// cond - [out] the pointer to the condition inside that consumer.
68546839
//
68556840
// Returns:
68566841
// A node that consumes the flags, or nullptr if no such node was found.
68576842
//
6858-
GenTreeCC* CodeGen::genTryFindFlagsConsumer(GenTree* producer)
6843+
GenTree* CodeGen::genTryFindFlagsConsumer(GenTree* producer, GenCondition** cond)
68596844
{
68606845
assert((producer->gtFlags & GTF_SET_FLAGS) != 0);
68616846
// We allow skipping some nodes where we know for sure that the flags are
@@ -6866,7 +6851,14 @@ GenTreeCC* CodeGen::genTryFindFlagsConsumer(GenTree* producer)
68666851
{
68676852
if (candidate->OperIs(GT_JCC, GT_SETCC))
68686853
{
6869-
return candidate->AsCC();
6854+
*cond = &candidate->AsCC()->gtCondition;
6855+
return candidate;
6856+
}
6857+
6858+
if (candidate->OperIs(GT_SELECTCC))
6859+
{
6860+
*cond = &candidate->AsOpCC()->gtCondition;
6861+
return candidate;
68706862
}
68716863

68726864
// The following nodes can be inserted between the compare and the user

src/coreclr/jit/compiler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,6 +2414,9 @@ class Compiler
24142414
// For binary opers.
24152415
GenTree* gtNewOperNode(genTreeOps oper, var_types type, GenTree* op1, GenTree* op2);
24162416

2417+
GenTreeCC* gtNewCC(genTreeOps oper, var_types type, GenCondition cond);
2418+
GenTreeOpCC* gtNewOperCC(genTreeOps oper, var_types type, GenCondition cond, GenTree* op1, GenTree* op2);
2419+
24172420
GenTreeColon* gtNewColonNode(var_types type, GenTree* elseNode, GenTree* thenNode);
24182421
GenTreeQmark* gtNewQmarkNode(var_types type, GenTree* cond, GenTreeColon* colon);
24192422

src/coreclr/jit/decomposelongs.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,10 +1545,10 @@ GenTree* DecomposeLongs::DecomposeSelect(LIR::Use& use)
15451545

15461546
// Normally GT_SELECT is responsible for evaluating the condition into
15471547
// flags, but for the "upper half" we treat the lower GT_SELECT similar to
1548-
// other flag producing nodes and reuse them. GT_SELECT_HI is the variant
1548+
// other flag producing nodes and reuse them. GT_SELECTCC is the variant
15491549
// that uses existing flags and has no condition as part of it.
15501550
select->gtFlags |= GTF_SET_FLAGS;
1551-
GenTree* hiSelect = m_compiler->gtNewOperNode(GT_SELECT_HI, TYP_INT, hiOp1, hiOp2);
1551+
GenTree* hiSelect = m_compiler->gtNewOperCC(GT_SELECTCC, TYP_INT, GenCondition::NE, hiOp1, hiOp2);
15521552

15531553
Range().InsertAfter(select, hiSelect);
15541554

src/coreclr/jit/gentree.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ void GenTree::InitNodeSize()
312312
static_assert_no_msg(sizeof(GenTreeLclVar) <= TREE_NODE_SZ_SMALL);
313313
static_assert_no_msg(sizeof(GenTreeLclFld) <= TREE_NODE_SZ_SMALL);
314314
static_assert_no_msg(sizeof(GenTreeCC) <= TREE_NODE_SZ_SMALL);
315+
static_assert_no_msg(sizeof(GenTreeOpCC) <= TREE_NODE_SZ_SMALL);
315316
static_assert_no_msg(sizeof(GenTreeCast) <= TREE_NODE_SZ_LARGE); // *** large node
316317
static_assert_no_msg(sizeof(GenTreeBox) <= TREE_NODE_SZ_LARGE); // *** large node
317318
static_assert_no_msg(sizeof(GenTreeField) <= TREE_NODE_SZ_LARGE); // *** large node
@@ -6944,6 +6945,18 @@ GenTree* Compiler::gtNewOperNode(genTreeOps oper, var_types type, GenTree* op1,
69446945
return node;
69456946
}
69466947

6948+
GenTreeCC* Compiler::gtNewCC(genTreeOps oper, var_types type, GenCondition cond)
6949+
{
6950+
GenTreeCC* node = new (this, oper) GenTreeCC(oper, type, cond);
6951+
return node;
6952+
}
6953+
6954+
GenTreeOpCC* Compiler::gtNewOperCC(genTreeOps oper, var_types type, GenCondition cond, GenTree* op1, GenTree* op2)
6955+
{
6956+
GenTreeOpCC* node = new (this, oper) GenTreeOpCC(oper, type, cond, op1, op2);
6957+
return node;
6958+
}
6959+
69476960
GenTreeColon* Compiler::gtNewColonNode(var_types type, GenTree* elseNode, GenTree* thenNode)
69486961
{
69496962
return new (this, GT_COLON) GenTreeColon(TYP_INT, elseNode, thenNode);
@@ -12135,6 +12148,10 @@ void Compiler::gtDispTree(GenTree* tree,
1213512148
break;
1213612149
}
1213712150
}
12151+
else if (tree->OperIs(GT_SELECTCC))
12152+
{
12153+
printf(" cond=%s", tree->AsOpCC()->gtCondition.Name());
12154+
}
1213812155

1213912156
gtDispCommonEndLine(tree);
1214012157

src/coreclr/jit/gentree.h

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,7 +1686,16 @@ struct GenTree
16861686

16871687
bool OperIsConditionalJump() const
16881688
{
1689-
return (gtOper == GT_JTRUE) || (gtOper == GT_JCMP) || (gtOper == GT_JCC);
1689+
return OperIs(GT_JTRUE, GT_JCMP, GT_JCC);
1690+
}
1691+
1692+
bool OperConsumesFlags() const
1693+
{
1694+
#if !defined(TARGET_64BIT)
1695+
if (OperIs(GT_ADD_HI, GT_SUB_HI))
1696+
return true;
1697+
#endif
1698+
return OperIs(GT_JCC, GT_SETCC, GT_SELECTCC);
16901699
}
16911700

16921701
#ifdef DEBUG
@@ -8521,12 +8530,11 @@ struct GenCondition
85218530
};
85228531

85238532
// Represents a GT_JCC or GT_SETCC node.
8524-
85258533
struct GenTreeCC final : public GenTree
85268534
{
85278535
GenCondition gtCondition;
85288536

8529-
GenTreeCC(genTreeOps oper, GenCondition condition, var_types type = TYP_VOID)
8537+
GenTreeCC(genTreeOps oper, var_types type, GenCondition condition)
85308538
: GenTree(oper, type DEBUGARG(/*largeNode*/ FALSE)), gtCondition(condition)
85318539
{
85328540
assert(OperIs(GT_JCC, GT_SETCC));
@@ -8539,6 +8547,24 @@ struct GenTreeCC final : public GenTree
85398547
#endif // DEBUGGABLE_GENTREE
85408548
};
85418549

8550+
// Represents a node with two operands and a condition.
8551+
struct GenTreeOpCC final : public GenTreeOp
8552+
{
8553+
GenCondition gtCondition;
8554+
8555+
GenTreeOpCC(genTreeOps oper, var_types type, GenCondition condition, GenTree* op1 = nullptr, GenTree* op2 = nullptr)
8556+
: GenTreeOp(oper, type, op1, op2 DEBUGARG(/*largeNode*/ FALSE)), gtCondition(condition)
8557+
{
8558+
assert(OperIs(GT_SELECTCC));
8559+
}
8560+
8561+
#if DEBUGGABLE_GENTREE
8562+
GenTreeOpCC() : GenTreeOp()
8563+
{
8564+
}
8565+
#endif // DEBUGGABLE_GENTREE
8566+
};
8567+
85428568
//------------------------------------------------------------------------
85438569
// Deferred inline functions of GenTree -- these need the subtypes above to
85448570
// be defined already.

src/coreclr/jit/gtlist.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,6 @@ GTNODE(SUB_HI , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR)
185185
GTNODE(LSH_HI , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR)
186186
GTNODE(RSH_LO , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR)
187187

188-
// Variant of SELECT that reuses flags computed by a previous SELECT.
189-
GTNODE(SELECT_HI , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR)
190188
#endif // !defined(TARGET_64BIT)
191189

192190
#ifdef FEATURE_HW_INTRINSICS
@@ -234,16 +232,19 @@ GTNODE(CNEG_LT , GenTreeOp ,0,GTK_UNOP|DBK_NOTHIR) // Conditi
234232
GTNODE(CMP , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR)
235233
// Generate a test instruction; sets the CPU flags according to (a & b) and does not produce a value.
236234
GTNODE(TEST , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR)
235+
#ifdef TARGET_XARCH
236+
// The XARCH BT instruction. Like CMP, this sets the condition flags (CF to be precise) and does not produce a value.
237+
GTNODE(BT , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR))
238+
#endif
237239
// Makes a comparison and jump if the condition specified. Does not set flags.
238240
GTNODE(JCMP , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR)
239241
// Checks the condition flags and branch if the condition specified by GenTreeCC::gtCondition is true.
240242
GTNODE(JCC , GenTreeCC ,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR)
241243
// Checks the condition flags and produces 1 if the condition specified by GenTreeCC::gtCondition is true and 0 otherwise.
242244
GTNODE(SETCC , GenTreeCC ,0,GTK_LEAF|DBK_NOTHIR)
243-
#ifdef TARGET_XARCH
244-
// The XARCH BT instruction. Like CMP, this sets the condition flags (CF to be precise) and does not produce a value.
245-
GTNODE(BT , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR))
246-
#endif
245+
// Variant of SELECT that reuses flags computed by a previous node with the specified condition.
246+
GTNODE(SELECTCC , GenTreeCC ,0,GTK_BINOP|DBK_NOTHIR)
247+
247248

248249
//-----------------------------------------------------------------------------
249250
// Other nodes that look like unary/binary operators:

src/coreclr/jit/gtstructs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ GTSTRUCT_1(AllocObj , GT_ALLOCOBJ)
112112
GTSTRUCT_1(RuntimeLookup, GT_RUNTIMELOOKUP)
113113
GTSTRUCT_1(ArrAddr , GT_ARR_ADDR)
114114
GTSTRUCT_2(CC , GT_JCC, GT_SETCC)
115+
GTSTRUCT_1(OpCC , GT_SELECTCC)
115116
#if defined(TARGET_X86)
116117
GTSTRUCT_1(MultiRegOp , GT_MUL_LONG)
117118
#elif defined (TARGET_ARM)

0 commit comments

Comments
 (0)