@@ -1249,21 +1249,40 @@ void ReplaceVisitor::EndBlock()
1249
1249
assert (!rep.NeedsReadBack || !rep.NeedsWriteBack );
1250
1250
if (rep.NeedsReadBack )
1251
1251
{
1252
- // We only mark fields as requiring read-back if they are live
1253
- // at the point where the stack local was written. If the
1254
- // replacement still needs to be read back then we saw no use
1255
- // between that point and the end of the BB, so we expect the
1256
- // use to be in a successor.
1257
- assert (m_liveness->IsReplacementLiveOut (m_currentBlock, agg->LclNum , (unsigned )i));
1258
-
1259
- JITDUMP (" Reading back replacement V%02u.[%03u..%03u) -> V%02u near the end of " FMT_BB " :\n " ,
1260
- agg->LclNum , rep.Offset , rep.Offset + genTypeSize (rep.AccessType ), rep.LclNum ,
1261
- m_currentBlock->bbNum );
1262
-
1263
- GenTree* readBack = Promotion::CreateReadBack (m_compiler, agg->LclNum , rep);
1264
- Statement* stmt = m_compiler->fgNewStmtFromTree (readBack);
1265
- DISPSTMT (stmt);
1266
- m_compiler->fgInsertStmtNearEnd (m_currentBlock, stmt);
1252
+ if (m_liveness->IsReplacementLiveOut (m_currentBlock, agg->LclNum , (unsigned )i))
1253
+ {
1254
+ JITDUMP (" Reading back replacement V%02u.[%03u..%03u) -> V%02u near the end of " FMT_BB " :\n " ,
1255
+ agg->LclNum , rep.Offset , rep.Offset + genTypeSize (rep.AccessType ), rep.LclNum ,
1256
+ m_currentBlock->bbNum );
1257
+
1258
+ GenTree* readBack = Promotion::CreateReadBack (m_compiler, agg->LclNum , rep);
1259
+ Statement* stmt = m_compiler->fgNewStmtFromTree (readBack);
1260
+ DISPSTMT (stmt);
1261
+ m_compiler->fgInsertStmtNearEnd (m_currentBlock, stmt);
1262
+ }
1263
+ else
1264
+ {
1265
+ // We only mark fields as requiring read-back if they are
1266
+ // live at the point where the stack local was written, so
1267
+ // at first glance we would not expect this case to ever
1268
+ // happen. However, it is possible that the field is live
1269
+ // because it has a future struct use, in which case we may
1270
+ // not need to insert any readbacks anywhere. For example,
1271
+ // consider:
1272
+ //
1273
+ // V03 = CALL() // V03 is a struct with promoted V03.[000..008)
1274
+ // CALL(struct V03) // V03.[000.008) marked as live here
1275
+ //
1276
+ // While V03.[000.008) gets marked for readback at the
1277
+ // assignment, no readback is necessary at the location of
1278
+ // the call argument, and it may die after that.
1279
+
1280
+ JITDUMP (" Skipping reading back dead replacement V%02u.[%03u..%03u) -> V%02u near the end of " FMT_BB
1281
+ " \n " ,
1282
+ agg->LclNum , rep.Offset , rep.Offset + genTypeSize (rep.AccessType ), rep.LclNum ,
1283
+ m_currentBlock->bbNum );
1284
+ }
1285
+
1267
1286
rep.NeedsReadBack = false ;
1268
1287
}
1269
1288
0 commit comments