Skip to content

Commit f99e58b

Browse files
authored
Reapply "Use rangecheck in assertprop (#112766)" (#112872) (#112907)
1 parent 998ca7b commit f99e58b

File tree

3 files changed

+129
-38
lines changed

3 files changed

+129
-38
lines changed

src/coreclr/jit/assertionprop.cpp

+70-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1111
*/
1212

1313
#include "jitpch.h"
14+
#include "rangecheck.h"
1415
#ifdef _MSC_VER
1516
#pragma hdrstop
1617
#endif
@@ -4155,6 +4156,69 @@ void Compiler::optAssertionProp_RangeProperties(ASSERT_VALARG_TP assertions,
41554156
}
41564157
}
41574158
}
4159+
4160+
if (*isKnownNonZero && *isKnownNonNegative)
4161+
{
4162+
return;
4163+
}
4164+
4165+
// Let's see if MergeEdgeAssertions can help us:
4166+
if (tree->TypeIs(TYP_INT))
4167+
{
4168+
// See if (X + CNS) is known to be non-negative
4169+
if (tree->OperIs(GT_ADD) && tree->gtGetOp2()->IsIntCnsFitsInI32())
4170+
{
4171+
Range rng = Range(Limit(Limit::keDependent));
4172+
ValueNum vn = vnStore->VNConservativeNormalValue(tree->gtGetOp1()->gtVNPair);
4173+
RangeCheck::MergeEdgeAssertions(this, vn, ValueNumStore::NoVN, assertions, &rng, false);
4174+
4175+
int cns = static_cast<int>(tree->gtGetOp2()->AsIntCon()->IconValue());
4176+
rng.LowerLimit().AddConstant(cns);
4177+
4178+
if ((rng.LowerLimit().IsConstant() && !rng.LowerLimit().AddConstant(cns)) ||
4179+
(rng.UpperLimit().IsConstant() && !rng.UpperLimit().AddConstant(cns)))
4180+
{
4181+
// Add cns to both bounds if they are constants. Make sure the addition doesn't overflow.
4182+
return;
4183+
}
4184+
4185+
if (rng.LowerLimit().IsConstant())
4186+
{
4187+
// E.g. "X + -8" when X's range is [8..unknown]
4188+
// it's safe to say "X + -8" is non-negative
4189+
if ((rng.LowerLimit().GetConstant() == 0))
4190+
{
4191+
*isKnownNonNegative = true;
4192+
}
4193+
4194+
// E.g. "X + 8" when X's range is [0..CNS]
4195+
// Here we have to check the upper bound as well to avoid overflow
4196+
if ((rng.LowerLimit().GetConstant() > 0) && rng.UpperLimit().IsConstant() &&
4197+
rng.UpperLimit().GetConstant() > rng.LowerLimit().GetConstant())
4198+
{
4199+
*isKnownNonNegative = true;
4200+
*isKnownNonZero = true;
4201+
}
4202+
}
4203+
}
4204+
else
4205+
{
4206+
Range rng = Range(Limit(Limit::keDependent));
4207+
RangeCheck::MergeEdgeAssertions(this, treeVN, ValueNumStore::NoVN, assertions, &rng, false);
4208+
Limit lowerBound = rng.LowerLimit();
4209+
if (lowerBound.IsConstant())
4210+
{
4211+
if (lowerBound.GetConstant() >= 0)
4212+
{
4213+
*isKnownNonNegative = true;
4214+
}
4215+
if (lowerBound.GetConstant() > 0)
4216+
{
4217+
*isKnownNonZero = true;
4218+
}
4219+
}
4220+
}
4221+
}
41584222
}
41594223

