-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JIT: Establish loop invariant base case based on IR #97182
Changes from 4 commits
9fc3436
1e1bfe7
0603636
1c302fc
a7ca60a
67f2ae4
5115ace
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5030,7 +5030,7 @@ bool FlowGraphNaturalLoop::AnalyzeIteration(NaturalLoopIterInfo* info) | |
|
||
if (!CheckLoopConditionBaseCase(initBlock, info)) | ||
{ | ||
JITDUMP(" Loop condition is not always true\n"); | ||
JITDUMP(" Loop condition may not be true on the first iteration\n"); | ||
return false; | ||
} | ||
|
||
|
@@ -5264,11 +5264,10 @@ bool FlowGraphNaturalLoop::EvaluateRelop(T op1, T op2, genTreeOps oper) | |
|
||
//------------------------------------------------------------------------ | ||
// CheckLoopConditionBaseCase: Verify that the loop condition is true when the | ||
// loop is entered (or validated immediately on entry). | ||
// loop is entered. | ||
// | ||
// Returns: | ||
// True if we could prove that the condition is true at all interesting | ||
// points in the loop. | ||
// True if we could prove that the condition is true on entry. | ||
// | ||
// Remarks: | ||
// Currently handles the following cases: | ||
|
@@ -5307,18 +5306,130 @@ bool FlowGraphNaturalLoop::CheckLoopConditionBaseCase(BasicBlock* initBlock, Nat | |
} | ||
|
||
// Do we have a zero-trip test? | ||
if (initBlock->KindIs(BBJ_COND)) | ||
if (initBlock->KindIs(BBJ_COND) && IsZeroTripTest(initBlock, info)) | ||
{ | ||
GenTree* enteringJTrue = initBlock->lastStmt()->GetRootNode(); | ||
assert(enteringJTrue->OperIs(GT_JTRUE)); | ||
if (enteringJTrue->gtGetOp1()->OperIsCompare() && ((enteringJTrue->gtGetOp1()->gtFlags & GTF_RELOP_ZTT) != 0)) | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
//------------------------------------------------------------------------ | ||
// IsZeroTripTest: Check whether `initBlock`, a BBJ_COND block that enters the | ||
// loop in one case and not in the other, implies that the loop invariant is | ||
// true on entry. | ||
// | ||
// Returns: | ||
// True if we could prove that the loop invariant is true on entry through | ||
// "initBlock". | ||
// | ||
bool FlowGraphNaturalLoop::IsZeroTripTest(BasicBlock* initBlock, NaturalLoopIterInfo* info) | ||
{ | ||
assert(initBlock->KindIs(BBJ_COND)); | ||
GenTree* enteringJTrue = initBlock->lastStmt()->GetRootNode(); | ||
assert(enteringJTrue->OperIs(GT_JTRUE)); | ||
GenTree* relop = enteringJTrue->gtGetOp1(); | ||
if (!relop->OperIsCmpCompare()) | ||
{ | ||
return false; | ||
} | ||
|
||
// Technically optExtractInitTestIncr only handles the "false" | ||
// entry case, and preheader creation should ensure that that's the | ||
// only time we'll see a BBJ_COND init block. However, it does not | ||
// hurt to let this logic be correct by construction. | ||
bool enterSide = CondInitBlockEnterSide(initBlock); | ||
|
||
JITDUMP(" Init block " FMT_BB " enters the loop when condition [%06u] evaluates to %s\n", initBlock->bbNum, | ||
Compiler::dspTreeID(relop), enterSide ? "true" : "false"); | ||
|
||
GenTree* limitCandidate; | ||
genTreeOps oper; | ||
|
||
if (relop->gtGetOp1()->OperIsScalarLocal() && (relop->gtGetOp1()->AsLclVarCommon()->GetLclNum() == info->IterVar)) | ||
{ | ||
JITDUMP(" op1 is the iteration variable\n"); | ||
oper = relop->gtOper; | ||
limitCandidate = relop->gtGetOp2(); | ||
} | ||
else if (relop->gtGetOp2()->OperIsScalarLocal() && | ||
(relop->gtGetOp2()->AsLclVarCommon()->GetLclNum() == info->IterVar)) | ||
{ | ||
JITDUMP(" op2 is the iteration variable\n"); | ||
oper = GenTree::SwapRelop(relop->gtOper); | ||
limitCandidate = relop->gtGetOp1(); | ||
} | ||
else | ||
{ | ||
JITDUMP(" Relop does not involve iteration variable\n"); | ||
return false; | ||
} | ||
|
||
if (!enterSide) | ||
{ | ||
oper = GenTree::ReverseRelop(oper); | ||
} | ||
|
||
// Here we want to prove that [iterVar] [oper] [limitCandidate] implies | ||
// [iterVar] [info->IterOper()] [info->Limit()]. Currently we just do the | ||
// simple exact checks, but this could be improved. | ||
|
||
if ((relop->IsUnsigned() != info->TestTree->IsUnsigned()) || (oper != info->TestOper())) | ||
{ | ||
JITDUMP(" Condition guarantees V%02u %s%s [%06u], but invariant requires V%02u %s%s [%06u]\n", info->IterVar, | ||
relop->IsUnsigned() ? "(uns) " : "", GenTree::OpName(oper), Compiler::dspTreeID(limitCandidate), | ||
info->IterVar, info->TestTree->IsUnsigned() ? "(uns) " : "", GenTree::OpName(info->TestOper()), | ||
Compiler::dspTreeID(info->Limit())); | ||
return false; | ||
} | ||
|
||
JITDUMP(" Condition is established before entry at [%06u]\n", Compiler::dspTreeID(enteringJTrue->gtGetOp1())); | ||
return true; | ||
} | ||
|
||
//------------------------------------------------------------------------ | ||
// CondInitBlockEnterSide: Determine whether a BBJ_COND init block enters the | ||
// loop in the false or true case. | ||
// | ||
// Parameters: | ||
// initBlock - A BBJ_COND block that is assumed to dominate the loop, and | ||
// only enter the loop in one of the two cases. | ||
// | ||
// Returns: | ||
// True if the loop is entered if the condition evaluates to true; otherwise false. | ||
// | ||
// Remarks: | ||
// Handles only limited cases (optExtractInitTestIncr ensures that we see | ||
// only limited cases). | ||
// | ||
bool FlowGraphNaturalLoop::CondInitBlockEnterSide(BasicBlock* initBlock) | ||
{ | ||
assert(initBlock->KindIs(BBJ_COND)); | ||
|
||
if (initBlock->FalseTargetIs(GetHeader())) | ||
{ | ||
return false; | ||
} | ||
|
||
if (initBlock->TrueTargetIs(GetHeader())) | ||
{ | ||
return true; | ||
} | ||
|
||
for (FlowEdge* enterEdge : EntryEdges()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is handling a preheader, can you add a comment? Can do this in a follow-up. Also we'd expect that The check in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Currently I'll add a comment. I also just noticed that I forgot the equality check on the limits, so need to add that too. |
||
{ | ||
BasicBlock* entering = enterEdge->getSourceBlock(); | ||
if (initBlock->FalseTargetIs(entering)) | ||
{ | ||
return false; | ||
} | ||
if (initBlock->TrueTargetIs(entering)) | ||
{ | ||
JITDUMP(" Condition is established before entry at [%06u]\n", | ||
Compiler::dspTreeID(enteringJTrue->gtGetOp1())); | ||
return true; | ||
} | ||
} | ||
|
||
assert(!"Could not find init block enter side"); | ||
return false; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming nit -- from the name I thought initially this was checking for side entries into loops.
Maybe something like
InitBlockEntersLoopOnTrue
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed it.