diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index d451f3c5d05d10..9862f0edb39be0 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -929,6 +929,8 @@ void Compiler::optAssertionInit(bool isLocalProp) optMaxAssertionCount = 64; } + optAssertionTabCapacity = optMaxAssertionCount; + // Local assertion prop keeps mappings from each local var to the assertions about that var. // optAssertionDep = @@ -947,21 +949,17 @@ void Compiler::optAssertionInit(bool isLocalProp) optLocalAssertionProp = false; optCrossBlockLocalAssertionProp = false; - // Heuristic for sizing the assertion table. - // - // 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)); + // The table grows on demand and the bit vectors are sized by the actual number + // of assertions once all of them are created (see optAssertionPropMain). + optMaxAssertionCount = 512; + optAssertionTabCapacity = 64; optComplementaryAssertionMap = new (this, CMK_AssertionProp) - AssertionIndex[optMaxAssertionCount + 1](); // zero-inited (NO_ASSERTION_INDEX) + AssertionIndex[optAssertionTabCapacity + 1](); // zero-inited (NO_ASSERTION_INDEX) } - optAssertionTabPrivate = new (this, CMK_AssertionProp) AssertionDsc[optMaxAssertionCount]; - optAssertionTraitsInit(optMaxAssertionCount); + optAssertionTabPrivate = new (this, CMK_AssertionProp) AssertionDsc[optAssertionTabCapacity]; + optAssertionTraitsInit(optAssertionTabCapacity); optAssertionCount = 0; optAssertionOverflow = 0; @@ -1622,6 +1620,27 @@ AssertionIndex Compiler::optAddAssertion(const AssertionDsc& newAssertion) return NO_ASSERTION_INDEX; } + if (optAssertionCount == optAssertionTabCapacity) + { + // Only global assertion prop grows the table, see optAssertionInit. + assert(!optLocalAssertionProp); + const AssertionIndex newCapacity = + (AssertionIndex)min(2u * optAssertionTabCapacity, (unsigned)optMaxAssertionCount); + + AssertionDsc* newTab = new (this, CMK_AssertionProp) AssertionDsc[newCapacity]; + for (unsigned i = 0; i < optAssertionCount; i++) + { + newTab[i] = optAssertionTabPrivate[i]; + } + optAssertionTabPrivate = newTab; + + AssertionIndex* newMap = new (this, CMK_AssertionProp) AssertionIndex[newCapacity + 1](); + memcpy(newMap, optComplementaryAssertionMap, (optAssertionTabCapacity + 1) * sizeof(AssertionIndex)); + optComplementaryAssertionMap = newMap; + + optAssertionTabCapacity = newCapacity; + } + optAssertionTabPrivate[optAssertionCount] = newAssertion; optAssertionCount++; @@ -6781,6 +6800,9 @@ PhaseStatus Compiler::optAssertionPropMain() madeChanges |= optCreateJumpTableImpliedAssertions(switchBlock); } + // All assertions are created, size the bit vectors accordingly. + optAssertionTraitsInit(max(optAssertionCount, (AssertionIndex)1)); + if (optAssertionCount == 0) { // Zero out the bbAssertionIn values, as these can be referenced in RangeCheck::MergeAssertion diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 0fd9d6f06c3d1c..0109eb08aa6b73 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9310,6 +9310,7 @@ class Compiler VNSet* optAssertionVNsMap = nullptr; AssertionIndex optAssertionCount = 0; // total number of assertions in the assertion table AssertionIndex optMaxAssertionCount; + AssertionIndex optAssertionTabCapacity; bool optCrossBlockLocalAssertionProp; unsigned optAssertionOverflow; bool optCanPropLclVar; diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index b55c8f96229369..1c5aa898063201 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1307,6 +1307,11 @@ void RangeCheck::MergeEdgeAssertionsWorker(Compiler* comp const Compiler::AssertionDsc& curAssertion = comp->optGetAssertion(assertionIndex); + if (curAssertion.GetOp2().KindIs(Compiler::O2K_CONST_INT) && (curAssertion.GetOp1().GetVN() != normalLclVN)) + { + continue; + } + Limit limit(Limit::keUndef); genTreeOps cmpOper = GT_NONE; bool isUnsigned = false;