@@ -233,7 +233,7 @@ void ObjectAllocator::MarkEscapingVarsAndBuildConnGraph()
233
233
lclEscapes = false ;
234
234
m_allocator->CheckForGuardedAllocationOrCopy (m_block, m_stmt, use, lclNum);
235
235
}
236
- else if (tree->OperIs (GT_LCL_VAR) && tree->TypeIs (TYP_REF, TYP_BYREF, TYP_I_IMPL))
236
+ else if (tree->OperIs (GT_LCL_VAR, GT_LCL_ADDR ) && tree->TypeIs (TYP_REF, TYP_BYREF, TYP_I_IMPL, TYP_STRUCT ))
237
237
{
238
238
assert (tree == m_ancestors.Top ());
239
239
if (!m_allocator->CanLclVarEscapeViaParentStack (&m_ancestors, lclNum, m_block))
@@ -265,7 +265,7 @@ void ObjectAllocator::MarkEscapingVarsAndBuildConnGraph()
265
265
{
266
266
var_types type = comp->lvaTable [lclNum].TypeGet ();
267
267
268
- if (type == TYP_REF || genActualType (type) == TYP_I_IMPL || type == TYP_BYREF)
268
+ if (type == TYP_REF || genActualType (type) == TYP_I_IMPL || type == TYP_BYREF || type == TYP_STRUCT )
269
269
{
270
270
m_ConnGraphAdjacencyMatrix[lclNum] = BitVecOps::MakeEmpty (&m_bitVecTraits);
271
271
@@ -1023,10 +1023,13 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack<GenTree*>* parent
1023
1023
assert (parentStack != nullptr );
1024
1024
int parentIndex = 1 ;
1025
1025
1026
- bool keepChecking = true ;
1027
- bool canLclVarEscapeViaParentStack = true ;
1028
- bool isCopy = true ;
1029
- bool isEnumeratorLocal = comp->lvaGetDesc (lclNum)->lvIsEnumerator ;
1026
+ LclVarDsc* const lclDsc = comp->lvaGetDesc (lclNum);
1027
+
1028
+ bool keepChecking = true ;
1029
+ bool canLclVarEscapeViaParentStack = true ;
1030
+ bool isCopy = true ;
1031
+ bool const isEnumeratorLocal = lclDsc->lvIsEnumerator ;
1032
+ bool const isSpanLocal = lclDsc->IsSpan ();
1030
1033
1031
1034
while (keepChecking)
1032
1035
{
@@ -1093,11 +1096,21 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack<GenTree*>* parent
1093
1096
case GT_ADD:
1094
1097
case GT_SUB:
1095
1098
case GT_FIELD_ADDR:
1096
- // Check whether the local escapes via its grandparent.
1099
+ // Check whether the local escapes higher up
1097
1100
++parentIndex;
1098
1101
keepChecking = true ;
1099
1102
break ;
1100
1103
1104
+ case GT_LCL_ADDR:
1105
+ if (isSpanLocal)
1106
+ {
1107
+ // Check whether the local escapes higher up
1108
+ ++parentIndex;
1109
+ keepChecking = true ;
1110
+ JITDUMP (" ... i'm good thanks\n " );
1111
+ }
1112
+ break ;
1113
+
1101
1114
case GT_BOX:
1102
1115
isCopy = wasCopy;
1103
1116
++parentIndex;
@@ -1119,11 +1132,43 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack<GenTree*>* parent
1119
1132
case GT_STOREIND:
1120
1133
case GT_STORE_BLK:
1121
1134
case GT_BLK:
1122
- if (tree != parent->AsIndir ()->Addr ())
1135
+ {
1136
+ GenTree* const addr = parent->AsIndir ()->Addr ();
1137
+ if (tree != addr)
1123
1138
{
1124
- // TODO-ObjectStackAllocation: track stores to fields.
1139
+ JITDUMP (" ... tree != addr\n " );
1140
+
1141
+ // Is this an array element address store to (the pointer) field of a span?
1142
+ // (note we can't yet handle cases where a span captures an object)
1143
+ //
1144
+ if (parent->OperIs (GT_STOREIND) && addr->OperIs (GT_FIELD_ADDR) && tree->OperIs (GT_INDEX_ADDR))
1145
+ {
1146
+ // Todo: mark the span pointer field addr like we mark IsSpanLength?
1147
+ // (for now we don't worry which field we store to)
1148
+ //
1149
+ GenTree* const base = addr->AsOp ()->gtGetOp1 ();
1150
+
1151
+ if (base->OperIs (GT_LCL_ADDR))
1152
+ {
1153
+ unsigned const dstLclNum = base->AsLclVarCommon ()->GetLclNum ();
1154
+ LclVarDsc* const dstLclDsc = comp->lvaGetDesc (dstLclNum);
1155
+
1156
+ if (dstLclDsc->IsSpan ())
1157
+ {
1158
+ JITDUMP (" ... span ptr store\n " );
1159
+ // Add an edge to the connection graph.
1160
+ AddConnGraphEdge (dstLclNum, lclNum);
1161
+ canLclVarEscapeViaParentStack = false ;
1162
+ }
1163
+ }
1164
+ }
1125
1165
break ;
1126
1166
}
1167
+ else
1168
+ {
1169
+ JITDUMP (" ... tree == addr\n " );
1170
+ }
1171
+ }
1127
1172
FALLTHROUGH;
1128
1173
case GT_IND:
1129
1174
// Address of the field/ind is not taken so the local doesn't escape.
@@ -1132,20 +1177,20 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack<GenTree*>* parent
1132
1177
1133
1178
case GT_CALL:
1134
1179
{
1135
- GenTreeCall* const asCall = parent->AsCall ();
1180
+ GenTreeCall* const call = parent->AsCall ();
1136
1181
1137
- if (asCall ->IsHelperCall ())
1182
+ if (call ->IsHelperCall ())
1138
1183
{
1139
1184
canLclVarEscapeViaParentStack =
1140
- !Compiler::s_helperCallProperties.IsNoEscape (comp->eeGetHelperNum (asCall ->gtCallMethHnd ));
1185
+ !Compiler::s_helperCallProperties.IsNoEscape (comp->eeGetHelperNum (call ->gtCallMethHnd ));
1141
1186
}
1142
- else if (asCall ->IsSpecialIntrinsic ())
1187
+ else if (call ->IsSpecialIntrinsic ())
1143
1188
{
1144
1189
// Some known special intrinsics don't escape. At this moment, only the ones accepting byrefs
1145
1190
// are supported. In order to support more intrinsics accepting objects, we need extra work
1146
1191
// on the VM side which is not ready for that yet.
1147
1192
//
1148
- switch (comp->lookupNamedIntrinsic (asCall ->gtCallMethHnd ))
1193
+ switch (comp->lookupNamedIntrinsic (call ->gtCallMethHnd ))
1149
1194
{
1150
1195
case NI_System_SpanHelpers_ClearWithoutReferences:
1151
1196
case NI_System_SpanHelpers_Fill:
@@ -1246,6 +1291,7 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack<GenTree*>* p
1246
1291
case GT_SUB:
1247
1292
case GT_FIELD_ADDR:
1248
1293
case GT_INDEX_ADDR:
1294
+ case GT_LCL_ADDR:
1249
1295
if (parent->TypeGet () == TYP_REF)
1250
1296
{
1251
1297
parent->ChangeType (newType);
@@ -1283,17 +1329,18 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack<GenTree*>* p
1283
1329
case GT_STOREIND:
1284
1330
case GT_STORE_BLK:
1285
1331
case GT_BLK:
1286
- assert (tree == parent->AsIndir ()->Addr ());
1287
-
1288
- // The new target could be *not* on the heap.
1289
- parent->gtFlags &= ~GTF_IND_TGT_HEAP;
1290
-
1291
- if (newType != TYP_BYREF)
1332
+ if (tree == parent->AsIndir ()->Addr ())
1292
1333
{
1293
- // This indicates that a write barrier is not needed when writing
1294
- // to this field/indirection since the address is not pointing to the heap.
1295
- // It's either null or points to inside a stack-allocated object.
1296
- parent->gtFlags |= GTF_IND_TGT_NOT_HEAP;
1334
+ // The new target could be *not* on the heap.
1335
+ parent->gtFlags &= ~GTF_IND_TGT_HEAP;
1336
+
1337
+ if (newType != TYP_BYREF)
1338
+ {
1339
+ // This indicates that a write barrier is not needed when writing
1340
+ // to this field/indirection since the address is not pointing to the heap.
1341
+ // It's either null or points to inside a stack-allocated object.
1342
+ parent->gtFlags |= GTF_IND_TGT_NOT_HEAP;
1343
+ }
1297
1344
}
1298
1345
break ;
1299
1346
@@ -1354,10 +1401,7 @@ void ObjectAllocator::RewriteUses()
1354
1401
if ((lclNum < BitVecTraits::GetSize (&m_allocator->m_bitVecTraits )) &&
1355
1402
m_allocator->MayLclVarPointToStack (lclNum))
1356
1403
{
1357
- // Analysis does not handle indirect access to pointer locals.
1358
- assert (tree->OperIsScalarLocal ());
1359
-
1360
- var_types newType;
1404
+ var_types newType = TYP_UNDEF;
1361
1405
if (m_allocator->m_HeapLocalToStackLocalMap .TryGetValue (lclNum, &newLclNum))
1362
1406
{
1363
1407
assert (tree->OperIs (GT_LCL_VAR)); // Must be a use.
@@ -1374,12 +1418,33 @@ void ObjectAllocator::RewriteUses()
1374
1418
}
1375
1419
}
1376
1420
1377
- if (lclVarDsc->lvType != newType)
1421
+ // For local structs, retype the GC fields.
1422
+ //
1423
+ if (lclVarDsc->lvType == TYP_STRUCT)
1424
+ {
1425
+ // We should only see spans here.
1426
+ //
1427
+ assert (lclVarDsc->IsSpan ());
1428
+ ClassLayout* const layout = lclVarDsc->GetLayout ();
1429
+ ClassLayout* newLayout = nullptr ;
1430
+
1431
+ if (newType == TYP_I_IMPL)
1432
+ {
1433
+ // No GC refs remain, so now a block layout
1434
+ newLayout = m_compiler->typGetBlkLayout (layout->GetSize ());
1435
+ JITDUMP (" Changing layout of span V%02u to block\n " , lclNum);
1436
+ lclVarDsc->ChangeLayout (newLayout);
1437
+ }
1438
+ }
1439
+ // For locals, retype the local
1440
+ //
1441
+ else if (lclVarDsc->lvType != newType)
1378
1442
{
1379
1443
JITDUMP (" Changing the type of V%02u from %s to %s\n " , lclNum, varTypeName (lclVarDsc->lvType ),
1380
1444
varTypeName (newType));
1381
1445
lclVarDsc->lvType = newType;
1382
1446
}
1447
+
1383
1448
m_allocator->UpdateAncestorTypes (tree, &m_ancestors, newType);
1384
1449
1385
1450
if (newLclNum != BAD_VAR_NUM)
0 commit comments