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
99 changes: 80 additions & 19 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,16 +725,14 @@ void Compiler::optAssertionInit(bool isLocalProp)
optLocalAssertionProp = false;
optCrossBlockLocalAssertionProp = false;

// Use a function countFunc to determine a proper maximum assertion count for the
// method being compiled. The function is linear to the IL size for small and
// moderate methods. For large methods, considering throughput impact, we track no
// more than 64 assertions.
// Note this tracks at most only 256 assertions.
// Heuristic for sizing the assertion table.
//
static const AssertionIndex countFunc[] = {64, 128, 256, 128, 64};
static const unsigned upperBound = ArrLen(countFunc) - 1;
const unsigned codeSize = info.compILCodeSize / 512;
optMaxAssertionCount = countFunc[min(upperBound, codeSize)];
// The weighting of basicBlocks vs locals reflects their relative contribution observed empirically.
// Validated against 1,115,046 compiled methods:
// - 94.6% of methods stay at the floor of 64 (only 1.9% actually need more).
// - Underpredicts for 481 methods (0.043%), with a worst-case deficit of 127.
// - Only 0.4% of methods hit the 256 cap.
optMaxAssertionCount = (AssertionIndex)max(64, min(256, (int)(lvaTrackedCount + 3 * fgBBcount + 48) >> 2));

optComplementaryAssertionMap = new (this, CMK_AssertionProp)
AssertionIndex[optMaxAssertionCount + 1](); // zero-inited (NO_ASSERTION_INDEX)
Expand Down Expand Up @@ -1352,6 +1350,8 @@ bool Compiler::optIsTreeKnownIntValue(bool vnBased, GenTree* tree, ssize_t* pCon
*/
AssertionIndex Compiler::optAddAssertion(const AssertionDsc& newAssertion)
{
bool canAddNewAssertions = optAssertionCount < optMaxAssertionCount;

// See if we already have this assertion in the table.
//
// For local assertion prop we can speed things up by checking the dep vector.
Expand All @@ -1378,21 +1378,33 @@ AssertionIndex Compiler::optAddAssertion(const AssertionDsc& newAssertion)
}
else
{
// For global prop we search the entire table.
//
// Check if exists already, so we can skip adding new one. Search backwards.
for (AssertionIndex index = optAssertionCount; index >= 1; index--)
bool mayHaveDuplicates =
optAssertionHasAssertionsForVN(newAssertion.GetOp1().GetVN(), /* addIfNotFound */ canAddNewAssertions);
// We need to register op2.vn too, even if we know for sure there are no duplicates
if (newAssertion.GetOp2().KindIs(O2K_CHECKED_BOUND_ADD_CNS))
{
mayHaveDuplicates |= optAssertionHasAssertionsForVN(newAssertion.GetOp2().GetCheckedBound(),
/* addIfNotFound */ canAddNewAssertions);
}

if (mayHaveDuplicates)
{
const AssertionDsc& curAssertion = optGetAssertion(index);
if (curAssertion.Equals(newAssertion, /* vnBased */ true))
// For global prop we search the entire table.
//
// Check if exists already, so we can skip adding new one. Search backwards.
for (AssertionIndex index = optAssertionCount; index >= 1; index--)
{
return index;
const AssertionDsc& curAssertion = optGetAssertion(index);
if (curAssertion.Equals(newAssertion, /* vnBased */ true))
{
return index;
}
}
}
}

// Check if we are within max count.
if (optAssertionCount >= optMaxAssertionCount)
if (!canAddNewAssertions)
{
optAssertionOverflow++;
return NO_ASSERTION_INDEX;
Expand Down Expand Up @@ -1439,6 +1451,49 @@ AssertionIndex Compiler::optAddAssertion(const AssertionDsc& newAssertion)
return optAssertionCount;
}

//------------------------------------------------------------------------
// optAssertionHasAssertionsForVN: Check if we already have assertions for the given VN.
// If "addIfNotFound" is true, add the VN to the map if it's not already there.
//
// Arguments:
// vn - the VN to check for
// addIfNotFound - whether to add the VN to the map if it's not found
//
// Return Value:
// true if we already have assertions for the given VN, false otherwise.
//
bool Compiler::optAssertionHasAssertionsForVN(ValueNum vn, bool addIfNotFound)
{
assert(!optLocalAssertionProp);
if (vn == ValueNumStore::NoVN)
{
assert(!addIfNotFound);
return false;
}

if (addIfNotFound)
{
// Lazy initialize the map when we first need to add to it
if (optAssertionVNsMap == nullptr)
{
optAssertionVNsMap = new (this, CMK_AssertionProp) VNSet(getAllocator(CMK_AssertionProp));
}

// Avoid double lookup by using the return value of LookupPointerOrAdd to
// determine whether the VN was already in the map.
bool* pValue = optAssertionVNsMap->LookupPointerOrAdd(vn, false);
if (!*pValue)
{
*pValue = true;
return false;
}
return true;
}

// Otherwise just do a normal lookup
return (optAssertionVNsMap != nullptr) && optAssertionVNsMap->Lookup(vn);
}

#ifdef DEBUG
void Compiler::optDebugCheckAssertion(const AssertionDsc& assertion) const
{
Expand Down Expand Up @@ -3275,12 +3330,18 @@ GenTree* Compiler::optAssertionProp_LclVar(ASSERT_VALARG_TP assertions, GenTreeL
// For local assertion prop we can filter the assertion set down.
//
const unsigned lclNum = tree->GetLclNum();
ValueNum treeVN = optConservativeNormalVN(tree);

ASSERT_TP filteredAssertions = assertions;
if (optLocalAssertionProp)
{
filteredAssertions = BitVecOps::Intersection(apTraits, GetAssertionDep(lclNum), filteredAssertions);
}
else if (!optAssertionHasAssertionsForVN(treeVN))
{
// There are no assertions for this VN
return nullptr;
}

BitVecOps::Iter iter(apTraits, filteredAssertions);
unsigned index = 0;
Expand Down Expand Up @@ -3342,7 +3403,7 @@ GenTree* Compiler::optAssertionProp_LclVar(ASSERT_VALARG_TP assertions, GenTreeL
else
{
// Check VN in Global Assertion Prop
if (curAssertion.GetOp1().GetVN() == vnStore->VNConservativeNormalValue(tree->gtVNPair))
if (curAssertion.GetOp1().GetVN() == treeVN)
{
return optConstantAssertionProp(curAssertion, tree, stmt DEBUGARG(assertionIndex));
}
Expand Down Expand Up @@ -4607,7 +4668,7 @@ bool Compiler::optAssertionVNIsNonNull(ValueNum vn, ASSERT_VALARG_TP assertions)
return true;
}

if (!BitVecOps::MayBeUninit(assertions))
if (!BitVecOps::MayBeUninit(assertions) && optAssertionHasAssertionsForVN(vn))
{
BitVecOps::Iter iter(apTraits, assertions);
unsigned index = 0;
Expand Down
4 changes: 3 additions & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -8410,7 +8410,8 @@ class Compiler
JitExpandArray<ASSERT_TP>* optAssertionDep = nullptr; // table that holds dependent assertions (assertions
// using the value of a local var) for each local var
AssertionDsc* optAssertionTabPrivate; // table that holds info about assertions
AssertionIndex optAssertionCount = 0; // total number of assertions in the assertion table
VNSet* optAssertionVNsMap = nullptr;
AssertionIndex optAssertionCount = 0; // total number of assertions in the assertion table
AssertionIndex optMaxAssertionCount;
bool optCrossBlockLocalAssertionProp;
unsigned optAssertionOverflow;
Expand Down Expand Up @@ -8538,6 +8539,7 @@ class Compiler

bool optCreateJumpTableImpliedAssertions(BasicBlock* switchBb);

bool optAssertionHasAssertionsForVN(ValueNum vn, bool addIfNotFound = false);
#ifdef DEBUG
void optPrintAssertion(const AssertionDsc& newAssertion, AssertionIndex assertionIndex = 0);
void optPrintAssertionIndex(AssertionIndex index);
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/rangecheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ void RangeCheck::MergeEdgeAssertions(Compiler* comp,
return;
}

if (normalLclVN == ValueNumStore::NoVN)
if (!comp->optAssertionHasAssertionsForVN(normalLclVN))
{
return;
}
Expand Down
Loading