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

Commit 04e0c4c

Browse files
Update array length compare value numbers on CSE
Modify CSE to identify compares which are functions of CSE candidate array length nodes; when those length nodes get CSEd and consequently have their value numbers updated, also update the value number on the compare consuming the array length; this way the assertions generated in assertion prop for the different compares on the CSEd array will use the same value numbers, and range check elimination becomes more effective. Resolves #5371
1 parent 0fae96e commit 04e0c4c

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed

src/jit/compiler.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5410,6 +5410,14 @@ class Compiler
54105410
CSEdsc** optCSEhash;
54115411
CSEdsc** optCSEtab;
54125412

5413+
typedef SimplerHashTable<GenTreePtr, PtrKeyFuncs<GenTree>, GenTreePtr, JitSimplerHashBehavior> NodeToNodeMap;
5414+
5415+
NodeToNodeMap* cseArrLenMap; // Maps array length nodes to ancestor compares that should be
5416+
// re-numbered with the array length to improve range check elimination
5417+
5418+
// Given a compare, look for a cse candidate arrlen feeding it and add a map entry if found.
5419+
void updateCseArrLenMap(GenTreePtr compare);
5420+
54135421
void optCSEstop();
54145422

54155423
CSEdsc* optCSEfindDsc(unsigned index);

src/jit/optcse.cpp

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,9 @@ void Compiler::optValnumCSE_Init()
509509

510510
optCSECandidateCount = 0;
511511
optDoCSE = false; // Stays false until we find duplicate CSE tree
512+
513+
// cseArrLenMap is unused in most functions, allocated only when used
514+
cseArrLenMap = nullptr;
512515
}
513516

514517
/*****************************************************************************
@@ -700,8 +703,17 @@ unsigned Compiler::optValnumCSE_Locate()
700703
noway_assert(stmt->gtOper == GT_STMT);
701704

702705
/* We walk the tree in the forwards direction (bottom up) */
706+
bool stmtHasArrLenCandidate = false;
703707
for (tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext)
704708
{
709+
if (tree->OperIsCompare() && stmtHasArrLenCandidate)
710+
{
711+
// Check if this compare is a function of (one of) the arrary
712+
// length candidate(s); we may want to update its value number
713+
// if the array length gets CSEd
714+
updateCseArrLenMap(tree);
715+
}
716+
705717
if (!optIsCSEcandidate(tree))
706718
{
707719
continue;
@@ -730,6 +742,11 @@ unsigned Compiler::optValnumCSE_Locate()
730742
{
731743
noway_assert(((unsigned)tree->gtCSEnum) == CSEindex);
732744
}
745+
746+
if (IS_CSE_INDEX(CSEindex) && (tree->OperGet() == GT_ARR_LENGTH))
747+
{
748+
stmtHasArrLenCandidate = true;
749+
}
733750
}
734751
}
735752
}
@@ -748,6 +765,102 @@ unsigned Compiler::optValnumCSE_Locate()
748765
return 1;
749766
}
750767

