Skip to content

Commit 544cf0c

Browse files
authored
JIT: fix issue in cloning loops with trys in handlers (#113586)
Loop cloning with EH had a bug when: * the loop contained a try T0 that was within a handler H1 * the associated try T1 was also within the loop * the entire loop was within another try T2 Here T0's containing try is T2, and its enclosing try index was properly adjusted when the EH table expands as part of cloning. We were doing another index adjustment later which lead to an out of bounds index. Also cleaned up the code that detects which try regions need cloning as in the above we only need to consider T1. When we go to clone it we also will clone H1 and hence T2. Also fixed/added some more dumping, and some new test cases. This fix addresses a problem that came up stress testing #112998. Since the underlying bug does not require inlining to create the EH setup above, I am fixing it separately.
1 parent 5c4a80e commit 544cf0c

File tree

3 files changed

+170
-10
lines changed

3 files changed

+170
-10
lines changed

src/coreclr/jit/fgehopt.cpp

+10-4
Original file line numberDiff line numberDiff line change
@@ -2680,8 +2680,8 @@ BasicBlock* Compiler::fgCloneTryRegion(BasicBlock* tryEntry, CloneTryInfo& info,
26802680
if (bbIsTryBeg(block))
26812681
{
26822682
assert(added);
2683-
JITDUMP("==> found try entry for EH#%02u nested in handler at " FMT_BB "\n", block->bbNum,
2684-
block->getTryIndex());
2683+
JITDUMP("==> found try entry for EH#%02u nested in handler at " FMT_BB "\n", block->getTryIndex(),
2684+
block->bbNum);
26852685
regionsToProcess.Push(block->getTryIndex());
26862686
}
26872687
}
@@ -2761,6 +2761,12 @@ BasicBlock* Compiler::fgCloneTryRegion(BasicBlock* tryEntry, CloneTryInfo& info,
27612761
assert(insertBeforeIndex == enclosingTryIndex);
27622762
}
27632763

2764+
if (insertBeforeIndex != compHndBBtabCount)
2765+
{
2766+
JITDUMP("Existing EH region(s) EH#%02u...EH#%02u will become EH#%02u...EH#%02u\n", insertBeforeIndex,
2767+
compHndBBtabCount - 1, insertBeforeIndex + regionCount, compHndBBtabCount + regionCount - 1);
2768+
}
2769+
27642770
// Once we call fgTryAddEHTableEntries with deferCloning = false,
27652771
// all the EH indicies at or above insertBeforeIndex will shift,
27662772
// and the EH table may reallocate.
@@ -2860,7 +2866,7 @@ BasicBlock* Compiler::fgCloneTryRegion(BasicBlock* tryEntry, CloneTryInfo& info,
28602866
//
28612867
if (ebd->ebdEnclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX)
28622868
{
2863-
if (XTnum < clonedOutermostRegionIndex)
2869+
if (ebd->ebdEnclosingTryIndex < clonedOutermostRegionIndex)
28642870
{
28652871
ebd->ebdEnclosingTryIndex += (unsigned short)indexShift;
28662872
}
@@ -2873,7 +2879,7 @@ BasicBlock* Compiler::fgCloneTryRegion(BasicBlock* tryEntry, CloneTryInfo& info,
28732879

28742880
if (ebd->ebdEnclosingHndIndex != EHblkDsc::NO_ENCLOSING_INDEX)
28752881
{
2876-
if (XTnum < clonedOutermostRegionIndex)
2882+
if (ebd->ebdEnclosingHndIndex < clonedOutermostRegionIndex)
28772883
{
28782884
ebd->ebdEnclosingHndIndex += (unsigned short)indexShift;
28792885
}

src/coreclr/jit/flowgraph.cpp

+22-6
Original file line numberDiff line numberDiff line change
@@ -6140,14 +6140,30 @@ bool FlowGraphNaturalLoop::CanDuplicateWithEH(INDEBUG(const char** reason))
61406140
// Check if this is an "outermost" try within the loop.
61416141
// If so, we have more checking to do later on.
61426142
//
6143-
const bool headerInTry = header->hasTryIndex();
6144-
unsigned blockIndex = block->getTryIndex();
6145-
unsigned outermostBlockIndex = comp->ehTrueEnclosingTryIndex(blockIndex);
6143+
bool const headerIsInTry = header->hasTryIndex();
6144+
unsigned const blockTryIndex = block->getTryIndex();
6145+
unsigned const enclosingTryIndex = comp->ehTrueEnclosingTryIndex(blockTryIndex);
61466146

6147-
if ((headerInTry && (outermostBlockIndex == header->getTryIndex())) ||
6148-
(!headerInTry && (outermostBlockIndex == EHblkDsc::NO_ENCLOSING_INDEX)))
6147+
if ((headerIsInTry && (enclosingTryIndex == header->getTryIndex())) ||
6148+
(!headerIsInTry && (enclosingTryIndex == EHblkDsc::NO_ENCLOSING_INDEX)))
61496149
{
6150-
tryRegionsToClone.Push(block);
6150+
// When we clone a try we also clone its handler.
6151+
//
6152+
// This try may be enclosed in a handler whose try begin is in the loop.
6153+
// If so we'll clone this try when we clone (the handler of) that try.
6154+
//
6155+
bool isInHandlerOfInLoopTry = false;
6156+
if (block->hasHndIndex())
6157+
{
6158+
unsigned const enclosingHndIndex = block->getHndIndex();
6159+
BasicBlock* const associatedTryBeg = comp->ehGetDsc(enclosingHndIndex)->ebdTryBeg;
6160+
isInHandlerOfInLoopTry = this->ContainsBlock(associatedTryBeg);
6161+
}
6162+
6163+
if (!isInHandlerOfInLoopTry)
6164+
{
6165+
tryRegionsToClone.Push(block);
6166+
}
61516167
}
61526168
}
61536169

src/tests/JIT/opt/Cloning/loops_with_eh.cs

+138
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// g - giant finally (TF will remain try finally)
1818
// p - regions are serial, not nested
1919
// TFi - try finally with what follows in the finally
20+
// I - inlining
2021
//
2122
// x: we currently cannot clone loops where the try is the first thing
2223
// as the header and preheader are different regions
@@ -1127,5 +1128,142 @@ public static int Sum_TFiTFxL(int[] data, int n)
11271128

11281129
return sum;
11291130
}
1131+
1132+
[Fact]
1133+
public static int Test_LTFiTF() => Sum_LTFiTF(data, n) - 130;
1134+
1135+
public static int Sum_LTFiTF(int[] data, int n)
1136+
{
1137+
int sum = 0;
1138+
1139+
for (int i = 0; i < n; i++)
1140+
{
1141+
sum += data[i];
1142+
try
1143+
{
1144+
SideEffect();
1145+
}
1146+
finally
1147+
{
1148+
try
1149+
{
1150+
SideEffect();
1151+
}
1152+
finally
1153+
{
1154+
sum += 1;
1155+
}
1156+
1157+
sum += 1;
1158+
}
1159+
}
1160+
1161+
return sum;
1162+
}
1163+
1164+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1165+
public static int SomeEH()
1166+
{
1167+
int sum = 0;
1168+
1169+
try
1170+
{
1171+
SideEffect();
1172+
}
1173+
finally
1174+
{
1175+
try
1176+
{
1177+
SideEffect();
1178+
}
1179+
finally
1180+
{
1181+
sum += 1;
1182+
}
1183+
1184+
sum += 1;
1185+
}
1186+
1187+
return sum;
1188+
}
1189+
1190+
[Fact]
1191+
public static int Test_LITFiTF() => Sum_LITFiTF(data, n) - 130;
1192+
1193+
public static int Sum_LITFiTF(int[] data, int n)
1194+
{
1195+
int sum = 0;
1196+
1197+
for (int i = 0; i < n; i++)
1198+
{
1199+
sum += data[i];
1200+
sum += SomeEH();
1201+
}
1202+
1203+
return sum;
1204+
}
1205+
1206+
[Fact]
1207+
public static int Test_TFLTFiTF() => Sum_TFLTFiTF(data, n) - 131;
1208+
1209+
public static int Sum_TFLTFiTF(int[] data, int n)
1210+
{
1211+
int sum = 0;
1212+
1213+
try
1214+
{
1215+
for (int i = 0; i < n; i++)
1216+
{
1217+
sum += data[i];
1218+
try
1219+
{
1220+
SideEffect();
1221+
}
1222+
finally
1223+
{
1224+
try
1225+
{
1226+
SideEffect();
1227+
}
1228+
finally
1229+
{
1230+
sum += 1;
1231+
}
1232+
1233+
sum += 1;
1234+
}
1235+
}
1236+
}
1237+
finally
1238+
{
1239+
SideEffect();
1240+
sum += 1;
1241+
}
1242+
return sum;
1243+
}
1244+
1245+
// [Fact]
1246+
public static int Test_TFLITFiTF() => Sum_TFLITFiTF(data, n) - 131;
1247+
1248+
public static int Sum_TFLITFiTF(int[] data, int n)
1249+
{
1250+
int sum = 0;
1251+
1252+
try
1253+
{
1254+
for (int i = 0; i < n; i++)
1255+
{
1256+
sum += data[i];
1257+
sum += SomeEH();
1258+
}
1259+
}
1260+
finally
1261+
{
1262+
SideEffect();
1263+
sum += 1;
1264+
}
1265+
1266+
return sum;
1267+
}
11301268
}
11311269

0 commit comments

Comments
 (0)