Skip to content

Commit a7f7ee7

Browse files
committed
JIT: Share suspension epilogs in async transformation
If we have more than one await, then create a shared `GT_RETURN_SUSPEND` block and use that for suspension always.
1 parent 4a6399a commit a7f7ee7

2 files changed

Lines changed: 120 additions & 54 deletions

File tree

src/coreclr/jit/async.cpp

Lines changed: 113 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,43 +1117,39 @@ PhaseStatus Compiler::TransformAsync()
11171117
PhaseStatus AsyncTransformation::Run()
11181118
{
11191119
PhaseStatus result = PhaseStatus::MODIFIED_NOTHING;
1120-
ArrayStack<BasicBlock*> worklist(m_compiler->getAllocator(CMK_Async));
1120+
ArrayStack<BasicBlock*> blocksWithNormalAwaits(m_compiler->getAllocator(CMK_Async));
1121+
ArrayStack<BasicBlock*> blocksWithTailAwaits(m_compiler->getAllocator(CMK_Async));
1122+
int numNormalAwaits = 0;
1123+
int numTailAwaits = 0;
1124+
FindAwaits(blocksWithNormalAwaits, blocksWithTailAwaits, &numNormalAwaits, &numTailAwaits);
11211125

1122-
// First find all basic blocks with awaits in them. We'll have to track
1123-
// liveness in these basic blocks, so it does not help to record the calls
1124-
// ahead of time.
1125-
BasicBlock* nextBlock;
1126-
for (BasicBlock* block = m_compiler->fgFirstBB; block != nullptr; block = nextBlock)
1126+
if (numNormalAwaits + numTailAwaits > 1)
11271127
{
1128-
bool hasAwait = false;
1129-
nextBlock = block->Next();
1130-
for (GenTree* tree : LIR::AsRange(block))
1131-
{
1132-
if (!tree->IsCall() || !tree->AsCall()->IsAsync() || tree->AsCall()->IsTailCall())
1133-
{
1134-
continue;
1135-
}
1136-
1137-
if (tree->AsCall()->GetAsyncInfo().IsTailAwait)
1138-
{
1139-
TransformTailAwait(block, tree->AsCall(), &nextBlock);
1140-
result = PhaseStatus::MODIFIED_EVERYTHING;
1141-
break;
1142-
}
1143-
1144-
JITDUMP(FMT_BB " contains await(s)\n", block->bbNum);
1145-
hasAwait = true;
1146-
}
1128+
CreateSharedReturnBB();
1129+
}
11471130

1148-
if (hasAwait)
1131+
// Transform all tail awaits first. They will not require running all of
1132+
// our analyses.
1133+
if (numTailAwaits > 0)
1134+
{
1135+
JITDUMP("Found %d tail awaits in %d blocks\n", numTailAwaits, blocksWithTailAwaits.Height());
1136+
TransformTailAwaits(blocksWithTailAwaits);
1137+
if (numNormalAwaits > 0)
11491138
{
1150-
worklist.Push(block);
1139+
blocksWithNormalAwaits.Reset();
1140+
blocksWithTailAwaits.Reset();
1141+
numNormalAwaits = 0;
1142+
numTailAwaits = 0;
1143+
FindAwaits(blocksWithNormalAwaits, blocksWithTailAwaits, &numNormalAwaits, &numTailAwaits);
1144+
assert((numTailAwaits == 0) && (blocksWithTailAwaits.Height() == 0));
11511145
}
1146+
1147+
result = PhaseStatus::MODIFIED_EVERYTHING;
11521148
}
11531149

1154-
JITDUMP("Found %d blocks with awaits\n", worklist.Height());
1150+
JITDUMP("Found %d awaits in %d blocks\n", numNormalAwaits, blocksWithNormalAwaits.Height());
11551151

1156-
if (worklist.Height() <= 0)
1152+
if (numNormalAwaits <= 0)
11571153
{
11581154
return result;
11591155
}
@@ -1165,9 +1161,6 @@ PhaseStatus AsyncTransformation::Run()
11651161

11661162
m_asyncInfo = m_compiler->eeGetAsyncInfo();
11671163

1168-
// Create the shared return BB now to put it in the right place in the block order.
1169-
GetSharedReturnBB();
1170-
11711164
// Compute liveness to be used for determining what must be captured on
11721165
// suspension.
11731166
if (m_compiler->m_dfsTree == nullptr)
@@ -1189,11 +1182,11 @@ PhaseStatus AsyncTransformation::Run()
11891182
// async calls are additional live variables that must be spilled.
11901183
jitstd::vector<GenTree*> defs(m_compiler->getAllocator(CMK_Async));
11911184

1192-
for (int i = 0; i < worklist.Height(); i++)
1185+
for (int i = 0; i < blocksWithNormalAwaits.Height(); i++)
11931186
{
11941187
assert(defs.size() == 0);
11951188

1196-
BasicBlock* block = worklist.Bottom(i);
1189+
BasicBlock* block = blocksWithNormalAwaits.Bottom(i);
11971190
liveness.StartBlock(block);
11981191

11991192
bool any;
@@ -1294,6 +1287,88 @@ PhaseStatus AsyncTransformation::Run()
12941287
return PhaseStatus::MODIFIED_EVERYTHING;
12951288
}
12961289

1290+
//------------------------------------------------------------------------
1291+
// AsyncTransformation::FindAwaits:
1292+
// Find the blocks that have awaits in them and do some accounting of how
1293+
// many awaits there are.
1294+
//
1295+
// Parameters:
1296+
// blocksWithNormalAwaits - [out] Blocks with normal awaits are pushed onto this stack
1297+
// blocksWithTailAwaits - [out] Blocks with tail awaits are pushed onto this stack
1298+
// numNormalAwaits - [out] Number of normal awaits found
1299+
// numTailAwaits - [out] Number of tail awaits found
1300+
//
1301+
void AsyncTransformation::FindAwaits(ArrayStack<BasicBlock*>& blocksWithNormalAwaits,
1302+
ArrayStack<BasicBlock*>& blocksWithTailAwaits,
1303+
int* numNormalAwaits,
1304+
int* numTailAwaits)
1305+
{
1306+
for (BasicBlock* block : m_compiler->Blocks())
1307+
{
1308+
bool hasNormalAwait = false;
1309+
bool hasTailAwait = false;
1310+
for (GenTree* tree : LIR::AsRange(block))
1311+
{
1312+
if (!tree->IsCall() || !tree->AsCall()->IsAsync() || tree->AsCall()->IsTailCall())
1313+
{
1314+
continue;
1315+
}
1316+
1317+
if (tree->AsCall()->GetAsyncInfo().IsTailAwait)
1318+
{
1319+
hasTailAwait = true;
1320+
(*numTailAwaits)++;
1321+
}
1322+
else
1323+
{
1324+
hasNormalAwait = true;
1325+
(*numNormalAwaits)++;
1326+
}
1327+
}
1328+
1329+
if (hasNormalAwait)
1330+
{
1331+
blocksWithNormalAwaits.Push(block);
1332+
}
1333+
1334+
if (hasTailAwait)
1335+
{
1336+
blocksWithTailAwaits.Push(block);
1337+
}
1338+
}
1339+
}
1340+
1341+
//------------------------------------------------------------------------
1342+
// AsyncTransformation::TransformTailAwaits:
1343+
// Transform all tail awaits in the specified blocks.
1344+
//
1345+
// Parameters:
1346+
// blocksWithTailAwaits - Blocks containing tail awaits
1347+
//
1348+
void AsyncTransformation::TransformTailAwaits(ArrayStack<BasicBlock*>& blocksWithTailAwaits)
1349+
{
1350+
for (int i = 0; i < blocksWithTailAwaits.Height(); i++)
1351+
{
1352+
BasicBlock* block = blocksWithTailAwaits.Bottom(i);
1353+
1354+
bool any;
1355+
do
1356+
{
1357+
any = false;
1358+
for (GenTree* tree : LIR::AsRange(block))
1359+
{
1360+
if (tree->IsCall() && tree->AsCall()->IsAsync() && !tree->AsCall()->IsTailCall() &&
1361+
tree->AsCall()->GetAsyncInfo().IsTailAwait)
1362+
{
1363+
TransformTailAwait(block, tree->AsCall(), &block);
1364+
any = true;
1365+
break;
1366+
}
1367+
}
1368+
} while (any);
1369+
}
1370+
}
1371+
12971372
//------------------------------------------------------------------------
12981373
// AsyncTransformation::TransformTailAwait:
12991374
// Transform an await that was marked as a tail await.
@@ -1328,7 +1403,7 @@ void AsyncTransformation::TransformTailAwait(BasicBlock* block, GenTreeCall* cal
13281403
//
13291404
BasicBlock* AsyncTransformation::CreateTailAwaitSuspension(BasicBlock* block, GenTreeCall* call)
13301405
{
1331-
BasicBlock* sharedReturnBB = GetSharedReturnBB();
1406+
BasicBlock* sharedReturnBB = m_sharedReturnBB;
13321407

13331408
if (m_lastSuspensionBB == nullptr)
13341409
{
@@ -3173,21 +3248,11 @@ unsigned AsyncTransformation::GetExceptionVar()
31733248
}
31743249

31753250
//------------------------------------------------------------------------
3176-
// AsyncTransformation::GetSharedReturnBB:
3177-
// Create the shared return BB, if one is needed.
3178-
//
3179-
// Returns:
3180-
// Basic block or nullptr.
3251+
// AsyncTransformation::CreateSharedReturnBB:
3252+
// Create the shared return BB.
31813253
//
3182-
BasicBlock* AsyncTransformation::GetSharedReturnBB()
3254+
void AsyncTransformation::CreateSharedReturnBB()
31833255
{
3184-
#ifdef JIT32_GCENCODER
3185-
if (m_sharedReturnBB != nullptr)
3186-
{
3187-
return m_sharedReturnBB;
3188-
}
3189-
3190-
// Due to a hard cap on epilogs we need a shared return here.
31913256
m_sharedReturnBB = m_compiler->fgNewBBafter(BBJ_RETURN, m_compiler->fgLastBBInMainFunction(), false);
31923257
m_sharedReturnBB->bbSetRunRarely();
31933258
m_sharedReturnBB->clearTryIndex();
@@ -3207,10 +3272,6 @@ BasicBlock* AsyncTransformation::GetSharedReturnBB()
32073272
JITDUMP("Created shared return BB " FMT_BB "\n", m_sharedReturnBB->bbNum);
32083273

32093274
DISPRANGE(LIR::AsRange(m_sharedReturnBB));
3210-
return m_sharedReturnBB;
3211-
#else
3212-
return nullptr;
3213-
#endif
32143275
}
32153276

32163277
//------------------------------------------------------------------------

src/coreclr/jit/async.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,12 @@ class AsyncTransformation
204204
BasicBlock* m_lastResumptionBB = nullptr;
205205
BasicBlock* m_sharedReturnBB = nullptr;
206206

207+
void FindAwaits(ArrayStack<BasicBlock*>& blocksWithNormalAwaits,
208+
ArrayStack<BasicBlock*>& blocksWithTailAwaits,
209+
int* numNormalAwaits,
210+
int* numTailAwaits);
211+
212+
void TransformTailAwaits(ArrayStack<BasicBlock*>& blocksWithTailAwaits);
207213
void TransformTailAwait(BasicBlock* block, GenTreeCall* call, BasicBlock** remainder);
208214
BasicBlock* CreateTailAwaitSuspension(BasicBlock* block, GenTreeCall* call);
209215

@@ -289,8 +295,7 @@ class AsyncTransformation
289295
unsigned GetNewContinuationVar();
290296
unsigned GetResultBaseVar();
291297
unsigned GetExceptionVar();
292-
BasicBlock* GetSharedReturnBB();
293-
298+
void CreateSharedReturnBB();
294299
bool ReuseContinuations();
295300
void CreateResumptionsAndSuspensions();
296301
void CreateResumptionSwitch();

0 commit comments

Comments
 (0)