41604224
//------------------------------------------------------------------------
@@ -4881,12 +4945,6 @@ GenTree* Compiler::optAssertionProp_Cast(ASSERT_VALARG_TP assertions, GenTreeCas
48814945
// Skip over a GT_COMMA node(s), if necessary to get to the lcl.
48824946
GenTree* lcl = op1->gtEffectiveVal();
48834947

4884-
// If we don't have a cast of a LCL_VAR then bail.
4885-
if (!lcl->OperIs(GT_LCL_VAR))
4886-
{
4887-
return nullptr;
4888-
}
4889-
48904948
// Try and see if we can make this cast into a cheaper zero-extending version
48914949
// if the input is known to be non-negative.
48924950
if (!cast->IsUnsigned() && genActualTypeIsInt(lcl) && cast->TypeIs(TYP_LONG) && (TARGET_POINTER_SIZE == 8))
@@ -4900,6 +4958,12 @@ GenTree* Compiler::optAssertionProp_Cast(ASSERT_VALARG_TP assertions, GenTreeCas
49004958
}
49014959
}
49024960

4961+
// If we don't have a cast of a LCL_VAR then bail.
4962+
if (!lcl->OperIs(GT_LCL_VAR))
4963+
{
4964+
return nullptr;
4965+
}
4966+
49034967
IntegralRange range = IntegralRange::ForCastInput(cast);
49044968
AssertionIndex index = optAssertionIsSubrange(lcl, range, assertions);
49054969
if (index != NO_ASSERTION_INDEX)

src/coreclr/jit/rangecheck.cpp

+53-31
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree*
251251
{
252252
JITDUMP("Looking for array size assertions for: " FMT_VN "\n", arrLenVn);
253253
Range arrLength = Range(Limit(Limit::keDependent));
254-
MergeEdgeAssertions(arrLenVn, block->bbAssertionIn, &arrLength);
254+
MergeEdgeAssertions(m_pCompiler, arrLenVn, arrLenVn, block->bbAssertionIn, &arrLength);
255255
if (arrLength.lLimit.IsConstant())
256256
{
257257
arrSize = arrLength.lLimit.GetConstant();
@@ -640,20 +640,28 @@ void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP
640640

641641
LclSsaVarDsc* ssaData = m_pCompiler->lvaGetDesc(lcl)->GetPerSsaData(lcl->GetSsaNum());
642642
ValueNum normalLclVN = m_pCompiler->vnStore->VNConservativeNormalValue(ssaData->m_vnPair);
643-
MergeEdgeAssertions(normalLclVN, assertions, pRange);
643+
ValueNum arrLenVN = m_pCompiler->vnStore->VNConservativeNormalValue(m_pCurBndsChk->GetArrayLength()->gtVNPair);
644+
MergeEdgeAssertions(m_pCompiler, normalLclVN, arrLenVN, assertions, pRange);
644645
}
645646

646647
//------------------------------------------------------------------------
647648
// MergeEdgeAssertions: Merge assertions on the edge flowing into the block about a variable
648649
//
649650
// Arguments:
650-
// normalLclVN - the value number to look for assertions for
651-
// assertions - the assertions to use
652-
// pRange - the range to tighten with assertions
651+
// comp - the compiler instance
652+
// normalLclVN - the value number to look for assertions for
653+
// preferredBoundVN - when this VN is set, it will be given preference over constant limits
654+
// assertions - the assertions to use
655+
// pRange - the range to tighten with assertions
653656
//
654-
void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP assertions, Range* pRange)
657+
void RangeCheck::MergeEdgeAssertions(Compiler* comp,
658+
ValueNum normalLclVN,
659+
ValueNum preferredBoundVN,
660+
ASSERT_VALARG_TP assertions,
661+
Range* pRange,
662+
bool log)
655663
{
656-
if (BitVecOps::IsEmpty(m_pCompiler->apTraits, assertions))
664+
if (BitVecOps::IsEmpty(comp->apTraits, assertions))
657665
{
658666
return;
659667
}
@@ -663,14 +671,14 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
663671
return;
664672
}
665673

