@@ -5029,7 +5029,7 @@ bool FlowGraphNaturalLoop::AnalyzeIteration(NaturalLoopIterInfo* info)
50295029
50305030 if (!CheckLoopConditionBaseCase (initBlock, info))
50315031 {
5032- JITDUMP (" Loop condition is not always true\n " );
5032+ JITDUMP (" Loop condition may not be true on the first iteration \n " );
50335033 return false ;
50345034 }
50355035
@@ -5263,11 +5263,10 @@ bool FlowGraphNaturalLoop::EvaluateRelop(T op1, T op2, genTreeOps oper)
52635263
52645264// ------------------------------------------------------------------------
52655265// CheckLoopConditionBaseCase: Verify that the loop condition is true when the
5266- // loop is entered (or validated immediately on entry) .
5266+ // loop is entered.
52675267//
52685268// Returns:
5269- // True if we could prove that the condition is true at all interesting
5270- // points in the loop.
5269+ // True if we could prove that the condition is true on entry.
52715270//
52725271// Remarks:
52735272// Currently handles the following cases:
@@ -5306,18 +5305,137 @@ bool FlowGraphNaturalLoop::CheckLoopConditionBaseCase(BasicBlock* initBlock, Nat
53065305 }
53075306
53085307 // Do we have a zero-trip test?
5309- if (initBlock->KindIs (BBJ_COND))
5308+ if (initBlock->KindIs (BBJ_COND) && IsZeroTripTest (initBlock, info) )
53105309 {
5311- GenTree* enteringJTrue = initBlock->lastStmt ()->GetRootNode ();
5312- assert (enteringJTrue->OperIs (GT_JTRUE));
5313- if (enteringJTrue->gtGetOp1 ()->OperIsCompare () && ((enteringJTrue->gtGetOp1 ()->gtFlags & GTF_RELOP_ZTT) != 0 ))
5310+ return true ;
5311+ }
5312+
5313+ return false ;
5314+ }
5315+
5316+ // ------------------------------------------------------------------------
5317+ // IsZeroTripTest: Check whether `initBlock`, a BBJ_COND block that enters the
5318+ // loop in one case and not in the other, implies that the loop invariant is
5319+ // true on entry.
5320+ //
5321+ // Returns:
5322+ // True if we could prove that the loop invariant is true on entry through
5323+ // "initBlock".
5324+ //
5325+ bool FlowGraphNaturalLoop::IsZeroTripTest (BasicBlock* initBlock, NaturalLoopIterInfo* info)
5326+ {
5327+ assert (initBlock->KindIs (BBJ_COND));
5328+ GenTree* enteringJTrue = initBlock->lastStmt ()->GetRootNode ();
5329+ assert (enteringJTrue->OperIs (GT_JTRUE));
5330+ GenTree* relop = enteringJTrue->gtGetOp1 ();
5331+ if (!relop->OperIsCmpCompare ())
5332+ {
5333+ return false ;
5334+ }
5335+
5336+ // Technically optExtractInitTestIncr only handles the "false"
5337+ // entry case, and preheader creation should ensure that that's the
5338+ // only time we'll see a BBJ_COND init block. However, it does not
5339+ // hurt to let this logic be correct by construction.
5340+ bool enterOnTrue = InitBlockEntersLoopOnTrue (initBlock);
5341+
5342+ JITDUMP (" Init block " FMT_BB " enters the loop when condition [%06u] evaluates to %s\n " , initBlock->bbNum ,
5343+ Compiler::dspTreeID (relop), enterOnTrue ? " true" : " false" );
5344+
5345+ GenTree* limitCandidate;
5346+ genTreeOps oper;
5347+
5348+ if (relop->gtGetOp1 ()->OperIsScalarLocal () && (relop->gtGetOp1 ()->AsLclVarCommon ()->GetLclNum () == info->IterVar ))
5349+ {
5350+ JITDUMP (" op1 is the iteration variable\n " );
5351+ oper = relop->gtOper ;
5352+ limitCandidate = relop->gtGetOp2 ();
5353+ }
5354+ else if (relop->gtGetOp2 ()->OperIsScalarLocal () &&
5355+ (relop->gtGetOp2 ()->AsLclVarCommon ()->GetLclNum () == info->IterVar ))
5356+ {
5357+ JITDUMP (" op2 is the iteration variable\n " );
5358+ oper = GenTree::SwapRelop (relop->gtOper );
5359+ limitCandidate = relop->gtGetOp1 ();
5360+ }
5361+ else
5362+ {
5363+ JITDUMP (" Relop does not involve iteration variable\n " );
5364+ return false ;
5365+ }
5366+
5367+ if (!enterOnTrue)
5368+ {
5369+ oper = GenTree::ReverseRelop (oper);
5370+ }
5371+
5372+ // Here we want to prove that [iterVar] [oper] [limitCandidate] implies
5373+ // [iterVar] [info->IterOper()] [info->Limit()]. Currently we just do the
5374+ // simple exact checks, but this could be improved. Note that using
5375+ // `GenTree::Compare` for the limits is ok for a "same value" check for the
5376+ // limited shapes of limits we recognize.
5377+ //
5378+ if ((relop->IsUnsigned () != info->TestTree ->IsUnsigned ()) || (oper != info->TestOper ()) ||
5379+ !GenTree::Compare (limitCandidate, info->Limit ()))
5380+ {
5381+ JITDUMP (" Condition guarantees V%02u %s%s [%06u], but invariant requires V%02u %s%s [%06u]\n " , info->IterVar ,
5382+ relop->IsUnsigned () ? " (uns) " : " " , GenTree::OpName (oper), Compiler::dspTreeID (limitCandidate),
5383+ info->IterVar , info->TestTree ->IsUnsigned () ? " (uns) " : " " , GenTree::OpName (info->TestOper ()),
5384+ Compiler::dspTreeID (info->Limit ()));
5385+ return false ;
5386+ }
5387+
5388+ JITDUMP (" Condition is established before entry at [%06u]\n " , Compiler::dspTreeID (enteringJTrue->gtGetOp1 ()));
5389+ return true ;
5390+ }
5391+
5392+ // ------------------------------------------------------------------------
5393+ // InitBlockEntersLoopOnTrue: Determine whether a BBJ_COND init block enters the
5394+ // loop in the false or true case.
5395+ //
5396+ // Parameters:
5397+ // initBlock - A BBJ_COND block that is assumed to dominate the loop, and
5398+ // only enter the loop in one of the two cases.
5399+ //
5400+ // Returns:
5401+ // True if the loop is entered if the condition evaluates to true; otherwise false.
5402+ //
5403+ // Remarks:
5404+ // Handles only limited cases (optExtractInitTestIncr ensures that we see
5405+ // only limited cases).
5406+ //
5407+ bool FlowGraphNaturalLoop::InitBlockEntersLoopOnTrue (BasicBlock* initBlock)
5408+ {
5409+ assert (initBlock->KindIs (BBJ_COND));
5410+
5411+ if (initBlock->FalseTargetIs (GetHeader ()))
5412+ {
5413+ return false ;
5414+ }
5415+
5416+ if (initBlock->TrueTargetIs (GetHeader ()))
5417+ {
5418+ return true ;
5419+ }
5420+
5421+ // `optExtractInitTestIncr` may look at preds of preds to find an init
5422+ // block, so try a little bit harder. Today this always happens since we
5423+ // always have preheaders created in the places we call
5424+ // FlowGraphNaturalLoop::AnalyzeIteration.
5425+ for (FlowEdge* enterEdge : EntryEdges ())
5426+ {
5427+ BasicBlock* entering = enterEdge->getSourceBlock ();
5428+ if (initBlock->FalseTargetIs (entering))
5429+ {
5430+ return false ;
5431+ }
5432+ if (initBlock->TrueTargetIs (entering))
53145433 {
5315- JITDUMP (" Condition is established before entry at [%06u]\n " ,
5316- Compiler::dspTreeID (enteringJTrue->gtGetOp1 ()));
53175434 return true ;
53185435 }
53195436 }
53205437
5438+ assert (!" Could not find init block enter side" );
53215439 return false ;
53225440}
53235441
0 commit comments