@@ -532,6 +532,96 @@ bool IsConstantRangeTest(GenTreeOp* tree, GenTree** varNode, GenTreeIntCon** cns
532
532
return false ;
533
533
}
534
534
535
+ // ------------------------------------------------------------------------------
536
+ // FoldRangeTests: Given two compare nodes (cmp1 && cmp2) where cmp1 is X >= 0
537
+ // and cmp2 is X < NN (NeverNegative), try to fold the range test into a single
538
+ // X u< NN (unsigned) compare node.
539
+ //
540
+ // Arguments:
541
+ // compiler - compiler instance
542
+ // cmp1 - first compare node
543
+ // cmp1IsReversed - true if cmp1 is in fact reversed
544
+ // cmp2 - second compare node
545
+ // cmp2IsReversed - true if cmp2 is in fact reversed
546
+ //
547
+ // Returns:
548
+ // true if cmp1 now represents the folded range check and cmp2 can be removed.
549
+ //
550
+ bool FoldNeverNegativeRangeTest (
551
+ Compiler* comp, GenTreeOp* cmp1, bool cmp1IsReversed, GenTreeOp* cmp2, bool cmp2IsReversed)
552
+ {
553
+ GenTree* var1Node;
554
+ GenTreeIntCon* cns1Node;
555
+ genTreeOps cmp1Op;
556
+
557
+ // First cmp has to be "X >= 0" (or "0 <= X")
558
+ // TODO: handle "X < NN && X >= 0" (where the 2nd comparison is the lower bound)
559
+ // It seems to be a rare case, so we don't handle it for now.
560
+ if (!IsConstantRangeTest (cmp1, &var1Node, &cns1Node, &cmp1Op))
561
+ {
562
+ return false ;
563
+ }
564
+
565
+ // Now, reverse the comparison if necessary depending on cmp1IsReversed and cmp2IsReversed
566
+ // so we'll get a canonical form of "X >= 0 && X </<= NN"
567
+ cmp1Op = cmp1IsReversed ? GenTree::ReverseRelop (cmp1Op) : cmp1Op;
568
+ genTreeOps cmp2Op = cmp2IsReversed ? GenTree::ReverseRelop (cmp2->OperGet ()) : cmp2->OperGet ();
569
+
570
+ if ((cmp1Op != GT_GE) || (!cns1Node->IsIntegralConst (0 )))
571
+ {
572
+ // Lower bound check has to be "X >= 0".
573
+ // We already re-ordered the comparison so that the constant is always on the right side.
574
+ return false ;
575
+ }
576
+
577
+ // Upper bound check has to be "X relop NN" or "NN relop X" (NN = NeverNegative)
578
+ // We allow var1Node to be a GT_COMMA node, so we need to call gtEffectiveVal() to get the actual variable
579
+ // since it's guaranteed to be evaluated first.
580
+ GenTree* upperBound;
581
+ if (cmp2->gtGetOp1 ()->OperIs (GT_LCL_VAR, GT_LCL_FLD) &&
582
+ GenTree::Compare (var1Node->gtEffectiveVal (), cmp2->gtGetOp1 ()))
583
+ {
584
+ // "X relop NN"
585
+ upperBound = cmp2->gtGetOp2 ();
586
+ }
587
+ else if (cmp2->gtGetOp2 ()->OperIs (GT_LCL_VAR, GT_LCL_FLD) &&
588
+ GenTree::Compare (var1Node->gtEffectiveVal (), cmp2->gtGetOp2 ()))
589
+ {
590
+ // "NN relop X"
591
+ upperBound = cmp2->gtGetOp1 ();
592
+ // Normalize to "X relop NN"
593
+ cmp2Op = GenTree::SwapRelop (cmp2Op);
594
+ }
595
+ else
596
+ {
597
+ return false ;
598
+ }
599
+
600
+ // Check that our upper bound is known to be never negative (e.g. GT_ARR_LENGTH or Span.Length, etc.)
601
+ if (!upperBound->IsNeverNegative (comp) || !upperBound->TypeIs (var1Node->TypeGet ()))
602
+ {
603
+ return false ;
604
+ }
605
+
606
+ if ((upperBound->gtFlags & GTF_SIDE_EFFECT) != 0 )
607
+ {
608
+ // We can't fold "X >= 0 && X < NN" to "X u< NN" if NN has side effects.
609
+ return false ;
610
+ }
611
+
612
+ if ((cmp2Op != GT_LT) && (cmp2Op != GT_LE))
613
+ {
614
+ // Upper bound check has to be "X < NN" or "X <= NN" (normalized form).
615
+ return false ;
616
+ }
617
+
618
+ cmp1->gtOp1 = var1Node;
619
+ cmp1->gtOp2 = upperBound;
620
+ cmp1->SetOper (cmp2IsReversed ? GenTree::ReverseRelop (cmp2Op) : cmp2Op);
621
+ cmp1->SetUnsigned ();
622
+ return true ;
623
+ }
624
+
535
625
// ------------------------------------------------------------------------------
536
626
// FoldRangeTests: Given two compare nodes (cmp1 && cmp2) that represent a range check,
537
627
// fold them into a single compare node if possible, e.g.:
@@ -560,12 +650,11 @@ bool FoldRangeTests(Compiler* comp, GenTreeOp* cmp1, bool cmp1IsReversed, GenTre
560
650
genTreeOps cmp2Op;
561
651
562
652
// Make sure both conditions are constant range checks, e.g. "X > CNS"
563
- // TODO: support more cases, e.g. "X >= 0 && X < array.Length" -> "(uint)X < array.Length"
564
- // Basically, we can use GenTree::IsNeverNegative() for it.
565
653
if (!IsConstantRangeTest (cmp1, &var1Node, &cns1Node, &cmp1Op) ||
566
654
!IsConstantRangeTest (cmp2, &var2Node, &cns2Node, &cmp2Op))
567
655
{
568
- return false ;
656
+ // Give FoldNeverNegativeRangeTest a try if both conditions are not constant range checks.
657
+ return FoldNeverNegativeRangeTest (comp, cmp1, cmp1IsReversed, cmp2, cmp2IsReversed);
569
658
}
570
659
571
660
// Reverse the comparisons if necessary so we'll get a canonical form "cond1 == true && cond2 == true" -> InRange.
0 commit comments