@@ -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,11 +74,18 @@ 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+ }
7286
73- // rank is always 0 for spans
74- assert (! arrIndex->isSpan || (rank == 0 ) );
87+ GenTree* arr =
88+ comp-> gtNewLclvNode ( arrIndex->GetArrayObjLcl (), comp-> lvaTable [arrIndex-> GetArrayObjLcl ()]. lvType );
7589
7690 for (int i = 0 ; i < rank; ++i)
7791 {
@@ -92,18 +106,7 @@ GenTree* LC_Array::ToGenTree(Compiler* comp, BasicBlock* bb)
92106 // If asked for arrlen invoke arr length operator.
93107 if (oper == ArrLen)
94108 {
95- GenTree* arrLen;
96- if (arrIndex->isSpan )
97- {
98- // For promoted spans, arr is already our length.
99- assert (arr->OperIs (GT_LCL_VAR));
100- assert (comp->lvaGetDesc (arr->AsLclVar ()->GetLclNum ())->IsSpanLength ());
101- arrLen = arr;
102- }
103- else
104- {
105- arrLen = comp->gtNewArrLen (TYP_INT, arr, OFFSETOF__CORINFO_Array__length, bb);
106- }
109+ GenTree* arrLen = comp->gtNewArrLen (TYP_INT, arr, OFFSETOF__CORINFO_Array__length, bb);
107110
108111 // We already guaranteed (by a sequence of preceding checks) that the array length operator will not
109112 // throw an exception because we null checked the base array.
@@ -934,7 +937,7 @@ unsigned LC_ArrayDeref::Lcl()
934937 unsigned lvl = level;
935938 if (lvl == 0 )
936939 {
937- return array.arrIndex ->arrLcl ;
940+ return array.arrIndex ->GetArrayObjLcl () ;
938941 }
939942 lvl--;
940943 return array.arrIndex ->indLcls [lvl];
@@ -970,27 +973,17 @@ bool LC_ArrayDeref::HasChildren()
970973//
971974void LC_ArrayDeref::DeriveLevelConditions (JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* conds)
972975{
976+ assert (this ->array .arrIndex ->HasArrayObj ());
977+
973978 if (level == 0 )
974979 {
975- if (this ->array .arrIndex ->isSpan )
976- {
977- // For promoted Spans we don't need the "array != null" check.
978- // However, the current algorithm doesn't expect that this condition might not be
979- // needed, we insert a dummy always-true condition.
980- //
981- (*conds)[level]->Push (
982- LC_Condition (GT_NE, LC_Expr (LC_Ident::CreateConst (1 )), LC_Expr (LC_Ident::CreateConst (0 ))));
983- }
984- else
985- {
986- // For level 0, just push (a != null).
987- (*conds)[level]->Push (
988- LC_Condition (GT_NE, LC_Expr (LC_Ident::CreateVar (Lcl ())), LC_Expr (LC_Ident::CreateNull ())));
989- }
980+ // For level 0, just push (a != null).
981+ (*conds)[level]->Push (
982+ LC_Condition (GT_NE, LC_Expr (LC_Ident::CreateVar (Lcl ())), LC_Expr (LC_Ident::CreateNull ())));
990983 }
991984 else
992985 {
993- assert (! this ->array .arrIndex ->isSpan );
986+ assert (this ->array .arrIndex ->HasArrayObj () );
994987
995988 // Adjust for level0 having just 1 condition and push conditions (i >= 0) && (i < a.len).
996989 // We fold the two compares into one using unsigned compare, since we know a.len is non-negative.
@@ -1109,7 +1102,7 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl
11091102 JitExpandArrayStack<LcOptInfo*>* optInfos = context->GetLoopOptInfo (loop->GetIndex ());
11101103 assert (optInfos->Size () > 0 );
11111104
1112- bool spanInvolved = false ;
1105+ bool spanMaybeInvolved = false ;
11131106
11141107 // We only need to check for iteration behavior if we have array checks.
11151108 //
@@ -1122,7 +1115,7 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl
11221115 {
11231116 case LcOptInfo::LcJaggedArray:
11241117 // Keep a note that we might be dealing with a Span
1125- spanInvolved |= optInfo->AsLcJaggedArrayOptInfo ()->arrIndex .isSpan ;
1118+ spanMaybeInvolved |= optInfo->AsLcJaggedArrayOptInfo ()->arrIndex .HasLength () ;
11261119 checkIterationBehavior = true ;
11271120 break ;
11281121
@@ -1205,7 +1198,7 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl
12051198 return false ;
12061199 }
12071200
1208- if (spanInvolved && (stride > 1 ))
1201+ if (spanMaybeInvolved && (stride > 1 ))
12091202 {
12101203 // Span<T> can only be iterated with a stride of 1 since its Length
12111204 // can be INT32_MAX.
@@ -1514,6 +1507,14 @@ bool Compiler::optComputeDerefConditions(FlowGraphNaturalLoop* loop, LoopCloneCo
15141507 {
15151508 LC_Array& array = (*arrayDeref)[i];
15161509
1510+ if (array.arrIndex ->HasLength ())
1511+ {
1512+ // If we have only length, then we don't need to deref the array (we don't even have the array object)
1513+ // and the array is guaranteed to be single-dimensional.
1514+ maxRank = max (0 , maxRank);
1515+ continue ;
1516+ }
1517+
15171518 // First populate the array base variable.
15181519 LC_ArrayDeref* node = LC_ArrayDeref::Find (&arrayDerefNodes, array.arrIndex ->arrLcl );
15191520 if (node == nullptr )
@@ -1954,7 +1955,6 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* contex
19541955 BasicBlock* insertAfter)
19551956{
19561957 JITDUMP (" Inserting loop " FMT_LP " loop choice conditions\n " , loop->GetIndex ());
1957- assert (context->HasBlockConditions (loop->GetIndex ()));
19581958 assert (slowPreheader != nullptr );
19591959
19601960 if (context->HasBlockConditions (loop->GetIndex ()))
@@ -2128,9 +2128,6 @@ void Compiler::optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* contex
21282128 // ...
21292129 // slowPreheader --> slowHeader
21302130 //
2131- // We should always have block conditions.
2132-
2133- assert (context->HasBlockConditions (loop->GetIndex ()));
21342131
21352132 // If any condition is false, go to slowPreheader (which branches or falls through to header of the slow loop).
21362133 BasicBlock* slowHeader = nullptr ;
@@ -2278,24 +2275,24 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN
22782275 return false ;
22792276 }
22802277
2281- bool isSpan = false ;
2282- unsigned arrLcl;
2278+ unsigned arrLcl = BAD_VAR_NUM;
2279+ unsigned arrLenLcl = BAD_VAR_NUM;
2280+
22832281 GenTree* arrLen = arrBndsChk->GetArrayLength ();
2282+ if (!arrLen->TypeIs (TYP_INT))
2283+ {
2284+ return false ;
2285+ }
2286+
22842287 if (arrLen->OperIsArrLength () && arrLen->gtGetOp1 ()->OperIs (GT_LCL_VAR))
22852288 {
22862289 // Case 1: Arrays (jagged or multi-dimensional), Strings
22872290 arrLcl = arrLen->gtGetOp1 ()->AsLclVarCommon ()->GetLclNum ();
22882291 }
22892292 else if (arrLen->OperIs (GT_LCL_VAR))
22902293 {
2291- // Case 2: Promoted spans
2292- arrLcl = arrLen->AsLclVarCommon ()->GetLclNum ();
2293- if (!lvaGetDesc (arrLcl)->IsSpanLength ())
2294- {
2295- return false ;
2296- }
2297- isSpan = true ;
2298- assert (arrLen->TypeIs (TYP_INT));
2294+ // Case 2: Array's length (or Span) is stored in a local variable.
2295+ arrLenLcl = arrLen->AsLclVarCommon ()->GetLclNum ();
22992296 }
23002297 else
23012298 {
@@ -2313,7 +2310,7 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN
23132310 {
23142311 result->arrLcl = arrLcl;
23152312 }
2316- result->isSpan = isSpan ;
2313+ result->arrLenLcl = arrLenLcl ;
23172314 result->indLcls .Push (indLcl);
23182315 result->bndsChks .Push (tree);
23192316 result->useBlock = compCurBB;
@@ -2546,10 +2543,19 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop
25462543 }
25472544#endif
25482545
2549- // Check that the array object local variable is invariant within the loop body.
2550- if (!optIsStackLocalInvariant (info->loop , arrIndex.arrLcl ))
2546+ // Check that the array object (or its length) local variable is invariant within the loop body.
2547+ if (!optIsStackLocalInvariant (info->loop ,
2548+ arrIndex.HasArrayObj () ? arrIndex.GetArrayObjLcl () : arrIndex.GetLengthLcl ()))
25512549 {
2552- JITDUMP (" V%02d is not loop invariant\n " , arrIndex.arrLcl );
2550+ if (arrIndex.HasArrayObj ())
2551+ {
2552+ JITDUMP (" ArrObj V%02d is not loop invariant\n " , arrIndex.GetArrayObjLcl ());
2553+ }
2554+ else
2555+ {
2556+ assert (arrIndex.HasLength ());
2557+ JITDUMP (" ArrLen V%02d is not loop invariant\n " , arrIndex.GetLengthLcl ());
2558+ }
25532559 return WALK_SKIP_SUBTREES;
25542560 }
25552561
0 commit comments