@@ -23,7 +23,14 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2323//
2424void ArrIndex::Print (unsigned dim /* = -1 */ )
2525{
26- printf (" V%02d" , arrLcl);
26+ // When we have only length, we deal with a single-dimensional array or span
27+ if (HasLength ())
28+ {
29+ printf (" Len: V%02d" , GetLengthLcl ());
30+ return ;
31+ }
32+
33+ printf (" ArrObj: V%02d" , GetArrayObjLcl ());
2734 for (unsigned i = 0 ; i < ((dim == (unsigned )-1 ) ? rank : dim); ++i)
2835 {
2936 printf (" [V%02d]" , indLcls.Get (i));
@@ -67,8 +74,19 @@ GenTree* LC_Array::ToGenTree(Compiler* comp, BasicBlock* bb)
6774 if (type == Jagged)
6875 {
6976 // Create a a[i][j][k].length type node.
70- GenTree* arr = comp->gtNewLclvNode (arrIndex->arrLcl , comp->lvaTable [arrIndex->arrLcl ].lvType );
71- int rank = GetDimRank ();
77+ int rank = GetDimRank ();
78+
79+ if (arrIndex->HasLength ())
80+ {
81+ assert (oper == ArrLen);
82+ assert (rank == 0 );
83+ assert (arrIndex->HasLength ());
84+ return comp->gtNewLclvNode (arrIndex->GetLengthLcl (), comp->lvaTable [arrIndex->GetLengthLcl ()].lvType );
85+ }
86+
87+ GenTree* arr =
88+ comp->gtNewLclvNode (arrIndex->GetArrayObjLcl (), comp->lvaTable [arrIndex->GetArrayObjLcl ()].lvType );
89+
7290 for (int i = 0 ; i < rank; ++i)
7391 {
7492 GenTree* idx = comp->gtNewLclvNode (arrIndex->indLcls [i], comp->lvaTable [arrIndex->indLcls [i]].lvType );
@@ -919,7 +937,7 @@ unsigned LC_ArrayDeref::Lcl()
919937 unsigned lvl = level;
920938 if (lvl == 0 )
921939 {
922- return array.arrIndex ->arrLcl ;
940+ return array.arrIndex ->GetArrayObjLcl () ;
923941 }
924942 lvl--;
925943 return array.arrIndex ->indLcls [lvl];
@@ -955,6 +973,8 @@ bool LC_ArrayDeref::HasChildren()
955973//
956974void LC_ArrayDeref::DeriveLevelConditions (JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* conds)
957975{
976+ assert (this ->array .arrIndex ->HasArrayObj ());
977+
958978 if (level == 0 )
959979 {
960980 // For level 0, just push (a != null).
@@ -1080,6 +1100,8 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl
10801100 JitExpandArrayStack<LcOptInfo*>* optInfos = context->GetLoopOptInfo (loop->GetIndex ());
10811101 assert (optInfos->Size () > 0 );
10821102
1103+ bool canBeSpan = false ;
1104+
10831105 // We only need to check for iteration behavior if we have array checks.
10841106 //
10851107 bool checkIterationBehavior = false ;
@@ -1090,6 +1112,11 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl
10901112 switch (optInfo->GetOptType ())
10911113 {
10921114 case LcOptInfo::LcJaggedArray:
1115+ // Keep a note that we *might* be dealing with a Span
1116+ canBeSpan |= optInfo->AsLcJaggedArrayOptInfo ()->arrIndex .HasLength ();
1117+ checkIterationBehavior = true ;
1118+ break ;
1119+
10931120 case LcOptInfo::LcMdArray:
10941121 checkIterationBehavior = true ;
10951122 break ;
@@ -1161,13 +1188,19 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl
11611188 // is beyond the limit.
11621189 int stride = abs (iterInfo->IterConst ());
11631190
1164- if (stride >= 58 )
1191+ static_assert_no_msg (INT32_MAX >= CORINFO_Array_MaxLength);
1192+ if (stride >= (INT32_MAX - CORINFO_Array_MaxLength))
11651193 {
11661194 // Array.MaxLength can have maximum of 0X7FFFFFC7 elements, so make sure
1167- // the stride increment doesn't overflow or underflow the index. Hence,
1168- // the maximum stride limit is set to
1169- // (int.MaxValue - (Array.MaxLength - 1) + 1), which is
1170- // (0X7fffffff - 0x7fffffc7 + 2) = 0x3a or 58.
1195+ // the stride increment doesn't overflow or underflow the index.
1196+ return false ;
1197+ }
1198+
1199+ // We don't know exactly whether we might be dealing with a Span<T> or not,
1200+ // but if we suspect we are, we need to be careful about the stride:
1201+ // As Span<>.Length can be INT32_MAX unlike arrays.
1202+ if (canBeSpan && (stride > 1 ))
1203+ {
11711204 return false ;
11721205 }
11731206
@@ -1473,6 +1506,14 @@ bool Compiler::optComputeDerefConditions(FlowGraphNaturalLoop* loop, LoopCloneCo
14731506 {
14741507 LC_Array& array = (*arrayDeref)[i];
14751508
1509+ if (array.arrIndex ->HasLength ())
1510+ {
1511+ // If we have only length, then we don't need to deref the array (we don't even have the array object)
1512+ // and the array is guaranteed to be single-dimensional.
1513+ maxRank = max (0 , maxRank);
1514+ continue ;
1515+ }
1516+
14761517 // First populate the array base variable.
14771518 LC_ArrayDeref* node = LC_ArrayDeref::Find (&arrayDerefNodes, array.arrIndex ->arrLcl );
14781519 if (node == nullptr )
@@ -1913,7 +1954,6 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* contex
19131954 BasicBlock* insertAfter)
19141955{
19151956 JITDUMP (" Inserting loop " FMT_LP " loop choice conditions\n " , loop->GetIndex ());
1916- assert (context->HasBlockConditions (loop->GetIndex ()));
19171957 assert (slowPreheader != nullptr );
19181958
19191959 if (context->HasBlockConditions (loop->GetIndex ()))
@@ -2087,9 +2127,6 @@ void Compiler::optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* contex
20872127 // ...
20882128 // slowPreheader --> slowHeader
20892129 //
2090- // We should always have block conditions.
2091-
2092- assert (context->HasBlockConditions (loop->GetIndex ()));
20932130
20942131 // If any condition is false, go to slowPreheader (which branches or falls through to header of the slow loop).
20952132 BasicBlock* slowHeader = nullptr ;
@@ -2237,17 +2274,30 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN
22372274 return false ;
22382275 }
22392276
2240- // For span we may see the array length is a local var or local field or constant.
2241- // We won't try and extract those.
2242- if (arrBndsChk->GetArrayLength ()->OperIs (GT_LCL_VAR, GT_LCL_FLD, GT_CNS_INT))
2277+ unsigned arrLcl = BAD_VAR_NUM;
2278+ unsigned arrLenLcl = BAD_VAR_NUM;
2279+
2280+ GenTree* arrLen = arrBndsChk->GetArrayLength ();
2281+ if (!arrLen->TypeIs (TYP_INT))
22432282 {
22442283 return false ;
22452284 }
2246- if (arrBndsChk->GetArrayLength ()->gtGetOp1 ()->gtOper != GT_LCL_VAR)
2285+
2286+ if (arrLen->OperIsArrLength () && arrLen->gtGetOp1 ()->OperIs (GT_LCL_VAR))
2287+ {
2288+ // Case 1: Arrays (jagged or multi-dimensional), Strings
2289+ arrLcl = arrLen->gtGetOp1 ()->AsLclVarCommon ()->GetLclNum ();
2290+ }
2291+ else if (arrLen->OperIs (GT_LCL_VAR))
2292+ {
2293+ // Case 2: Array's length (or Span) is stored in a local variable.
2294+ arrLenLcl = arrLen->AsLclVarCommon ()->GetLclNum ();
2295+ }
2296+ else
22472297 {
22482298 return false ;
22492299 }
2250- unsigned arrLcl = arrBndsChk-> GetArrayLength ()-> gtGetOp1 ()-> AsLclVarCommon ()-> GetLclNum ();
2300+
22512301 if (lhsNum != BAD_VAR_NUM && arrLcl != lhsNum)
22522302 {
22532303 return false ;
@@ -2259,6 +2309,7 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN
22592309 {
22602310 result->arrLcl = arrLcl;
22612311 }
2312+ result->arrLenLcl = arrLenLcl;
22622313 result->indLcls .Push (indLcl);
22632314 result->bndsChks .Push (tree);
22642315 result->useBlock = compCurBB;
@@ -2491,10 +2542,19 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop
24912542 }
24922543#endif
24932544
2494- // Check that the array object local variable is invariant within the loop body.
2495- if (!optIsStackLocalInvariant (info->loop , arrIndex.arrLcl ))
2545+ // Check that the array object (or its length) local variable is invariant within the loop body.
2546+ if (!optIsStackLocalInvariant (info->loop ,
2547+ arrIndex.HasArrayObj () ? arrIndex.GetArrayObjLcl () : arrIndex.GetLengthLcl ()))
24962548 {
2497- JITDUMP (" V%02d is not loop invariant\n " , arrIndex.arrLcl );
2549+ if (arrIndex.HasArrayObj ())
2550+ {
2551+ JITDUMP (" ArrObj V%02d is not loop invariant\n " , arrIndex.GetArrayObjLcl ());
2552+ }
2553+ else
2554+ {
2555+ assert (arrIndex.HasLength ());
2556+ JITDUMP (" ArrLen V%02d is not loop invariant\n " , arrIndex.GetLengthLcl ());
2557+ }
24982558 return WALK_SKIP_SUBTREES;
24992559 }
25002560
0 commit comments