@@ -637,29 +637,30 @@ void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP
637637}
638638
639639// ------------------------------------------------------------------------
640- // TryGetRangeFromAssertions : Cheaper version of TryGetRange that is based purely on assertions
640+ // GetRangeFromAssertions : Cheaper version of TryGetRange that is based purely on assertions
641641// and does not require a full range analysis based on SSA.
642642//
643643// Arguments:
644644// comp - the compiler instance
645645// num - the value number to analyze range for
646646// assertions - the assertions to use
647- // pRange - the range to tighten with assertions
648647//
649648// Return Value:
650- // True if the range was successfully computed
649+ // The computed range
651650//
652- bool RangeCheck::TryGetRangeFromAssertions (Compiler* comp, ValueNum num, ASSERT_VALARG_TP assertions, Range* pRange )
651+ Range RangeCheck::GetRangeFromAssertions (Compiler* comp, ValueNum num, ASSERT_VALARG_TP assertions, int budget )
653652{
654- assert (pRange != nullptr );
655- assert (pRange->LowerLimit ().IsUnknown ());
656- assert (pRange->UpperLimit ().IsUnknown ());
653+ // Start with the widest possible constant range.
654+ Range result = Range (Limit (Limit::keConstant, INT32_MIN), Limit (Limit::keConstant, INT32_MAX));
657655
658- if (num == ValueNumStore::NoVN)
656+ if (( num == ValueNumStore::NoVN) || (budget <= 0 ) )
659657 {
660- return false ;
658+ return result ;
661659 }
662660
661+ // Currently, we only handle int32 and smaller integer types.
662+ assert (varTypeIsInt (comp->vnStore ->TypeOfVN (num)) || varTypeIsSmall (comp->vnStore ->TypeOfVN (num)));
663+
663664 //
664665 // First, let's see if we can tighten the range based on VN information.
665666 //
@@ -668,14 +669,9 @@ bool RangeCheck::TryGetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_
668669 int cns;
669670 if (comp->vnStore ->IsVNIntegralConstant (num, &cns))
670671 {
671- pRange->lLimit = Limit (Limit::keConstant, cns);
672- pRange->uLimit = Limit (Limit::keConstant, cns);
673- return true ;
672+ return Range (Limit (Limit::keConstant, cns));
674673 }
675674
676- // Start with the widest possible constant range.
677- Range result = Range (Limit (Limit::keConstant, INT32_MIN), Limit (Limit::keConstant, INT32_MAX));
678-
679675 VNFuncApp funcApp;
680676 if (comp->vnStore ->GetVNFunc (num, &funcApp))
681677 {
@@ -718,6 +714,16 @@ bool RangeCheck::TryGetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_
718714 }
719715 break ;
720716
717+ case VNF_NEG:
718+ {
719+ Range r1 = GetRangeFromAssertions (comp, funcApp.m_args [0 ], assertions, --budget);
720+ Range unaryOpResult = RangeOps::Negate (r1);
721+
722+ // We can use the result only if it never overflows.
723+ result = unaryOpResult.IsConstantRange () ? unaryOpResult : result;
724+ break ;
725+ }
726+
721727 case VNF_LSH:
722728 case VNF_ADD:
723729 case VNF_MUL:
@@ -728,49 +734,41 @@ bool RangeCheck::TryGetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_
728734 case VNF_UMOD:
729735 {
730736 // Get ranges of both operands and perform the same operation on the ranges.
731- Range r1 = Range ( Limit (Limit::keUnknown) );
732- Range r2 = Range ( Limit (Limit::keUnknown) );
733- if ( TryGetRangeFromAssertions (comp, funcApp. m_args [ 0 ], assertions, &r1) &&
734- TryGetRangeFromAssertions (comp, funcApp.m_args [ 1 ], assertions, &r2) )
737+ Range r1 = GetRangeFromAssertions (comp, funcApp. m_args [ 0 ], assertions, --budget );
738+ Range r2 = GetRangeFromAssertions (comp, funcApp. m_args [ 1 ], assertions, --budget );
739+ Range binOpResult = Range ( Limit (Limit::keUnknown));
740+ switch ( funcApp.m_func )
735741 {
736- Range binOpResult = Range (Limit (Limit::keUnknown));
737- switch (funcApp.m_func )
738- {
739- case VNF_ADD:
740- binOpResult = RangeOps::Add (r1, r2);
741- break ;
742- case VNF_MUL:
743- binOpResult = RangeOps::Multiply (r1, r2);
744- break ;
745- case VNF_AND:
746- binOpResult = RangeOps::And (r1, r2);
747- break ;
748- case VNF_OR:
749- binOpResult = RangeOps::Or (r1, r2);
750- break ;
751- case VNF_LSH:
752- binOpResult = RangeOps::ShiftLeft (r1, r2);
753- break ;
754- case VNF_RSH:
755- binOpResult = RangeOps::ShiftRight (r1, r2, /* logical*/ false );
756- break ;
757- case VNF_RSZ:
758- binOpResult = RangeOps::ShiftRight (r1, r2, /* logical*/ true );
759- break ;
760- case VNF_UMOD:
761- binOpResult = RangeOps::UnsignedMod (r1, r2);
762- break ;
763- default :
764- unreached ();
765- }
766-
767- if (binOpResult.IsConstantRange ())
768- {
769- result = binOpResult;
770- }
771- // if the result is unknown (or may overflow), we'll just analyze the binop itself based on the
772- // assertions
742+ case VNF_ADD:
743+ binOpResult = RangeOps::Add (r1, r2);
744+ break ;
745+ case VNF_MUL:
746+ binOpResult = RangeOps::Multiply (r1, r2);
747+ break ;
748+ case VNF_AND:
749+ binOpResult = RangeOps::And (r1, r2);
750+ break ;
751+ case VNF_OR:
752+ binOpResult = RangeOps::Or (r1, r2);
753+ break ;
754+ case VNF_LSH:
755+ binOpResult = RangeOps::ShiftLeft (r1, r2);
756+ break ;
757+ case VNF_RSH:
758+ binOpResult = RangeOps::ShiftRight (r1, r2, /* logical*/ false );
759+ break ;
760+ case VNF_RSZ:
761+ binOpResult = RangeOps::ShiftRight (r1, r2, /* logical*/ true );
762+ break ;
763+ case VNF_UMOD:
764+ binOpResult = RangeOps::UnsignedMod (r1, r2);
765+ break ;
766+ default :
767+ unreached ();
773768 }
769+
770+ // We can use the result only if it never overflows.
771+ result = binOpResult.IsConstantRange () ? binOpResult : result;
774772 break ;
775773 }
776774
@@ -807,10 +805,28 @@ bool RangeCheck::TryGetRangeFromAssertions(Compiler* comp, ValueNum num, ASSERT_
807805 }
808806 }
809807
808+ Range phiRange = Range (Limit (Limit::keUndef));
809+ if (comp->optVisitReachingAssertions (num,
810+ [comp, &phiRange, &budget](ValueNum reachingVN, ASSERT_TP reachingAssertions) {
811+ // call GetRangeFromAssertions for each reaching VN using reachingAssertions
812+ Range edgeRange = GetRangeFromAssertions (comp, reachingVN, reachingAssertions, --budget);
813+
814+ // If phiRange is not yet set, set it to the first edgeRange
815+ // else merge it with the new edgeRange. Example: [10..100] U [50..150] = [10..150]
816+ phiRange = phiRange.LowerLimit ().IsUndef () ? edgeRange : RangeOps::Merge (phiRange, edgeRange, false );
817+
818+ // if any edge produces a non-constant range, we abort further processing
819+ // We also give up if the range is full, as it won't help tighten the result.
820+ return edgeRange.IsConstantRange () && !edgeRange.IsFullRange () ? Compiler::AssertVisit::Continue
821+ : Compiler::AssertVisit::Abort;
822+ }) == Compiler::AssertVisit::Continue)
823+ {
824+ result = phiRange;
825+ }
826+
810827 MergeEdgeAssertions (comp, num, ValueNumStore::NoVN, assertions, &result, false );
811828 assert (result.IsConstantRange ());
812- *pRange = result;
813- return true ;
829+ return result;
814830}
815831
816832// ------------------------------------------------------------------------
0 commit comments