Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Propagate assertions for more checked bounds #11521

Merged
merged 2 commits into from
May 20, 2017

Conversation

JosephTremoulet
Copy link

Generalize (and rename) the assertion/limit code for tracking array
lengths (which have until now been recognized by the VNFunc being
GT_ARR_LENGTH) to track all values that appear in the function as
length arguments to bounds chcks nodes. Add a hashtable to identify that
set of value numbers, m_checkedBoundVNs to the VNStore object, and
populate it during value-numbering. Wherever names were adjusted, the
generalization of "array length" is called "checked bound".

This allows the array bound check removal machinery to operate on span
bound checks as well.

@JosephTremoulet
Copy link
Author

@briansull, @AndyAyersMS PTAL
/cc @dotnet/jit-contrib

This addresses #10050 with the exception that the value-number update that happens for compare nodes when one of their arguments is an array length that gets CSE'd does not apply in as many cases to spans, because we don't have the CSE candidate occurrence lists collected yet when we build the optCseCheckedBoundMap; that is left as a future possible improvement.

The Performance/CodeQuality/Span/Indexer test consists of several indexing patterns which we can use to compare the power of span bound check removal against array bound check removal. The effect of this change (on top of #11207) is:

Sub-Test Effect of this change Effect of this change plus #10453 Effect of this change plus fix for optCseCheckedBoundMap issue
Indexer1 bound check removed; array parity
Indexer2 no change (CSE doesn't update VN) bound check removed; array parity bound check removed; array parity
Indexer3 bound checked removed; array parity
Indexer4 bound checked removed; array parity
Indexer5 no change (afraid write could clobber span length) bound checked removed; array parity
Indexer6 no change (afraid write could clobber span length) bound checked removed; array parity
ReadOnlyIndexer1 bound check removed; array parity
ReadOnlyIndexer2 no change (CSE doesn't update VN) bound check removed; array parity bound check removed; array parity
WriteViaIndexer1 no change (afraid write could clobber span length) bound checks on data[idx] removed, not on data[0]
WriteViaIndexer2 no change, but already have array parity
KnownSizeArray no change, but already have array parity
KnownSizeCtor no change, but already have array parity
KnownSizeCtor2 no change, but already have array parity
SameIndex1 no change, but already have array parity
SameIndex2 no change (afraid write could clobber span length) bound checked removed; array parity
CoveredIndex1 no change, but already have array parity
CoveredIndex2 no change, but already have array parity
CoveredIndex3 no change, but already have array parity

No other diffs from this change (spans aren't used much in our test corpus)

// Cases where op1 holds the condition bound and op2 is 0.
// Loop condition like: "i < len == 0"
// Assertion: "i < len == false"
else if (vnStore->IsVNCompareCheckedBound(vn) &&
Copy link

@briansull briansull May 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment block reads better like this:

// Cases where op1 holds the upper bound and op2 is 0.
// Loop condition like: "i < bnd == 0"
// Assertion: "i < bnd == false"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change this also change the previous comment block to match.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And a few more below...

Copy link
Member

@AndyAyersMS AndyAyersMS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall. Just some nits on comments and use of locals to cache things.

{
// Found an arrayLen feeding a compare that is a tracatable function of it;
// record this in the map so we can update the compare VN if the array length
// Found a checked bound feeding a compare that is a tracatable function of it;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well fix this typo while you're at it: tracatable -> tractable

ValueNumStore::ArrLenArithBoundInfo info;
ValueNum newCmpArgVN;
if (vnStore->IsVNArrLenBound(oldCmpVN))
ValueNumStore* vnStore = m_pCompiler->vnStore;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you hoist the vnStore local defn up a few lines and use it in the new code you added above?

@@ -96,44 +95,35 @@ bool RangeCheck::BetweenBounds(Range& range, int lower, GenTreePtr upper)
else if (m_pCompiler->vnStore->IsVNArrLen(uLimitVN))
{
// Get the array reference from the length.
arrRefVN = m_pCompiler->vnStore->GetArrForLenVn(uLimitVN);
ValueNum arrRefVN = m_pCompiler->vnStore->GetArrForLenVn(uLimitVN);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe introduce a local for m_pCompiler->vnStore (up above somewhere suitable), it is used a dozen or more times below and would both make the code more readable and run a tiny bit faster.

// Cases where op1 holds the condition bound and op2 is 0.
// Loop condition like: "i < len == 0"
// Assertion: "i < len == false"
else if (vnStore->IsVNCompareCheckedBound(vn) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change this also change the previous comment block to match.

// Cases where op1 holds the condition bound and op2 is 0.
// Loop condition like: "i < len == 0"
// Assertion: "i < len == false"
else if (vnStore->IsVNCompareCheckedBound(vn) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And a few more below...

@@ -7028,6 +7062,14 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
excSet = vnStore->VNPExcSetUnion(excSet, vnStore->VNPExcVal(tree->AsBoundsChk()->gtArrLen->gtVNPair));

tree->gtVNPair = vnStore->VNPWithExc(vnStore->VNPForVoid(), excSet);

// Record non-constant value numbers that are used a the length argument to bounds checks, so that
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are used as the length

 - Add default key infos for int/unsigned
 - Call compGetMem through a forward-declared wrapper to avoid circular
   dependency problems if Compiler itself (or one of its dependencies)
   uses a SmallHashTable
Generalize (and rename) the assertion/limit code for tracking array
lengths (which have until now been recognized by the VNFunc being
GT_ARR_LENGTH) to track all values that appear in the function as
length arguments to bounds chcks nodes.  Add a hashtable to identify that
set of value numbers, `m_checkedBoundVNs` to the VNStore object, and
populate it during value-numbering.  Wherever names were adjusted, the
generalization of "array length" is called "checked bound".

This allows the array bound check removal machinery to operate on span
bound checks as well.
@JosephTremoulet
Copy link
Author

Updated per feedback (and to fix build issue)

@JosephTremoulet
Copy link
Author

Perf lab run came back, the indexer tests with diffs do indeed show improvement:

Test Improvement
Indexer1 19%
Indexer3 18%
Indexer4 7%
ReadOnlyIndexer1 19%

@JosephTremoulet
Copy link
Author

@dotnet-bot test this please

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants