@@ -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