}
break;
+ case GT_JCC:
+ {
+ GenTreeJumpCC* jcc = treeNode->AsJumpCC();
+
+ assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
+
+ CompareKind compareKind = ((jcc->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
+ emitJumpKind jumpKind = genJumpKindForOper(jcc->gtCondition, compareKind);
+
+ inst_JMP(jumpKind, compiler->compCurBB->bbJumpDest);
+ }
+ break;
+
case GT_RETURNTRAP:
{
// this is nothing but a conditional call to CORINFO_HELP_STOP_FOR_GC
void fgUnreachableBlock(BasicBlock* block);
- void fgRemoveJTrue(BasicBlock* block);
+ void fgRemoveConditionalJump(BasicBlock* block);
BasicBlock* fgLastBBInMainFunction();
switch (oper)
{
case GT_JTRUE:
+ case GT_JCC:
case GT_SWITCH:
case GT_LABEL:
/*****************************************************************************************************
*
- * Function called to remove or morph a GT_JTRUE statement when we jump to the same
+ * Function called to remove or morph a jump when we jump to the same
* block when both the condition is true or false.
*/
-void Compiler::fgRemoveJTrue(BasicBlock* block)
+void Compiler::fgRemoveConditionalJump(BasicBlock* block)
{
noway_assert(block->bbJumpKind == BBJ_COND && block->bbJumpDest == block->bbNext);
assert(compRationalIRForm == block->IsLIR());
LIR::Range& blockRange = LIR::AsRange(block);
GenTree* test = blockRange.LastNode();
- assert(test->OperGet() == GT_JTRUE);
+ assert(test->OperIsConditionalJump());
bool isClosed;
unsigned sideEffects;
{
test->gtStmtExpr = sideEffList;
- fgMorphBlockStmt(block, test DEBUGARG("fgRemoveJTrue"));
+ fgMorphBlockStmt(block, test DEBUGARG("fgRemoveConditionalJump"));
}
}
}
// Make sure we are replacing "block" with "succBlock" in predBlock->bbJumpDest.
noway_assert(predBlock->bbJumpDest == block);
predBlock->bbJumpDest = succBlock;
- fgRemoveJTrue(predBlock);
+ fgRemoveConditionalJump(predBlock);
break;
}
/* Check for branch to next block */
if (bPrev->bbJumpDest == bPrev->bbNext)
{
- fgRemoveJTrue(bPrev);
+ fgRemoveConditionalJump(bPrev);
}
break;
{
LIR::Range& blockRange = LIR::AsRange(block);
GenTree* jmp = blockRange.LastNode();
- assert(jmp->OperGet() == GT_JTRUE);
+ assert(jmp->OperIsConditionalJump());
bool isClosed;
unsigned sideEffects;
/* Reverse the jump condition */
GenTree* test = block->lastNode();
- noway_assert(test->gtOper == GT_JTRUE);
+ noway_assert(test->OperIsConditionalJump());
- GenTree* cond = gtReverseCond(test->gtOp.gtOp1);
- assert(cond == test->gtOp.gtOp1); // Ensure `gtReverseCond` did not create a new node.
- test->gtOp.gtOp1 = cond;
+ if (test->OperGet() == GT_JTRUE)
+ {
+ GenTree* cond = gtReverseCond(test->gtOp.gtOp1);
+ assert(cond == test->gtOp.gtOp1); // Ensure `gtReverseCond` did not create a new node.
+ test->gtOp.gtOp1 = cond;
+ }
+ else
+ {
+ gtReverseCond(test);
+ }
// Optimize the Conditional JUMP to go to the new target
block->bbJumpDest = bNext->bbJumpDest;
// If the block is a BBJ_COND, a BBJ_SWITCH or a
// lowered GT_SWITCH_TABLE node then make sure it
- // ends with a GT_JTRUE or a GT_SWITCH
+ // ends with a jump or a GT_SWITCH
if (block->bbJumpKind == BBJ_COND)
{
- noway_assert(block->lastNode()->gtNext == nullptr && block->lastNode()->gtOper == GT_JTRUE);
+ noway_assert(block->lastNode()->gtNext == nullptr && block->lastNode()->OperIsConditionalJump());
}
else if (block->bbJumpKind == BBJ_SWITCH)
{
tree->gtFlags ^= GTF_RELOP_NAN_UN;
}
}
+ else if (tree->OperGet() == GT_JCC)
+ {
+ GenTreeJumpCC* jcc = tree->AsJumpCC();
+ jcc->gtCondition = GenTree::ReverseRelop(jcc->gtCondition);
+ }
else
{
tree = gtNewOperNode(GT_NOT, TYP_INT, tree);
}
break;
+ case GT_JCC:
+ printf(" cond=%s", GenTree::NodeName(tree->AsJumpCC()->gtCondition));
+ break;
+
default:
assert(!"don't know how to display tree leaf node");
}
{
case GT_STOREIND:
case GT_JTRUE:
+ case GT_JCC:
case GT_RETURN:
case GT_RETFILT:
case GT_STORE_LCL_FLD:
return (gtOper == GT_LIST) && ((gtFlags & GTF_LIST_AGGREGATE) != 0);
}
+ bool OperIsConditionalJump()
+ {
+ return (gtOper == GT_JTRUE) || (gtOper == GT_JCC);
+ }
+
// Requires that "op" is an op= operator. Returns
// the corresponding "op".
static genTreeOps OpAsgToOper(genTreeOps op);
#endif
};
+
+struct GenTreeJumpCC final : public GenTree
+{
+ genTreeOps gtCondition; // any relop
+
+ GenTreeJumpCC(genTreeOps condition)
+ : GenTree(GT_JCC, TYP_VOID DEBUGARG(/*largeNode*/ FALSE))
+ , gtCondition(condition)
+ {
+ assert(OperIsCompare(condition));
+ }
+
+#if DEBUGGABLE_GENTREE
+ GenTreeJumpCC()
+ : GenTree()
+ {
+ }
+#endif // DEBUGGABLE_GENTREE
+};
+
//------------------------------------------------------------------------
// Deferred inline functions of GenTree -- these need the subtypes above to
// be defined already.
//-----------------------------------------------------------------------------
GTNODE(JTRUE , "jmpTrue" ,0,GTK_UNOP|GTK_NOVALUE)
+GTNODE(JCC , "jcc" ,0,GTK_LEAF|GTK_NOVALUE)
GTNODE(LIST , "<list>" ,0,GTK_BINOP)
GTSTRUCT_1(SIMD , GT_SIMD)
#endif // FEATURE_SIMD
GTSTRUCT_1(AllocObj , GT_ALLOCOBJ)
+GTSTRUCT_1(JumpCC , GT_JCC)
/*****************************************************************************/
#undef GTSTRUCT_0
#undef GTSTRUCT_1
switch (block->bbJumpKind)
{
case BBJ_COND:
- assert(insertionPoint->OperGet() == GT_JTRUE);
+ assert(insertionPoint->OperIsConditionalJump());
break;
case BBJ_SWITCH:
unsigned weight = m_block->getBBWeight(comp);
LIR::Use loSrc1(BlockRange(), &(src1->gtOp.gtOp1), src1);
- LIR::Use hiSrc1(BlockRange(), &(src1->gtOp.gtOp2), src1);
LIR::Use loSrc2(BlockRange(), &(src2->gtOp.gtOp1), src2);
- LIR::Use hiSrc2(BlockRange(), &(src2->gtOp.gtOp2), src2);
if (loSrc1.Def()->OperGet() != GT_CNS_INT && loSrc1.Def()->OperGet() != GT_LCL_VAR)
{
loSrc1.ReplaceWithLclVar(comp, weight);
}
- if (hiSrc1.Def()->OperGet() != GT_CNS_INT && hiSrc1.Def()->OperGet() != GT_LCL_VAR)
- {
- hiSrc1.ReplaceWithLclVar(comp, weight);
- }
-
if (loSrc2.Def()->OperGet() != GT_CNS_INT && loSrc2.Def()->OperGet() != GT_LCL_VAR)
{
loSrc2.ReplaceWithLclVar(comp, weight);
}
-
- if (hiSrc2.Def()->OperGet() != GT_CNS_INT && hiSrc2.Def()->OperGet() != GT_LCL_VAR)
- {
- hiSrc2.ReplaceWithLclVar(comp, weight);
- }
BasicBlock* jumpDest = m_block->bbJumpDest;
BasicBlock* nextDest = m_block->bbNext;
BasicBlock* newBlock = comp->fgSplitBlockAtEnd(m_block);
cmp->gtType = TYP_INT;
- cmp->gtOp.gtOp1 = hiSrc1.Def();
- cmp->gtOp.gtOp2 = hiSrc2.Def();
+ cmp->gtOp.gtOp1 = src1->gtOp.gtOp2;
+ cmp->gtOp.gtOp2 = src2->gtOp.gtOp2;
if (cmp->OperGet() == GT_EQ || cmp->OperGet() == GT_NE)
{
BlockRange().Remove(loSrc2.Def());
GenTree* loCmp = comp->gtNewOperNode(cmp->OperGet(), TYP_INT, loSrc1.Def(), loSrc2.Def());
loCmp->gtFlags = cmp->gtFlags;
- GenTree* loJcc = comp->gtNewOperNode(GT_JTRUE, TYP_VOID, loCmp);
- LIR::AsRange(newBlock).InsertAfter(nullptr, loSrc1.Def(), loSrc2.Def(), loCmp, loJcc);
+ GenTree* loJtrue = comp->gtNewOperNode(GT_JTRUE, TYP_VOID, loCmp);
+ LIR::AsRange(newBlock).InsertAfter(nullptr, loSrc1.Def(), loSrc2.Def(), loCmp, loJtrue);
m_block->bbJumpKind = BBJ_COND;
BasicBlock* newBlock2 = comp->fgSplitBlockAtEnd(newBlock);
- GenTree* hiSrc1Def = comp->gtClone(hiSrc1.Def());
- GenTree* hiSrc2Def = comp->gtClone(hiSrc2.Def());
- GenTree* hiCmp = comp->gtNewOperNode(hiCmpOper, TYP_INT, hiSrc1Def, hiSrc2Def);
- hiCmp->gtFlags = cmp->gtFlags;
- GenTree* hiJcc = comp->gtNewOperNode(GT_JTRUE, TYP_VOID, hiCmp);
- LIR::AsRange(newBlock).InsertAfter(nullptr, hiSrc1Def, hiSrc2Def, hiCmp, hiJcc);
+ GenTree* hiJcc = new (comp, GT_JCC) GenTreeJumpCC(hiCmpOper);
+ hiJcc->gtFlags = cmp->gtFlags;
+ LIR::AsRange(newBlock).InsertAfter(nullptr, hiJcc);
BlockRange().Remove(loSrc1.Def());
BlockRange().Remove(loSrc2.Def());
GenTree* loCmp = comp->gtNewOperNode(loCmpOper, TYP_INT, loSrc1.Def(), loSrc2.Def());
loCmp->gtFlags = cmp->gtFlags | GTF_UNSIGNED;
- GenTree* loJcc = comp->gtNewOperNode(GT_JTRUE, TYP_VOID, loCmp);
- LIR::AsRange(newBlock2).InsertAfter(nullptr, loSrc1.Def(), loSrc2.Def(), loCmp, loJcc);
+ GenTree* loJtrue = comp->gtNewOperNode(GT_JTRUE, TYP_VOID, loCmp);
+ LIR::AsRange(newBlock2).InsertAfter(nullptr, loSrc1.Def(), loSrc2.Def(), loCmp, loJtrue);
m_block->bbJumpKind = BBJ_COND;
m_block->bbJumpDest = nextDest;
l->clearDstCount(tree->gtOp.gtOp1);
break;
+ case GT_JCC:
+ info->srcCount = 0;
+ info->dstCount = 0;
+ break;
+
case GT_JMP:
info->srcCount = 0;
info->dstCount = 0;
noway_assert(!blockRange.IsEmpty());
GenTree* branch = blockRange.LastNode();
- assert(branch->OperGet() == GT_JTRUE || branch->OperGet() == GT_SWITCH_TABLE ||
+ assert(branch->OperIsConditionalJump() || branch->OperGet() == GT_SWITCH_TABLE ||
branch->OperGet() == GT_SWITCH);
blockRange.InsertBefore(branch, std::move(treeRange));
noway_assert(!blockRange.IsEmpty());
GenTree* branch = blockRange.LastNode();
- assert(branch->OperGet() == GT_JTRUE || branch->OperGet() == GT_SWITCH_TABLE ||
+ assert(branch->OperIsConditionalJump() || branch->OperGet() == GT_SWITCH_TABLE ||
branch->OperGet() == GT_SWITCH);
blockRange.InsertBefore(branch, std::move(swapRange));