768+
//------------------------------------------------------------------------
769+
// updateCseArrLenMap: Check if this compare is a tractable function of
770+
// an array length that is a CSE candidate, and insert
771+
// an entry in the cseArrLenMap if so. This facilitates
772+
// subsequently updating the compare's value number if
773+
// the array length gets CSEd.
774+
//
775+
// Arguments:
776+
// compare - The compare node to check
777+
778+
void Compiler::updateCseArrLenMap(GenTreePtr compare)
779+
{
780+
assert(compare->OperIsCompare());
781+
782+
ValueNum compareVN = compare->gtVNPair.GetConservative();
783+
VNFuncApp cmpVNFuncApp;
784+
785+
if (!vnStore->GetVNFunc(compareVN, &cmpVNFuncApp) ||
786+
(cmpVNFuncApp.m_func != GetVNFuncForOper(compare->OperGet(), (compare->gtFlags & GTF_UNSIGNED) != 0)))
787+
{
788+
// Value numbering inferred this compare as something other
789+
// than its own operator; leave its value number alone.
790+
return;
791+
}
792+
793+
// Now look for an array length feeding the compare
794+
ValueNumStore::ArrLenArithBoundInfo info;
795+
GenTreePtr arrLenParent = nullptr;
796+
797+
if (vnStore->IsVNArrLenBound(compareVN))
798+
{
799+
// Simple compare of an array legnth against something else.
800+
801+
vnStore->GetArrLenBoundInfo(compareVN, &info);
802+
arrLenParent = compare;
803+
}
804+
else if (vnStore->IsVNArrLenArithBound(compareVN))
805+
{
806+
// Compare of an array length +/- some offset to something else.
807+
808+
GenTreePtr op1 = compare->gtGetOp1();
809+
GenTreePtr op2 = compare->gtGetOp2();
810+
811+
vnStore->GetArrLenArithBoundInfo(compareVN, &info);
812+
if (GetVNFuncForOper(op1->OperGet(), (op1->gtFlags & GTF_UNSIGNED) != 0) == info.arrOper)
813+
{
814+
// The arithmetic node is the array length's parent.
815+
arrLenParent = op1;
816+
}
817+
else if (GetVNFuncForOper(op2->OperGet(), (op2->gtFlags & GTF_UNSIGNED) != 0) == info.arrOper)
818+
{
819+
// The arithmetic node is the array length's parent.
820+
arrLenParent = op2;
821+
}
822+
}
823+
824+
if (arrLenParent != nullptr)
825+
{
826+
GenTreePtr arrLen = nullptr;
827+
828+
// Find which child of arrLenParent is the array length. Abort if its
829+
// conservative value number doesn't match the one from the compare VN.
830+
831+
GenTreePtr child1 = arrLenParent->gtGetOp1();
832+
if ((child1->OperGet() == GT_ARR_LENGTH) && IS_CSE_INDEX(child1->gtCSEnum) &&
833+
(info.vnArray == child1->AsArrLen()->ArrRef()->gtVNPair.GetConservative()))
834+
{
835+
arrLen = child1;
836+
}
837+
else
838+
{
839+
GenTreePtr child2 = arrLenParent->gtGetOp2();
840+
if ((child2->OperGet() == GT_ARR_LENGTH) && IS_CSE_INDEX(child2->gtCSEnum) &&
841+
(info.vnArray == child2->AsArrLen()->ArrRef()->gtVNPair.GetConservative()))
842+
{
843+
arrLen = child2;
844+
}
845+
}
846+
847+
if (arrLen != nullptr)
848+
{
849+
// Found an arrayLen feeding a compare that is a tracatable function of it;
850+
// record this in the map so we can update the compare VN if the array length
851+
// node gets CSEd.
852+
853+
if (cseArrLenMap == nullptr)
854+
{
855+
// Allocate map on first use.
856+
cseArrLenMap = new (getAllocator()) NodeToNodeMap(getAllocator());
857+
}
858+
859+
cseArrLenMap->Set(arrLen, compare);
860+
}
861+
}
862+
}
863+
751864
/*****************************************************************************
752865
*
753866
* Compute each blocks bbCseGen
@@ -1909,6 +2022,39 @@ class CSE_Heuristic
19092022
// use to fetch the same value with no reload, so we can safely propagate that
19102023
// conservative VN to this use. This can help range check elimination later on.
19112024
cse->gtVNPair.SetConservative(defConservativeVN);
2025+
2026+
GenTreePtr cmp;
2027+
if ((exp->OperGet() == GT_ARR_LENGTH) && (m_pCompiler->cseArrLenMap != nullptr) &&
2028+
(m_pCompiler->cseArrLenMap->Lookup(exp, &cmp)))
2029+
{
2030+
// Propagate the new value number to this compare node as well, since
2031+
// subsequent range check elimination will try to correlate it with
2032+
// the other appearances that are getting CSEd.
2033+
2034+
ValueNumStore* vnStore = m_pCompiler->vnStore;
2035+
ValueNum oldCmpVN = cmp->gtVNPair.GetConservative();
2036+
ValueNumStore::ArrLenArithBoundInfo info;
2037+
ValueNum newCmpArgVN;
2038+
if (vnStore->IsVNArrLenBound(oldCmpVN))
2039+
{
2040+
// Comparison is against the array length directly.
2041+
2042+
newCmpArgVN = defConservativeVN;
2043+
vnStore->GetArrLenBoundInfo(oldCmpVN, &info);
2044+
}
2045+
else
2046+
{
2047+
// Comparison is against the array length +/- some offset.
2048+
2049+
assert(vnStore->IsVNArrLenArithBound(oldCmpVN));
2050+
vnStore->GetArrLenArithBoundInfo(oldCmpVN, &info);
2051+
newCmpArgVN = vnStore->VNForFunc(vnStore->TypeOfVN(info.arrOp), (VNFunc)info.arrOper,
2052+
info.arrOp, defConservativeVN);
2053+
}
2054+
ValueNum newCmpVN = vnStore->VNForFunc(vnStore->TypeOfVN(oldCmpVN), (VNFunc)info.cmpOper,
2055+
info.cmpOp, newCmpArgVN);
2056+
cmp->gtVNPair.SetConservative(newCmpVN);
2057+
}
19122058
}
19132059
#ifdef DEBUG
19142060
cse->gtDebugFlags |= GTF_DEBUG_VAR_CSE_REF;

0 commit comments

Comments
 (0)