666-
// Walk through the "assertions" to check if the apply.
667-
BitVecOps::Iter iter(m_pCompiler->apTraits, assertions);
674+
// Walk through the "assertions" to check if they apply.
675+
BitVecOps::Iter iter(comp->apTraits, assertions);
668676
unsigned index = 0;
669677
while (iter.NextElem(&index))
670678
{
671679
AssertionIndex assertionIndex = GetAssertionIndex(index);
672680

673-
Compiler::AssertionDsc* curAssertion = m_pCompiler->optGetAssertion(assertionIndex);
681+
Compiler::AssertionDsc* curAssertion = comp->optGetAssertion(assertionIndex);
674682

675683
Limit limit(Limit::keUndef);
676684
genTreeOps cmpOper = GT_NONE;
@@ -683,7 +691,7 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
683691
ValueNumStore::CompareCheckedBoundArithInfo info;
684692

685693
// Get i, len, cns and < as "info."
686-
m_pCompiler->vnStore->GetCompareCheckedBoundArithInfo(curAssertion->op1.vn, &info);
694+
comp->vnStore->GetCompareCheckedBoundArithInfo(curAssertion->op1.vn, &info);
687695

688696
// If we don't have the same variable we are comparing against, bail.
689697
if (normalLclVN != info.cmpOp)
@@ -697,12 +705,12 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
697705
}
698706

699707
// If the operand that operates on the bound is not constant, then done.
700-
if (!m_pCompiler->vnStore->IsVNInt32Constant(info.arrOp))
708+
if (!comp->vnStore->IsVNInt32Constant(info.arrOp))
701709
{
702710
continue;
703711
}
704712

705-
int cons = m_pCompiler->vnStore->ConstantValue<int>(info.arrOp);
713+
int cons = comp->vnStore->ConstantValue<int>(info.arrOp);
706714
limit = Limit(Limit::keBinOpArray, info.vnBound, info.arrOper == GT_SUB ? -cons : cons);
707715
cmpOper = (genTreeOps)info.cmpOper;
708716
}
@@ -712,7 +720,7 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
712720
ValueNumStore::CompareCheckedBoundArithInfo info;
713721

714722
// Get the info as "i", "<" and "len"
715-
m_pCompiler->vnStore->GetCompareCheckedBound(curAssertion->op1.vn, &info);
723+
comp->vnStore->GetCompareCheckedBound(curAssertion->op1.vn, &info);
716724

717725
// If we don't have the same variable we are comparing against, bail.
718726
if (normalLclVN == info.cmpOp)
@@ -736,7 +744,7 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
736744
ValueNumStore::ConstantBoundInfo info;
737745

738746
// Get the info as "i", "<" and "100"
739-
m_pCompiler->vnStore->GetConstantBoundInfo(curAssertion->op1.vn, &info);
747+
comp->vnStore->GetConstantBoundInfo(curAssertion->op1.vn, &info);
740748

741749
// If we don't have the same variable we are comparing against, bail.
742750
if (normalLclVN != info.cmpOpVN)
@@ -757,10 +765,10 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
757765
}
758766

759767
int cnstLimit = (int)curAssertion->op2.u1.iconVal;
760-
assert(cnstLimit == m_pCompiler->vnStore->CoercedConstantValue<int>(curAssertion->op2.vn));
768+
assert(cnstLimit == comp->vnStore->CoercedConstantValue<int>(curAssertion->op2.vn));
761769

762770
if ((cnstLimit == 0) && (curAssertion->assertionKind == Compiler::OAK_NOT_EQUAL) &&
763-
m_pCompiler->vnStore->IsVNCheckedBound(curAssertion->op1.vn))
771+
comp->vnStore->IsVNCheckedBound(curAssertion->op1.vn))
764772
{
765773
// we have arr.Len != 0, so the length must be atleast one
766774
limit = Limit(Limit::keConstant, 1);
@@ -805,31 +813,31 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
805813

806814
// Make sure the assertion is of the form != 0 or == 0 if it isn't a constant assertion.
807815
if (!isConstantAssertion && (curAssertion->assertionKind != Compiler::OAK_NO_THROW) &&
808-
(curAssertion->op2.vn != m_pCompiler->vnStore->VNZeroForType(TYP_INT)))
816+
(curAssertion->op2.vn != comp->vnStore->VNZeroForType(TYP_INT)))
809817
{
810818
continue;
811819
}
812820
#ifdef DEBUG
813-
if (m_pCompiler->verbose)
821+
if (comp->verbose)
814822
{
815-
m_pCompiler->optPrintAssertion(curAssertion, assertionIndex);
823+
comp->optPrintAssertion(curAssertion, assertionIndex);
816824
}
817825
#endif
818826

