@@ -1104,6 +1104,42 @@ void Compiler::fgExtendDbgLifetimes()
1104
1104
#endif // DEBUG
1105
1105
}
1106
1106
1107
+ // ------------------------------------------------------------------------
1108
+ // fgGetHandlerLiveVars: determine set of locals live because of implicit
1109
+ // exception flow from a block.
1110
+ //
1111
+ // Arguments:
1112
+ // block - the block in question
1113
+ //
1114
+ // Returns:
1115
+ // Additional set of locals to be considered live throughout the block.
1116
+ //
1117
+ // Notes:
1118
+ // Assumes caller has screened candidate blocks to only those with
1119
+ // exception flow, via `ehBlockHasExnFlowDsc`.
1120
+ //
1121
+ // Exception flow can arise because of a newly raised exception (for
1122
+ // blocks within try regions) or because of an actively propagating exception
1123
+ // (for filter blocks). This flow effectively creates additional successor
1124
+ // edges in the flow graph that the jit does not model. This method computes
1125
+ // the net contribution from all the missing successor edges.
1126
+ //
1127
+ // For example, with the following C# source, during EH processing of the throw,
1128
+ // the outer filter will execute in pass1, before the inner handler executes
1129
+ // in pass2, and so the filter blocks should show the inner handler's local is live.
1130
+ //
1131
+ // try
1132
+ // {
1133
+ // using (AllocateObject()) // ==> try-finally; handler calls Dispose
1134
+ // {
1135
+ // throw new Exception();
1136
+ // }
1137
+ // }
1138
+ // catch (Exception e1) when (IsExpectedException(e1))
1139
+ // {
1140
+ // Console.WriteLine("In catch 1");
1141
+ // }
1142
+
1107
1143
VARSET_VALRET_TP Compiler::fgGetHandlerLiveVars (BasicBlock* block)
1108
1144
{
1109
1145
noway_assert (block);
@@ -1115,7 +1151,6 @@ VARSET_VALRET_TP Compiler::fgGetHandlerLiveVars(BasicBlock* block)
1115
1151
do
1116
1152
{
1117
1153
/* Either we enter the filter first or the catch/finally */
1118
-
1119
1154
if (HBtab->HasFilter ())
1120
1155
{
1121
1156
VarSetOps::UnionD (this , liveVars, HBtab->ebdFilter ->bbLiveIn );
@@ -1147,6 +1182,72 @@ VARSET_VALRET_TP Compiler::fgGetHandlerLiveVars(BasicBlock* block)
1147
1182
1148
1183
} while (true );
1149
1184
1185
+ // If this block is within a filter, we also need to report as live
1186
+ // any vars live into enclosed finally or fault handlers, since the
1187
+ // filter will run during the first EH pass, and enclosed or enclosing
1188
+ // handlers will run during the second EH pass. So all these handlers
1189
+ // are "exception flow" successors of the filter.
1190
+ //
1191
+ // Note we are relying on ehBlockHasExnFlowDsc to return true
1192
+ // for any filter block that we should examine here.
1193
+ if (block->hasHndIndex ())
1194
+ {
1195
+ const unsigned thisHndIndex = block->getHndIndex ();
1196
+ EHblkDsc* enclosingHBtab = ehGetDsc (thisHndIndex);
1197
+
1198
+ if (enclosingHBtab->InFilterRegionBBRange (block))
1199
+ {
1200
+ assert (enclosingHBtab->HasFilter ());
1201
+
1202
+ // Search the EH table for enclosed regions.
1203
+ //
1204
+ // All the enclosed regions will be lower numbered and
1205
+ // immediately prior to and contiguous with the enclosing
1206
+ // region in the EH tab.
1207
+ unsigned index = thisHndIndex;
1208
+
1209
+ while (index > 0 )
1210
+ {
1211
+ index--;
1212
+ unsigned enclosingIndex = ehGetEnclosingTryIndex (index);
1213
+ bool isEnclosed = false ;
1214
+
1215
+ // To verify this is an enclosed region, search up
1216
+ // through the enclosing regions until we find the
1217
+ // region associated with the filter.
1218
+ while (enclosingIndex != EHblkDsc::NO_ENCLOSING_INDEX)
1219
+ {
1220
+ if (enclosingIndex == thisHndIndex)
1221
+ {
1222
+ isEnclosed = true ;
1223
+ break ;
1224
+ }
1225
+
1226
+ enclosingIndex = ehGetEnclosingTryIndex (enclosingIndex);
1227
+ }
1228
+
1229
+ // If we found an enclosed region, check if the region
1230
+ // is a try fault or try finally, and if so, add any
1231
+ // locals live into the enclosed region's handler into this
1232
+ // block's live-in set.
1233
+ if (isEnclosed)
1234
+ {
1235
+ EHblkDsc* enclosedHBtab = ehGetDsc (index);
1236
+
1237
+ if (enclosedHBtab->HasFinallyOrFaultHandler ())
1238
+ {
1239
+ VarSetOps::UnionD (this , liveVars, enclosedHBtab->ebdHndBeg ->bbLiveIn );
1240
+ }
1241
+ }
1242
+ // Once we run across a non-enclosed region, we can stop searching.
1243
+ else
1244
+ {
1245
+ break ;
1246
+ }
1247
+ }
1248
+ }
1249
+ }
1250
+
1150
1251
return liveVars;
1151
1252
}
1152
1253
@@ -1219,14 +1320,17 @@ class LiveVarAnalysis
1219
1320
// since (without proof otherwise) the use and def may touch different memory at run-time.
1220
1321
m_memoryLiveIn = m_memoryLiveOut | block->bbMemoryUse ;
1221
1322
1222
- /* Can exceptions from this block be handled (in this function)? */
1223
-
1323
+ // Does this block have implicit exception flow to a filter or handler?
1324
+ // If so, include the effects of that flow.
1224
1325
if (m_compiler->ehBlockHasExnFlowDsc (block))
1225
1326
{
1226
1327
const VARSET_TP& liveVars (m_compiler->fgGetHandlerLiveVars (block));
1227
-
1228
1328
VarSetOps::UnionD (m_compiler, m_liveIn, liveVars);
1229
1329
VarSetOps::UnionD (m_compiler, m_liveOut, liveVars);
1330
+
1331
+ // Implicit eh edges can induce loop-like behavior,
1332
+ // so make sure we iterate to closure.
1333
+ m_hasPossibleBackEdge = true ;
1230
1334
}
1231
1335
1232
1336
/* Has there been any change in either live set? */
@@ -3093,7 +3197,6 @@ void Compiler::fgInterBlockLocalVarLiveness()
3093
3197
if (block->bbCatchTyp != BBCT_NONE)
3094
3198
{
3095
3199
/* Note the set of variables live on entry to exception handler */
3096
-
3097
3200
VarSetOps::UnionD (this , exceptVars, block->bbLiveIn );
3098
3201
}
3099
3202
0 commit comments