Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1703,7 +1703,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
{
// Move the checked bound to the right side for simplicity
relopFunc = ValueNumStore::SwapRelop(relopFunc);
AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(relopFunc, op2VN, op1VN, 0);
AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(this, relopFunc, op2VN, op1VN, 0);
AssertionIndex idx = optAddAssertion(dsc);
optCreateComplementaryAssertion(idx);
return idx;
Expand All @@ -1712,7 +1712,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
// "X <relop> CheckedBnd"
if (!isUnsignedRelop && vnStore->IsVNCheckedBound(op2VN))
{
AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(relopFunc, op1VN, op2VN, 0);
AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(this, relopFunc, op1VN, op2VN, 0);
AssertionIndex idx = optAddAssertion(dsc);
optCreateComplementaryAssertion(idx);
return idx;
Expand All @@ -1725,7 +1725,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
{
// Move the (CheckedBnd + CNS) part to the right side for simplicity
relopFunc = ValueNumStore::SwapRelop(relopFunc);
AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(relopFunc, op2VN, checkedBnd, checkedBndCns);
AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(this, relopFunc, op2VN, checkedBnd, checkedBndCns);
AssertionIndex idx = optAddAssertion(dsc);
optCreateComplementaryAssertion(idx);
return idx;
Expand All @@ -1734,7 +1734,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
// "X <relop> (CheckedBnd + CNS)"
if (!isUnsignedRelop && vnStore->IsVNCheckedBoundAddConst(op2VN, &checkedBnd, &checkedBndCns))
{
AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(relopFunc, op1VN, checkedBnd, checkedBndCns);
AssertionDsc dsc = AssertionDsc::CreateCompareCheckedBound(this, relopFunc, op1VN, checkedBnd, checkedBndCns);
AssertionIndex idx = optAddAssertion(dsc);
optCreateComplementaryAssertion(idx);
return idx;
Expand All @@ -1748,7 +1748,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
ValueNum idxVN = vnStore->VNNormalValue(unsignedCompareBnd.vnIdx);
ValueNum lenVN = vnStore->VNNormalValue(unsignedCompareBnd.vnBound);

AssertionDsc dsc = AssertionDsc::CreateNoThrowArrBnd(idxVN, lenVN);
AssertionDsc dsc = AssertionDsc::CreateNoThrowArrBnd(this, idxVN, lenVN);
AssertionIndex index = optAddAssertion(dsc);
if (unsignedCompareBnd.cmpOper == VNF_GE_UN)
{
Expand Down Expand Up @@ -2006,6 +2006,21 @@ void Compiler::optAssertionGen(GenTree* tree)
}
break;

case GT_LCL_VAR:
if (!optLocalAssertionProp && tree->TypeIs(TYP_INT) &&
lvaGetDesc(tree->AsLclVarCommon())->IsNeverNegative())
{
// Create "LCL_VAR >= 0" assertion for int local variables that are never negative.
// Typically, it's Span.Length.
ValueNum opVN = optConservativeNormalVN(tree);
if (opVN != ValueNumStore::NoVN)
{
assertionInfo = optAddAssertion(
AssertionDsc::CreateConstantBound(this, VNF_GE, opVN, vnStore->VNZeroForType(TYP_INT)));
}
}
break;

case GT_IND:
case GT_XAND:
case GT_XORR:
Expand Down Expand Up @@ -2036,8 +2051,9 @@ void Compiler::optAssertionGen(GenTree* tree)
case GT_BOUNDS_CHECK:
if (!optLocalAssertionProp)
{
ValueNum idxVN = optConservativeNormalVN(tree->AsBoundsChk()->GetIndex());
ValueNum lenVN = optConservativeNormalVN(tree->AsBoundsChk()->GetArrayLength());
GenTree* arrLenNode = tree->AsBoundsChk()->GetArrayLength();
ValueNum idxVN = optConservativeNormalVN(tree->AsBoundsChk()->GetIndex());
ValueNum lenVN = optConservativeNormalVN(arrLenNode);
if ((idxVN == ValueNumStore::NoVN) || (lenVN == ValueNumStore::NoVN))
{
assertionInfo = NO_ASSERTION_INDEX;
Expand All @@ -2047,7 +2063,7 @@ void Compiler::optAssertionGen(GenTree* tree)
// GT_BOUNDS_CHECK node provides the following contract:
// * idxVN < lenVN
// * lenVN is non-negative
assertionInfo = optAddAssertion(AssertionDsc::CreateNoThrowArrBnd(idxVN, lenVN));
assertionInfo = optAddAssertion(AssertionDsc::CreateNoThrowArrBnd(this, idxVN, lenVN));
}
}
break;
Expand Down
41 changes: 29 additions & 12 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -7712,7 +7712,6 @@ class Compiler

enum optOp2Kind : uint8_t
{
O2K_INVALID,
O2K_LCLVAR_COPY,
O2K_CONST_INT,
O2K_CONST_DOUBLE,
Expand All @@ -7732,6 +7731,7 @@ class Compiler
friend struct AssertionDsc; // For AssertionDsc::Create* factory methods

private:
INDEBUG(const Compiler* m_compiler);
optOp1Kind m_kind;
union
{
Expand All @@ -7753,13 +7753,15 @@ class Compiler

ValueNum GetVN() const
{
assert(!m_compiler->optLocalAssertionProp);
// TODO-Cleanup: O1K_LCLVAR should be Local-AP only.
assert(m_vn != ValueNumStore::NoVN);
return m_vn;
}

unsigned GetLclNum() const
{
assert(m_compiler->optLocalAssertionProp);
assert(m_lclNum != BAD_VAR_NUM);
assert(KindIs(O1K_LCLVAR));
return m_lclNum;
Expand All @@ -7776,6 +7778,7 @@ class Compiler
friend struct AssertionDsc; // For AssertionDsc::Create* factory methods

private:
INDEBUG(const Compiler* m_compiler);
optOp2Kind m_kind;
bool m_checkedBoundIsNeverNegative; // only meaningful for O2K_CHECKED_BOUND_ADD_CNS kind
uint16_t m_encodedIconFlags; // encoded icon gtFlags
Expand Down Expand Up @@ -7806,6 +7809,7 @@ class Compiler

unsigned GetLclNum() const
{
assert(m_compiler->optLocalAssertionProp);
assert(KindIs(O2K_LCLVAR_COPY));
return m_lclNum;
}
Expand All @@ -7824,6 +7828,7 @@ class Compiler

IntegralRange GetIntegralRange() const
{
assert(m_compiler->optLocalAssertionProp);
assert(KindIs(O2K_SUBRANGE));
return m_range;
}
Expand All @@ -7837,6 +7842,7 @@ class Compiler

ValueNum GetVN() const
{
assert(!m_compiler->optLocalAssertionProp);
assert(KindIs(O2K_CONST_INT, O2K_CONST_DOUBLE, O2K_ZEROOBJ));
assert(m_vn != ValueNumStore::NoVN);
return m_vn;
Expand All @@ -7845,6 +7851,7 @@ class Compiler
// For "checkedBndVN + cns" form, return the "cns" part.
int GetCheckedBoundConstant() const
{
assert(!m_compiler->optLocalAssertionProp);
assert(KindIs(O2K_CHECKED_BOUND_ADD_CNS));
assert(FitsIn<int>(m_icon.m_iconVal));
return (int)m_icon.m_iconVal;
Expand All @@ -7854,13 +7861,15 @@ class Compiler
// We intentionally don't allow to use it via GetVN() to avoid confusion.
ValueNum GetCheckedBound() const
{
assert(!m_compiler->optLocalAssertionProp);
assert(KindIs(O2K_CHECKED_BOUND_ADD_CNS));
assert(m_vn != ValueNumStore::NoVN);
return m_vn;
}

bool IsCheckedBoundNeverNegative() const
{
assert(!m_compiler->optLocalAssertionProp);
assert(KindIs(O2K_CHECKED_BOUND_ADD_CNS));
return m_checkedBoundIsNeverNegative;
}
Expand Down Expand Up @@ -7911,6 +7920,14 @@ class Compiler
optAssertionKind m_assertionKind;
AssertionDscOp1 m_op1;
AssertionDscOp2 m_op2;

static AssertionDsc CreateEmptyAssertion(const Compiler* comp)
{
AssertionDsc dsc = {};
INDEBUG(dsc.m_op1.m_compiler = comp);
INDEBUG(dsc.m_op2.m_compiler = comp);
return dsc;
}
public:

optAssertionKind GetKind() const
Expand Down Expand Up @@ -8168,7 +8185,6 @@ class Compiler
case O2K_SUBRANGE:
return GetOp2().GetIntegralRange().Equals(that.GetOp2().GetIntegralRange());

case O2K_INVALID:
default:
assert(!"Unexpected value for GetOp2().m_kind in AssertionDsc.");
break;
Expand Down Expand Up @@ -8203,7 +8219,7 @@ class Compiler
GenTreeFlags iconFlags = GTF_EMPTY,
FieldSeq* fldSeq = nullptr)
{
AssertionDsc dsc = {};
AssertionDsc dsc = CreateEmptyAssertion(comp);
dsc.m_assertionKind = equals ? OAK_EQUAL : OAK_NOT_EQUAL;
dsc.m_op1.m_kind = O1K_LCLVAR;

Expand Down Expand Up @@ -8284,7 +8300,7 @@ class Compiler
assert(lclNum1 != BAD_VAR_NUM);
assert(lclNum2 != BAD_VAR_NUM);

AssertionDsc dsc = {};
AssertionDsc dsc = CreateEmptyAssertion(comp);
dsc.m_assertionKind = equals ? OAK_EQUAL : OAK_NOT_EQUAL;
dsc.m_op1.m_kind = O1K_LCLVAR;
dsc.m_op1.m_lclNum = lclNum1;
Expand All @@ -8300,7 +8316,7 @@ class Compiler
assert(comp->optLocalAssertionProp);
assert(lclNum != BAD_VAR_NUM);

AssertionDsc dsc = {};
AssertionDsc dsc = CreateEmptyAssertion(comp);
dsc.m_assertionKind = OAK_SUBRANGE;
dsc.m_op1.m_kind = O1K_LCLVAR;
dsc.m_op1.m_lclNum = lclNum;
Expand All @@ -8321,7 +8337,7 @@ class Compiler
assert(!comp->vnStore->IsVNHandle(op2VN));
assert(!comp->optLocalAssertionProp);

AssertionDsc dsc = {};
AssertionDsc dsc = CreateEmptyAssertion(comp);
dsc.m_assertionKind = equals ? OAK_EQUAL : OAK_NOT_EQUAL;
dsc.m_op1.m_vn = op1VN;
dsc.m_op2.m_vn = op2VN;
Expand All @@ -8336,7 +8352,7 @@ class Compiler
{
assert((objVN != ValueNumStore::NoVN) && comp->vnStore->IsVNTypeHandle(typeHndVN));

AssertionDsc dsc = {};
AssertionDsc dsc = CreateEmptyAssertion(comp);
dsc.m_op1.m_kind = exact ? O1K_EXACT_TYPE : O1K_SUBTYPE;
dsc.m_op1.m_vn = objVN;
dsc.m_op2.m_kind = O2K_CONST_INT;
Expand All @@ -8348,12 +8364,12 @@ class Compiler

// Create a no-throw bounds check assertion: idxVN u< lenVN where lenVN is never negative
// Effectively, this means "idxVN is in range [0, lenVN)".
static AssertionDsc CreateNoThrowArrBnd(ValueNum idxVN, ValueNum lenVN)
static AssertionDsc CreateNoThrowArrBnd(const Compiler* comp, ValueNum idxVN, ValueNum lenVN)
{
assert(idxVN != ValueNumStore::NoVN);
assert(lenVN != ValueNumStore::NoVN);

AssertionDsc dsc = {};
AssertionDsc dsc = CreateEmptyAssertion(comp);
dsc.m_assertionKind = OAK_LT_UN;
dsc.m_op1.m_kind = O1K_VN;
dsc.m_op1.m_vn = idxVN;
Expand All @@ -8367,12 +8383,13 @@ class Compiler
}

// Create "i <relop> (bnd + cns)" assertion
static AssertionDsc CreateCompareCheckedBound(VNFunc relop, ValueNum op1VN, ValueNum checkedBndVN, int cns)
static AssertionDsc CreateCompareCheckedBound(
const Compiler* comp, VNFunc relop, ValueNum op1VN, ValueNum checkedBndVN, int cns)
{
assert(op1VN != ValueNumStore::NoVN);
assert(checkedBndVN != ValueNumStore::NoVN);

AssertionDsc dsc = {};
AssertionDsc dsc = CreateEmptyAssertion(comp);
dsc.m_assertionKind = FromVNFunc(relop);
dsc.m_op1.m_kind = O1K_VN;
dsc.m_op1.m_vn = op1VN;
Expand All @@ -8391,7 +8408,7 @@ class Compiler
bool op2IsCns = comp->vnStore->IsVNIntegralConstant(cnsVN, &cns);
assert(op2IsCns);

AssertionDsc dsc = {};
AssertionDsc dsc = CreateEmptyAssertion(comp);
dsc.m_assertionKind = FromVNFunc(relop);
dsc.m_op1.m_kind = O1K_VN;
dsc.m_op1.m_vn = op1VN;
Expand Down
9 changes: 1 addition & 8 deletions src/coreclr/jit/rangecheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1092,14 +1092,7 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp,
int cnstLimit = (int)curAssertion.GetOp2().GetIntConstant();
assert(cnstLimit == comp->vnStore->CoercedConstantValue<int>(curAssertion.GetOp2().GetVN()));

if ((cnstLimit == 0) && curAssertion.KindIs(Compiler::OAK_NOT_EQUAL) && canUseCheckedBounds &&
comp->vnStore->IsVNCheckedBound(curAssertion.GetOp1().GetVN()))
{
// we have arr.Len != 0, so the length must be atleast one
limit = Limit(Limit::keConstant, 1);
cmpOper = GT_GE;
}
else if (curAssertion.KindIs(Compiler::OAK_EQUAL))
if (curAssertion.KindIs(Compiler::OAK_EQUAL))
{
limit = Limit(Limit::keConstant, cnstLimit);
cmpOper = GT_EQ;
Expand Down
Loading