@@ -4933,15 +4933,31 @@ GenTreeLclVarCommon* FlowGraphNaturalLoop::FindDef(unsigned lclNum)
49334933// otherwise false.
49344934//
49354935// Remarks:
4936- // The function guarantees that at all definitions of the iteration local,
4937- // the loop condition is reestablished before iteration continues. In other
4938- // words, if this function returns true the loop invariant is guaranteed to
4939- // be upheld in all blocks in the loop (but see below for establishing the
4940- // base case).
4941- //
4942- // Currently we do not validate that there is a zero-trip test ensuring the
4943- // condition is true, so it is up to the user to validate that. This is
4944- // normally done via GTF_RELOP_ZTT set by loop inversion.
4936+ // On a true return, the function guarantees that the loop invariant is true
4937+ // and maintained at all points within the loop, except possibly right after
4938+ // the update of the iterator variable (NaturalLoopIterInfo::IterTree). The
4939+ // function guarantees that the test (NaturalLoopIterInfo::TestTree) occurs
4940+ // immediately after the update, so no IR in the loop is executed without the
4941+ // loop invariant being true, except for the test.
4942+ //
4943+ // The loop invariant is defined as the expression obtained by
4944+ // [info->IterVar] [info->TestOper()] [info->Limit()]. Note that
4945+ // [info->TestTree()] may not be of this form; it could for instance have the
4946+ // iterator variable as the second operand. However,
4947+ // [NaturalLoopIterInfo::TestOper()] will automatically normalize the test
4948+ // oper so that the invariant is equivalent to the returned form that has the
4949+ // iteration variable as op1 and the limit as op2.
4950+ //
4951+ // The limit can be further decomposed via NaturalLoopIterInfo::ConstLimit,
4952+ // ::VarLimit and ::ArrLenLimit.
4953+ //
4954+ // As an example, if info->IterVar == V02, info->TestOper() == GT_LT and
4955+ // info->ConstLimit() == 10, then the function guarantees that the value of
4956+ // the local V02 is less than 10 everywhere within the loop (except possibly
4957+ // at the test).
4958+ //
4959+ // In some cases we also know the initial value on entry to the loop; see
4960+ // ::HasConstInit and ::ConstInitValue.
49454961//
49464962bool FlowGraphNaturalLoop::AnalyzeIteration (NaturalLoopIterInfo* info)
49474963{
@@ -4972,8 +4988,15 @@ bool FlowGraphNaturalLoop::AnalyzeIteration(NaturalLoopIterInfo* info)
49724988 return false ;
49734989 }
49744990
4975- info->TestBlock = cond;
4976- info->IterVar = comp->optIsLoopIncrTree (info->IterTree );
4991+ assert (ContainsBlock (cond->GetTrueTarget ()) != ContainsBlock (cond->GetFalseTarget ()));
4992+
4993+ info->TestBlock = cond;
4994+ info->IterVar = comp->optIsLoopIncrTree (info->IterTree );
4995+ info->ExitedOnTrue = !ContainsBlock (cond->GetTrueTarget ());
4996+
4997+ // TODO-CQ: Currently, since we only look at the lexically bottom most block all loops have
4998+ // ExitedOnTrue == false. Once we recognize more structures this can be true.
4999+ assert (!info->ExitedOnTrue );
49775000
49785001 assert (info->IterVar != BAD_VAR_NUM);
49795002 LclVarDsc* const iterVarDsc = comp->lvaGetDesc (info->IterVar );
@@ -5027,13 +5050,20 @@ bool FlowGraphNaturalLoop::AnalyzeIteration(NaturalLoopIterInfo* info)
50275050 return false ;
50285051 }
50295052
5053+ if (!CheckLoopConditionBaseCase (initBlock, info))
5054+ {
5055+ JITDUMP (" Loop condition is not always true\n " );
5056+ return false ;
5057+ }
5058+
50305059#ifdef DEBUG
50315060 if (comp->verbose )
50325061 {
50335062 printf (" IterVar = V%02u\n " , info->IterVar );
50345063
50355064 if (info->HasConstInit )
5036- printf (" Const init with value %d in " FMT_BB " \n " , info->ConstInitValue , info->InitBlock ->bbNum );
5065+ printf (" Const init with value %d (at [%06u])\n " , info->ConstInitValue ,
5066+ Compiler::dspTreeID (info->InitTree ));
50375067
50385068 printf (" Test is [%06u] (" , Compiler::dspTreeID (info->TestTree ));
50395069 if (info->HasConstLimit )
@@ -5075,7 +5105,7 @@ void FlowGraphNaturalLoop::MatchInit(NaturalLoopIterInfo* info, BasicBlock* init
50755105
50765106 info->HasConstInit = true ;
50775107 info->ConstInitValue = (int )initValue->AsIntCon ()->IconValue ();
5078- info->InitBlock = initBlock ;
5108+ INDEBUG ( info->InitTree = init) ;
50795109}
50805110
50815111// ------------------------------------------------------------------------
@@ -5118,12 +5148,12 @@ bool FlowGraphNaturalLoop::MatchLimit(NaturalLoopIterInfo* info, GenTree* test)
51185148 GenTree* limitOp;
51195149
51205150 // Make sure op1 or op2 is the iterVar.
5121- if (opr1->gtOper == GT_LCL_VAR && opr1->AsLclVarCommon ()->GetLclNum () == info->IterVar )
5151+ if (opr1->OperIsScalarLocal () && ( opr1->AsLclVarCommon ()->GetLclNum () == info->IterVar ) )
51225152 {
51235153 iterOp = opr1;
51245154 limitOp = opr2;
51255155 }
5126- else if (opr2->gtOper == GT_LCL_VAR && opr2->AsLclVarCommon ()->GetLclNum () == info->IterVar )
5156+ else if (opr2->OperIsScalarLocal () && ( opr2->AsLclVarCommon ()->GetLclNum () == info->IterVar ) )
51275157 {
51285158 iterOp = opr2;
51295159 limitOp = opr1;
@@ -5138,11 +5168,8 @@ bool FlowGraphNaturalLoop::MatchLimit(NaturalLoopIterInfo* info, GenTree* test)
51385168 return false ;
51395169 }
51405170
5141- // Mark the iterator node.
5142- iterOp->gtFlags |= GTF_VAR_ITERATOR;
5143-
51445171 // Check what type of limit we have - constant, variable or arr-len.
5145- if (limitOp->gtOper == GT_CNS_INT )
5172+ if (limitOp->IsCnsIntOrI () )
51465173 {
51475174 info->HasConstLimit = true ;
51485175 if ((limitOp->gtFlags & GTF_ICON_SIMD_COUNT) != 0 )
@@ -5212,6 +5239,105 @@ bool FlowGraphNaturalLoop::MatchLimit(NaturalLoopIterInfo* info, GenTree* test)
52125239 return true ;
52135240}
52145241
5242+ // ------------------------------------------------------------------------
5243+ // EvaluateRelop: Evaluate a relational operator with constant arguments.
5244+ //
5245+ // Parameters:
5246+ // op1 - First operand
5247+ // op2 - Second operand
5248+ // oper - Operator
5249+ //
5250+ // Returns:
5251+ // Result.
5252+ //
5253+ template <typename T>
5254+ bool FlowGraphNaturalLoop::EvaluateRelop (T op1, T op2, genTreeOps oper)
5255+ {
5256+ switch (oper)
5257+ {
5258+ case GT_EQ:
5259+ return op1 == op2;
5260+
5261+ case GT_NE:
5262+ return op1 != op2;
5263+
5264+ case GT_LT:
5265+ return op1 < op2;
5266+
5267+ case GT_LE:
5268+ return op1 <= op2;
5269+
5270+ case GT_GT:
5271+ return op1 > op2;
5272+
5273+ case GT_GE:
5274+ return op1 >= op2;
5275+
5276+ default :
5277+ unreached ();
5278+ }
5279+ }
5280+
5281+ // ------------------------------------------------------------------------
5282+ // CheckLoopConditionBaseCase: Verify that the loop condition is true when the
5283+ // loop is entered (or validated immediately on entry).
5284+ //
5285+ // Returns:
5286+ // True if we could prove that the condition is true at all interesting
5287+ // points in the loop.
5288+ //
5289+ // Remarks:
5290+ // Currently handles the following cases:
5291+ // * The condition being trivially true in the first iteration (e.g. for (int i = 0; i < 3; i++))
5292+ // * The condition is checked before entry (often due to loop inversion)
5293+ //
5294+ bool FlowGraphNaturalLoop::CheckLoopConditionBaseCase (BasicBlock* initBlock, NaturalLoopIterInfo* info)
5295+ {
5296+ // TODO: A common loop idiom is to enter the loop at the test, with the
5297+ // unique in-loop predecessor of the header block being the increment. We
5298+ // currently do not handle these patterns in `optExtractInitTestIncr`.
5299+ // Instead we depend on loop inversion to put them into an
5300+ // if (x) { do { ... } while (x) }
5301+ // form. Once we handle the pattern in `optExtractInitTestIncr` we can
5302+ // handle it here by checking for whether the test is the header and first
5303+ // thing in the header.
5304+
5305+ // Is it trivially true?
5306+ if (info->HasConstInit && info->HasConstLimit )
5307+ {
5308+ int initVal = info->ConstInitValue ;
5309+ int limitVal = info->ConstLimit ();
5310+
5311+ assert (genActualType (info->TestTree ->gtGetOp1 ()) == TYP_INT);
5312+
5313+ bool isTriviallyTrue = info->TestTree ->IsUnsigned ()
5314+ ? EvaluateRelop<uint32_t >((uint32_t )initVal, (uint32_t )limitVal, info->TestOper ())
5315+ : EvaluateRelop<int32_t >(initVal, limitVal, info->TestOper ());
5316+
5317+ if (isTriviallyTrue)
5318+ {
5319+ JITDUMP (" Condition is trivially true on entry (%d %s%s %d)\n " , initVal,
5320+ info->TestTree ->IsUnsigned () ? " (uns)" : " " , GenTree::OpName (info->TestOper ()), limitVal);
5321+ return true ;
5322+ }
5323+ }
5324+
5325+ // Do we have a zero-trip test?
5326+ if (initBlock->KindIs (BBJ_COND))
5327+ {
5328+ GenTree* enteringJTrue = initBlock->lastStmt ()->GetRootNode ();
5329+ assert (enteringJTrue->OperIs (GT_JTRUE));
5330+ if (enteringJTrue->gtGetOp1 ()->OperIsCompare () && ((enteringJTrue->gtGetOp1 ()->gtFlags & GTF_RELOP_ZTT) != 0 ))
5331+ {
5332+ JITDUMP (" Condition is established before entry at [%06u]\n " ,
5333+ Compiler::dspTreeID (enteringJTrue->gtGetOp1 ()));
5334+ return true ;
5335+ }
5336+ }
5337+
5338+ return false ;
5339+ }
5340+
52155341// ------------------------------------------------------------------------
52165342// GetLexicallyTopMostBlock: Get the lexically top-most block contained within
52175343// the loop.
@@ -5340,7 +5466,7 @@ var_types NaturalLoopIterInfo::IterOperType()
53405466//
53415467bool NaturalLoopIterInfo::IsReversed ()
53425468{
5343- return TestTree->gtGetOp2 ()->OperIs (GT_LCL_VAR ) && (( TestTree->gtGetOp2 ()->gtFlags & GTF_VAR_ITERATOR) != 0 );
5469+ return TestTree->gtGetOp2 ()->OperIsScalarLocal ( ) && (TestTree->gtGetOp2 ()->AsLclVar ()-> GetLclNum () == IterVar );
53445470}
53455471
53465472// ------------------------------------------------------------------------
@@ -5353,7 +5479,15 @@ bool NaturalLoopIterInfo::IsReversed()
53535479genTreeOps NaturalLoopIterInfo::TestOper ()
53545480{
53555481 genTreeOps op = TestTree->OperGet ();
5356- return IsReversed () ? GenTree::SwapRelop (op) : op;
5482+ if (IsReversed ())
5483+ {
5484+ op = GenTree::SwapRelop (op);
5485+ }
5486+ if (ExitedOnTrue)
5487+ {
5488+ op = GenTree::ReverseRelop (op);
5489+ }
5490+ return op;
53575491}
53585492
53595493// ------------------------------------------------------------------------
0 commit comments