@@ -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,8 +74,19 @@ 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
+ }
86
+
87
+ GenTree* arr =
88
+ comp->gtNewLclvNode (arrIndex->GetArrayObjLcl (), comp->lvaTable [arrIndex->GetArrayObjLcl ()].lvType );
89
+
72
90
for (int i = 0 ; i < rank; ++i)
73
91
{
74
92
GenTree* idx = comp->gtNewLclvNode (arrIndex->indLcls [i], comp->lvaTable [arrIndex->indLcls [i]].lvType );
@@ -919,7 +937,7 @@ unsigned LC_ArrayDeref::Lcl()
919
937
unsigned lvl = level;
920
938
if (lvl == 0 )
921
939
{
922
- return array.arrIndex ->arrLcl ;
940
+ return array.arrIndex ->GetArrayObjLcl () ;
923
941
}
924
942
lvl--;
925
943
return array.arrIndex ->indLcls [lvl];
@@ -955,6 +973,8 @@ bool LC_ArrayDeref::HasChildren()
955
973
//
956
974
void LC_ArrayDeref::DeriveLevelConditions (JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* conds)
957
975
{
976
+ assert (this ->array .arrIndex ->HasArrayObj ());
977
+
958
978
if (level == 0 )
959
979
{
960
980
// For level 0, just push (a != null).
@@ -1080,6 +1100,8 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl
1080
1100
JitExpandArrayStack<LcOptInfo*>* optInfos = context->GetLoopOptInfo (loop->GetIndex ());
1081
1101
assert (optInfos->Size () > 0 );
1082
1102
1103
+ bool canBeSpan = false ;
1104
+
1083
1105
// We only need to check for iteration behavior if we have array checks.
1084
1106
//
1085
1107
bool checkIterationBehavior = false ;
@@ -1090,6 +1112,11 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl
1090
1112
switch (optInfo->GetOptType ())
1091
1113
{
1092
1114
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
+
1093
1120
case LcOptInfo::LcMdArray:
1094
1121
checkIterationBehavior = true ;
1095
1122
break ;
@@ -1161,13 +1188,19 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl
1161
1188
// is beyond the limit.
1162
1189
int stride = abs (iterInfo->IterConst ());
1163
1190
1164
- if (stride >= 58 )
1191
+ static_assert_no_msg (INT32_MAX >= CORINFO_Array_MaxLength);
1192
+ if (stride >= (INT32_MAX - CORINFO_Array_MaxLength))
1165
1193
{
1166
1194
// 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
+ {
1171
1204
return false ;
1172
1205
}
1173
1206
@@ -1473,6 +1506,14 @@ bool Compiler::optComputeDerefConditions(FlowGraphNaturalLoop* loop, LoopCloneCo
1473
1506
{
1474
1507
LC_Array& array = (*arrayDeref)[i];
1475
1508
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
+
1476
1517
// First populate the array base variable.
1477
1518
LC_ArrayDeref* node = LC_ArrayDeref::Find (&arrayDerefNodes, array.arrIndex ->arrLcl );
1478
1519
if (node == nullptr )
@@ -1913,7 +1954,6 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* contex
1913
1954
BasicBlock* insertAfter)
1914
1955
{
1915
1956
JITDUMP (" Inserting loop " FMT_LP " loop choice conditions\n " , loop->GetIndex ());
1916
- assert (context->HasBlockConditions (loop->GetIndex ()));
1917
1957
assert (slowPreheader != nullptr );
1918
1958
1919
1959
if (context->HasBlockConditions (loop->GetIndex ()))
@@ -2087,9 +2127,6 @@ void Compiler::optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* contex
2087
2127
// ...
2088
2128
// slowPreheader --> slowHeader
2089
2129
//
2090
- // We should always have block conditions.
2091
-
2092
- assert (context->HasBlockConditions (loop->GetIndex ()));
2093
2130
2094
2131
// If any condition is false, go to slowPreheader (which branches or falls through to header of the slow loop).
2095
2132
BasicBlock* slowHeader = nullptr ;
@@ -2237,17 +2274,30 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN
2237
2274
return false ;
2238
2275
}
2239
2276
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))
2243
2282
{
2244
2283
return false ;
2245
2284
}
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
2247
2297
{
2248
2298
return false ;
2249
2299
}
2250
- unsigned arrLcl = arrBndsChk-> GetArrayLength ()-> gtGetOp1 ()-> AsLclVarCommon ()-> GetLclNum ();
2300
+
2251
2301
if (lhsNum != BAD_VAR_NUM && arrLcl != lhsNum)
2252
2302
{
2253
2303
return false ;
@@ -2259,6 +2309,7 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN
2259
2309
{
2260
2310
result->arrLcl = arrLcl;
2261
2311
}
2312
+ result->arrLenLcl = arrLenLcl;
2262
2313
result->indLcls .Push (indLcl);
2263
2314
result->bndsChks .Push (tree);
2264
2315
result->useBlock = compCurBB;
@@ -2491,10 +2542,19 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop
2491
2542
}
2492
2543
#endif
2493
2544
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 ()))
2496
2548
{
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
+ }
2498
2558
return WALK_SKIP_SUBTREES;
2499
2559
}
2500
2560
0 commit comments