@@ -1150,10 +1150,68 @@ GenTree* Promotion::CreateReadBack(Compiler* compiler, unsigned structLclNum, co
11501150 return store;
11511151}
11521152
1153+ // ------------------------------------------------------------------------
1154+ // EndBlock:
1155+ // Handle reaching the end of the currently started block by preparing
1156+ // internal state for upcoming basic blocks, and inserting any necessary
1157+ // readbacks.
1158+ //
1159+ // Remarks:
1160+ // We currently expect all fields to be most up-to-date in their field locals
1161+ // at the beginning of every basic block. That means all replacements should
1162+ // have Replacement::NeedsReadBack == false and Replacement::NeedsWriteBack
1163+ // == true at the beginning of every block. This function makes it so that is
1164+ // the case.
1165+ //
1166+ void ReplaceVisitor::EndBlock ()
1167+ {
1168+ for (AggregateInfo* agg : m_aggregates)
1169+ {
1170+ if (agg == nullptr )
1171+ {
1172+ continue ;
1173+ }
1174+
1175+ for (size_t i = 0 ; i < agg->Replacements .size (); i++)
1176+ {
1177+ Replacement& rep = agg->Replacements [i];
1178+ assert (!rep.NeedsReadBack || !rep.NeedsWriteBack );
1179+ if (rep.NeedsReadBack )
1180+ {
1181+ if (m_liveness->IsReplacementLiveOut (m_currentBlock, agg->LclNum , (unsigned )i))
1182+ {
1183+ JITDUMP (" Reading back replacement V%02u.[%03u..%03u) -> V%02u near the end of " FMT_BB " :\n " ,
1184+ agg->LclNum , rep.Offset , rep.Offset + genTypeSize (rep.AccessType ), rep.LclNum ,
1185+ m_currentBlock->bbNum );
1186+
1187+ GenTree* readBack = Promotion::CreateReadBack (m_compiler, agg->LclNum , rep);
1188+ Statement* stmt = m_compiler->fgNewStmtFromTree (readBack);
1189+ DISPSTMT (stmt);
1190+ m_compiler->fgInsertStmtNearEnd (m_currentBlock, stmt);
1191+ }
1192+ else
1193+ {
1194+ JITDUMP (" Skipping reading back dead replacement V%02u.[%03u..%03u) -> V%02u near the end of " FMT_BB
1195+ " \n " ,
1196+ agg->LclNum , rep.Offset , rep.Offset + genTypeSize (rep.AccessType ), rep.LclNum ,
1197+ m_currentBlock->bbNum );
1198+ }
1199+ rep.NeedsReadBack = false ;
1200+ }
1201+
1202+ rep.NeedsWriteBack = true ;
1203+ }
1204+ }
1205+
1206+ m_hasPendingReadBacks = false ;
1207+ }
1208+
11531209Compiler::fgWalkResult ReplaceVisitor::PostOrderVisit (GenTree** use, GenTree* user)
11541210{
11551211 GenTree* tree = *use;
11561212
1213+ use = InsertMidTreeReadBacksIfNecessary (use);
1214+
11571215 if (tree->OperIsStore ())
11581216 {
11591217 if (tree->OperIsLocalStore ())
@@ -1191,6 +1249,80 @@ Compiler::fgWalkResult ReplaceVisitor::PostOrderVisit(GenTree** use, GenTree* us
11911249 return fgWalkResult::WALK_CONTINUE;
11921250}
11931251
1252+ // ------------------------------------------------------------------------
1253+ // InsertMidTreeReadBacksIfNecessary:
1254+ // If necessary, insert IR to read back all replacements before the specified use.
1255+ //
1256+ // Parameters:
1257+ // use - The use
1258+ //
1259+ // Returns:
1260+ // New use pointing to the old tree.
1261+ //
1262+ // Remarks:
1263+ // When a struct field is most up-to-date in its struct local it is marked to
1264+ // need a read back. We then need to decide when to insert IR to read it back
1265+ // to its field local.
1266+ //
1267+ // We normally do this before the first use of the field we find, or before
1268+ // we transfer control to any successor. This method handles the case of
1269+ // implicit control flow related to EH; when this basic block is in a
1270+ // try-region (or filter block) and we find a tree that may throw it eagerly
1271+ // inserts pending readbacks.
1272+ //
1273+ GenTree** ReplaceVisitor::InsertMidTreeReadBacksIfNecessary (GenTree** use)
1274+ {
1275+ if (!m_hasPendingReadBacks || !m_compiler->ehBlockHasExnFlowDsc (m_currentBlock))
1276+ {
1277+ return use;
1278+ }
1279+
1280+ if (((*use)->gtFlags & (GTF_EXCEPT | GTF_CALL)) == 0 )
1281+ {
1282+ assert (!(*use)->OperMayThrow (m_compiler));
1283+ return use;
1284+ }
1285+
1286+ if (!(*use)->OperMayThrow (m_compiler))
1287+ {
1288+ return use;
1289+ }
1290+
1291+ JITDUMP (" Reading back pending replacements before tree with possible exception side effect inside block in try "
1292+ " region\n " );
1293+
1294+ for (AggregateInfo* agg : m_aggregates)
1295+ {
1296+ if (agg == nullptr )
1297+ {
1298+ continue ;
1299+ }
1300+
1301+ for (Replacement& rep : agg->Replacements )
1302+ {
1303+ // TODO-CQ: We should ensure we do not mark dead fields as
1304+ // requiring readback. Currently it is handled by querying liveness
1305+ // as part of end-of-block readback insertion, but for these
1306+ // mid-tree readbacks we cannot query liveness information for
1307+ // arbitrary locals.
1308+ if (!rep.NeedsReadBack )
1309+ {
1310+ continue ;
1311+ }
1312+
1313+ rep.NeedsReadBack = false ;
1314+ GenTree* readBack = Promotion::CreateReadBack (m_compiler, agg->LclNum , rep);
1315+ *use =
1316+ m_compiler->gtNewOperNode (GT_COMMA, (*use)->IsValue () ? (*use)->TypeGet () : TYP_VOID, readBack, *use);
1317+ use = &(*use)->AsOp ()->gtOp2 ;
1318+ m_madeChanges = true ;
1319+ }
1320+ }
1321+
1322+ m_hasPendingReadBacks = false ;
1323+ return use;
1324+ }
1325+
11941326// ------------------------------------------------------------------------
11951327// LoadStoreAroundCall:
11961328// Handle a call that may involve struct local arguments and that may
@@ -1237,7 +1369,10 @@ void ReplaceVisitor::LoadStoreAroundCall(GenTreeCall* call, GenTree* user)
12371369 GenTreeLclVarCommon* retBufLcl = retBufArg->GetNode ()->AsLclVarCommon ();
12381370 unsigned size = m_compiler->typGetObjLayout (call->gtRetClsHnd )->GetSize ();
12391371
1240- MarkForReadBack (retBufLcl->GetLclNum (), retBufLcl->GetLclOffs (), size);
1372+ if (MarkForReadBack (retBufLcl->GetLclNum (), retBufLcl->GetLclOffs (), size))
1373+ {
1374+ JITDUMP (" Retbuf has replacements that were marked for read back\n " );
1375+ }
12411376 }
12421377}
12431378
@@ -1269,10 +1404,9 @@ bool ReplaceVisitor::IsPromotedStructLocalDying(GenTreeLclVarCommon* lcl)
12691404 }
12701405
12711406 AggregateInfo* agg = m_aggregates[lcl->GetLclNum ()];
1272-
1273- for (size_t i = 0 ; i < agg->Replacements .size (); i++)
1407+ for (Replacement& rep : agg->Replacements )
12741408 {
1275- if (agg-> Replacements [i] .NeedsReadBack )
1409+ if (rep .NeedsReadBack )
12761410 {
12771411 return false ;
12781412 }
@@ -1487,11 +1621,11 @@ void ReplaceVisitor::WriteBackBefore(GenTree** use, unsigned lcl, unsigned offs,
14871621// offs - The starting offset of the range in the struct local that needs to be read back from.
14881622// size - The size of the range
14891623//
1490- void ReplaceVisitor::MarkForReadBack (unsigned lcl, unsigned offs, unsigned size)
1624+ bool ReplaceVisitor::MarkForReadBack (unsigned lcl, unsigned offs, unsigned size)
14911625{
14921626 if (m_aggregates[lcl] == nullptr )
14931627 {
1494- return ;
1628+ return false ;
14951629 }
14961630
14971631 jitstd::vector<Replacement>& replacements = m_aggregates[lcl]->Replacements ;
@@ -1506,17 +1640,20 @@ void ReplaceVisitor::MarkForReadBack(unsigned lcl, unsigned offs, unsigned size)
15061640 }
15071641 }
15081642
1509- bool result = false ;
1510- unsigned end = offs + size;
1643+ bool any = false ;
1644+ unsigned end = offs + size;
15111645 while ((index < replacements.size ()) && (replacements[index].Offset < end))
15121646 {
1513- result = true ;
1647+ any = true ;
15141648 Replacement& rep = replacements[index];
15151649 assert (rep.Overlaps (offs, size));
1516- rep.NeedsReadBack = true ;
1517- rep.NeedsWriteBack = false ;
1650+ rep.NeedsReadBack = true ;
1651+ rep.NeedsWriteBack = false ;
1652+ m_hasPendingReadBacks = true ;
15181653 index++;
15191654 }
1655+
1656+ return any;
15201657}
15211658
15221659// ------------------------------------------------------------------------
@@ -1631,10 +1768,12 @@ PhaseStatus Promotion::Run()
16311768 ReplaceVisitor replacer (this , aggregates, &liveness);
16321769 for (BasicBlock* bb : m_compiler->Blocks ())
16331770 {
1771+ replacer.StartBlock (bb);
1772+
16341773 for (Statement* stmt : bb->Statements ())
16351774 {
16361775 DISPSTMT (stmt);
1637- replacer.Reset ();
1776+ replacer.StartStatement ();
16381777 replacer.WalkTree (stmt->GetRootNodePointer (), nullptr );
16391778
16401779 if (replacer.MadeChanges ())
@@ -1646,42 +1785,7 @@ PhaseStatus Promotion::Run()
16461785 }
16471786 }
16481787
1649- for (unsigned i = 0 ; i < numLocals; i++)
1650- {
1651- if (aggregates[i] == nullptr )
1652- {
1653- continue ;
1654- }
1655-
1656- for (size_t j = 0 ; j < aggregates[i]->Replacements .size (); j++)
1657- {
1658- Replacement& rep = aggregates[i]->Replacements [j];
1659- assert (!rep.NeedsReadBack || !rep.NeedsWriteBack );
1660- if (rep.NeedsReadBack )
1661- {
1662- if (liveness.IsReplacementLiveOut (bb, i, (unsigned )j))
1663- {
1664- JITDUMP (" Reading back replacement V%02u.[%03u..%03u) -> V%02u near the end of " FMT_BB " :\n " , i,
1665- rep.Offset , rep.Offset + genTypeSize (rep.AccessType ), rep.LclNum , bb->bbNum );
1666-
1667- GenTree* readBack = CreateReadBack (m_compiler, i, rep);
1668- Statement* stmt = m_compiler->fgNewStmtFromTree (readBack);
1669- DISPSTMT (stmt);
1670- m_compiler->fgInsertStmtNearEnd (bb, stmt);
1671- }
1672- else
1673- {
1674- JITDUMP (
1675- " Skipping reading back dead replacement V%02u.[%03u..%03u) -> V%02u near the end of " FMT_BB
1676- " \n " ,
1677- i, rep.Offset , rep.Offset + genTypeSize (rep.AccessType ), rep.LclNum , bb->bbNum );
1678- }
1679- rep.NeedsReadBack = false ;
1680- }
1681-
1682- rep.NeedsWriteBack = true ;
1683- }
1684- }
1788+ replacer.EndBlock ();
16851789 }
16861790
16871791 // Insert initial IR to read arguments/OSR locals into replacement locals,
0 commit comments