819827
// Limits are sometimes made with the form vn + constant, where vn is a known constant
820828
// see if we can simplify this to just a constant
821-
if (limit.IsBinOpArray() && m_pCompiler->vnStore->IsVNInt32Constant(limit.vn))
829+
if (limit.IsBinOpArray() && comp->vnStore->IsVNInt32Constant(limit.vn))
822830
{
823-
Limit tempLimit = Limit(Limit::keConstant, m_pCompiler->vnStore->ConstantValue<int>(limit.vn));
831+
Limit tempLimit = Limit(Limit::keConstant, comp->vnStore->ConstantValue<int>(limit.vn));
824832
if (tempLimit.AddConstant(limit.cns))
825833
{
826834
limit = tempLimit;
827835
}
828836
}
829837

830-
ValueNum arrLenVN = m_pCompiler->vnStore->VNConservativeNormalValue(m_pCurBndsChk->GetArrayLength()->gtVNPair);
838+
ValueNum arrLenVN = preferredBoundVN;
831839

832-
if (m_pCompiler->vnStore->IsVNConstant(arrLenVN))
840+
if (comp->vnStore->IsVNConstant(arrLenVN))
833841
{
834842
// Set arrLenVN to NoVN; this will make it match the "vn" recorded on
835843
// constant limits (where we explicitly track the constant and don't
@@ -918,7 +926,10 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
918926

919927
if (limit.vn != arrLenVN)
920928
{
921-
JITDUMP("Array length VN did not match arrLen=" FMT_VN ", limit=" FMT_VN "\n", arrLenVN, limit.vn);
929+
if (log)
930+
{
931+
JITDUMP("Array length VN did not match arrLen=" FMT_VN ", limit=" FMT_VN "\n", arrLenVN, limit.vn);
932+
}
922933
continue;
923934
}
924935

@@ -928,7 +939,10 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
928939
// Incoming limit doesn't tighten the existing upper limit.
929940
if (limCns >= curCns)
930941
{
931-
JITDUMP("Bound limit %d doesn't tighten current bound %d\n", limCns, curCns);
942+
if (log)
943+
{
944+
JITDUMP("Bound limit %d doesn't tighten current bound %d\n", limCns, curCns);
945+
}
932946
continue;
933947
}
934948
}
@@ -955,8 +969,12 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
955969

956970
case GT_GT:
957971
case GT_GE:
958-
pRange->lLimit = limit;
959-
// it doesn't matter if it's isUnsigned or not here - it's not negative anyway.
972+
// GT/GE being unsigned creates a non-contiguous range which we can't represent
973+
// using single Range object.
974+
if (!isUnsigned)
975+
{
976+
pRange->lLimit = limit;
977+
}
960978
break;
961979

962980
case GT_EQ:
@@ -968,9 +986,13 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse
968986
// All other 'cmpOper' kinds leave lLimit/uLimit unchanged
969987
break;
970988
}
971-
JITDUMP("The range after edge merging:");
972-
JITDUMP(pRange->ToString(m_pCompiler));
973-
JITDUMP("\n");
989+
990+
if (log)
991+
{
992+
JITDUMP("The range after edge merging:");
993+
JITDUMP(pRange->ToString(comp));
994+
JITDUMP("\n");
995+
}
974996
}
975997
}
976998

src/coreclr/jit/rangecheck.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,12 @@ class RangeCheck
686686
void MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP assertions, Range* pRange);
687687

688688
// Inspect the assertions about the current ValueNum to refine pRange
689-
void MergeEdgeAssertions(ValueNum num, ASSERT_VALARG_TP assertions, Range* pRange);
689+
static void MergeEdgeAssertions(Compiler* comp,
690+
ValueNum num,
691+
ValueNum preferredBoundVN,
692+
ASSERT_VALARG_TP assertions,
693+
Range* pRange,
694+
bool log = true);
690695

691696
// The maximum possible value of the given "limit". If such a value could not be determined
692697
// return "false". For example: CORINFO_Array_MaxLength for array length.

0 commit comments

Comments
 (0)