@@ -23,7 +23,14 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
23
23
//
24
24
void ArrIndex::Print (unsigned dim /* = -1 */ )
25
25
{
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 ());
27
34
for (unsigned i = 0 ; i < ((dim == (unsigned )-1 ) ? rank : dim); ++i)
28
35
{
29
36
printf (" [V%02d]" , indLcls.Get (i));
@@ -67,11 +74,18 @@ GenTree* LC_Array::ToGenTree(Compiler* comp, BasicBlock* bb)
67
74
if (type == Jagged)
68
75
{
69
76
// 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
+ }
72
86
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 );
75
89
76
90
for (int i = 0 ; i < rank; ++i)
77
91
{
@@ -92,18 +106,7 @@ GenTree* LC_Array::ToGenTree(Compiler* comp, BasicBlock* bb)
92
106
// If asked for arrlen invoke arr length operator.
93
107
if (oper == ArrLen)
94
108
{
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);
107
110
108
111
// We already guaranteed (by a sequence of preceding checks) that the array length operator will not
109
112
// throw an exception because we null checked the base array.
@@ -934,7 +937,7 @@ unsigned LC_ArrayDeref::Lcl()
934
937
unsigned lvl = level;
935
938
if (lvl == 0 )
936
939
{
937
- return array.arrIndex ->arrLcl ;
940
+ return array.arrIndex ->GetArrayObjLcl () ;
938
941
}
939
942
lvl--;
940
943
return array.arrIndex ->indLcls [lvl];
@@ -970,27 +973,17 @@ bool LC_ArrayDeref::HasChildren()
970
973
//
971
974
void LC_ArrayDeref::DeriveLevelConditions (JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* conds)
972
975
{
976
+ assert (this ->array .arrIndex ->HasArrayObj ());
977
+
973
978
if (level == 0 )
974
979
{
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 ())));
990
983
}
991
984
else
992
985
{
993
- assert (! this ->array .arrIndex ->isSpan );
986
+ assert (this ->array .arrIndex ->HasArrayObj () );
994
987
995
988
// Adjust for level0 having just 1 condition and push conditions (i >= 0) && (i < a.len).
996
989
// 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
1109
1102
JitExpandArrayStack<LcOptInfo*>* optInfos = context->GetLoopOptInfo (loop->GetIndex ());
1110
1103
assert (optInfos->Size () > 0 );
1111
1104
1112
- bool spanInvolved = false ;
1105
+ bool spanMaybeInvolved = false ;
1113
1106
1114
1107
// We only need to check for iteration behavior if we have array checks.
1115
1108
//
@@ -1122,7 +1115,7 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl
1122
1115
{
1123
1116
case LcOptInfo::LcJaggedArray:
1124
1117
// Keep a note that we might be dealing with a Span
1125
- spanInvolved |= optInfo->AsLcJaggedArrayOptInfo ()->arrIndex .isSpan ;
1118
+ spanMaybeInvolved |= optInfo->AsLcJaggedArrayOptInfo ()->arrIndex .HasLength () ;
1126
1119
checkIterationBehavior = true ;
1127
1120
break ;
1128
1121
@@ -1205,7 +1198,7 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl
1205
1198
return false ;
1206
1199
}
1207
1200
1208
- if (spanInvolved && (stride > 1 ))
1201
+ if (spanMaybeInvolved && (stride > 1 ))
1209
1202
{
1210
1203
// Span<T> can only be iterated with a stride of 1 since its Length
1211
1204
// can be INT32_MAX.
@@ -1514,6 +1507,14 @@ bool Compiler::optComputeDerefConditions(FlowGraphNaturalLoop* loop, LoopCloneCo
1514
1507
{
1515
1508
LC_Array& array = (*arrayDeref)[i];
1516
1509
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
+
1517
1518
// First populate the array base variable.
1518
1519
LC_ArrayDeref* node = LC_ArrayDeref::Find (&arrayDerefNodes, array.arrIndex ->arrLcl );
1519
1520
if (node == nullptr )
@@ -1954,7 +1955,6 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* contex
1954
1955
BasicBlock* insertAfter)
1955
1956
{
1956
1957
JITDUMP (" Inserting loop " FMT_LP " loop choice conditions\n " , loop->GetIndex ());
1957
- assert (context->HasBlockConditions (loop->GetIndex ()));
1958
1958
assert (slowPreheader != nullptr );
1959
1959
1960
1960
if (context->HasBlockConditions (loop->GetIndex ()))
@@ -2128,9 +2128,6 @@ void Compiler::optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* contex
2128
2128
// ...
2129
2129
// slowPreheader --> slowHeader
2130
2130
//
2131
- // We should always have block conditions.
2132
-
2133
- assert (context->HasBlockConditions (loop->GetIndex ()));
2134
2131
2135
2132
// If any condition is false, go to slowPreheader (which branches or falls through to header of the slow loop).
2136
2133
BasicBlock* slowHeader = nullptr ;
@@ -2278,24 +2275,24 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN
2278
2275
return false ;
2279
2276
}
2280
2277
2281
- bool isSpan = false ;
2282
- unsigned arrLcl;
2278
+ unsigned arrLcl = BAD_VAR_NUM;
2279
+ unsigned arrLenLcl = BAD_VAR_NUM;
2280
+
2283
2281
GenTree* arrLen = arrBndsChk->GetArrayLength ();
2282
+ if (!arrLen->TypeIs (TYP_INT))
2283
+ {
2284
+ return false ;
2285
+ }
2286
+
2284
2287
if (arrLen->OperIsArrLength () && arrLen->gtGetOp1 ()->OperIs (GT_LCL_VAR))
2285
2288
{
2286
2289
// Case 1: Arrays (jagged or multi-dimensional), Strings
2287
2290
arrLcl = arrLen->gtGetOp1 ()->AsLclVarCommon ()->GetLclNum ();
2288
2291
}
2289
2292
else if (arrLen->OperIs (GT_LCL_VAR))
2290
2293
{
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 ();
2299
2296
}
2300
2297
else
2301
2298
{
@@ -2313,7 +2310,7 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN
2313
2310
{
2314
2311
result->arrLcl = arrLcl;
2315
2312
}
2316
- result->isSpan = isSpan ;
2313
+ result->arrLenLcl = arrLenLcl ;
2317
2314
result->indLcls .Push (indLcl);
2318
2315
result->bndsChks .Push (tree);
2319
2316
result->useBlock = compCurBB;
@@ -2546,10 +2543,19 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop
2546
2543
}
2547
2544
#endif
2548
2545
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 ()))
2551
2549
{
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
+ }
2553
2559
return WALK_SKIP_SUBTREES;
2554
2560
}
2555
2561
0 commit comments