Skip to content

Commit 030e62c

Browse files
authored
[RuntimeAsync] Model async continuation definition via delay-free (#3090)
Instead of introducing a new mechanism to mark the async continuation register as busy until a kill we can keep use the delay-free mechanism, provided we take a bit care about where we insert the ref positions and about ensuring the async continuation ends up in the right spot.
1 parent 74fed3e commit 030e62c

File tree

11 files changed

+47
-78
lines changed

11 files changed

+47
-78
lines changed

src/coreclr/jit/lower.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -733,8 +733,7 @@ GenTree* Lowering::LowerNode(GenTree* node)
733733
break;
734734

735735
case GT_ASYNC_CONTINUATION:
736-
LowerAsyncContinuation(node);
737-
break;
736+
return LowerAsyncContinuation(node);
738737

739738
case GT_RETURN_SUSPEND:
740739
LowerReturnSuspend(node);
@@ -5424,14 +5423,21 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret)
54245423
// Arguments:
54255424
// asyncCont - Async continuation node
54265425
//
5427-
void Lowering::LowerAsyncContinuation(GenTree* asyncCont)
5426+
// Returns:
5427+
// Next node to lower.
5428+
//
5429+
GenTree* Lowering::LowerAsyncContinuation(GenTree* asyncCont)
54285430
{
54295431
assert(asyncCont->OperIs(GT_ASYNC_CONTINUATION));
54305432

5431-
// When the ASYNC_CONTINUATION was created as a result of
5432-
// StubHelpers.AsyncCallContinuation() the previous call hasn't been
5433-
// marked as an async call. We need to do that to get the right GC
5434-
// reporting behavior for the returned async continuation.
5433+
GenTree* next = asyncCont->gtNext;
5434+
5435+
// When the ASYNC_CONTINUATION was created as a result of the
5436+
// AsyncCallContinuation() intrinsic the previous call hasn't been marked
5437+
// as an async call. We need to do that to get the right GC reporting
5438+
// behavior for the returned async continuation. Furthermore, we ensure the
5439+
// async continuation follows the call to simplify marking the registers
5440+
// busy in LSRA.
54355441
GenTree* node = asyncCont;
54365442
while (true)
54375443
{
@@ -5447,9 +5453,13 @@ void Lowering::LowerAsyncContinuation(GenTree* asyncCont)
54475453
node->AsCall()->gtIsAsyncCall = true;
54485454
}
54495455

5456+
BlockRange().Remove(asyncCont);
5457+
BlockRange().InsertAfter(node, asyncCont);
54505458
break;
54515459
}
54525460
}
5461+
5462+
return next;
54535463
}
54545464

54555465
//----------------------------------------------------------------------------------------------

src/coreclr/jit/lower.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class Lowering final : public Phase
180180
GenTree* LowerStoreLocCommon(GenTreeLclVarCommon* lclVar);
181181
void LowerRetStruct(GenTreeUnOp* ret);
182182
void LowerRetSingleRegStructLclVar(GenTreeUnOp* ret);
183-
void LowerAsyncContinuation(GenTree* asyncCont);
183+
GenTree* LowerAsyncContinuation(GenTree* asyncCont);
184184
void LowerReturnSuspend(GenTree* retSuspend);
185185
void LowerRetFieldList(GenTreeOp* ret, GenTreeFieldList* fieldList);
186186
bool IsFieldListCompatibleWithReturn(GenTreeFieldList* fieldList);

src/coreclr/jit/lsra.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3902,11 +3902,6 @@ void LinearScan::processKills(RefPosition* killRefPosition)
39023902
regsBusyUntilKill &= ~killRefPosition->getKilledRegisters();
39033903
INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_KILL_REGS, nullptr, REG_NA, nullptr, NONE,
39043904
killRefPosition->getKilledRegisters()));
3905-
3906-
if (killRefPosition->busyUntilNextKill)
3907-
{
3908-
regsBusyUntilKill |= killRefPosition->getKilledRegisters();
3909-
}
39103905
}
39113906

39123907
//------------------------------------------------------------------------

src/coreclr/jit/lsra.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2603,9 +2603,6 @@ class RefPosition
26032603
unsigned char liveVarUpperSave : 1;
26042604
#endif
26052605

2606-
// For a phys reg, indicates that it should be marked as busy until the next time it is killed.
2607-
unsigned char busyUntilNextKill : 1;
2608-
26092606
#ifdef DEBUG
26102607
// Minimum number registers that needs to be ensured while
26112608
// constraining candidates for this ref position under
@@ -2649,7 +2646,6 @@ class RefPosition
26492646
, isLocalDefUse(false)
26502647
, delayRegFree(false)
26512648
, outOfOrder(false)
2652-
, busyUntilNextKill(false)
26532649
#ifdef DEBUG
26542650
, minRegCandidateCount(1)
26552651
, rpNum(0)

src/coreclr/jit/lsraarm.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -634,11 +634,6 @@ int LinearScan::BuildNode(GenTree* tree)
634634
case GT_ASYNC_CONTINUATION:
635635
srcCount = 0;
636636
assert(dstCount == 1);
637-
// We kill the continuation arg here to communicate to the
638-
// selection phase that the argument is no longer busy. This is a
639-
// hack to make sure we do not overwrite the continuation between
640-
// the call and this node.
641-
addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc);
642637
BuildDef(tree, RBM_ASYNC_CONTINUATION_RET.GetIntRegSet());
643638
break;
644639

src/coreclr/jit/lsraarm64.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,12 +1322,6 @@ int LinearScan::BuildNode(GenTree* tree)
13221322

13231323
case GT_ASYNC_CONTINUATION:
13241324
srcCount = 0;
1325-
assert(dstCount == 1);
1326-
// We kill the continuation arg here to communicate to the
1327-
// selection phase that the argument is no longer busy. This is a
1328-
// hack to make sure we do not overwrite the continuation between
1329-
// the call and this node.
1330-
addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc);
13311325
BuildDef(tree, RBM_ASYNC_CONTINUATION_RET.GetIntRegSet());
13321326
break;
13331327

src/coreclr/jit/lsraarmarch.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,11 @@ int LinearScan::BuildCall(GenTreeCall* call)
274274
buildInternalRegisterUses();
275275

276276
// Now generate defs and kills.
277+
if (call->IsAsync() && compiler->compIsAsync() && !call->IsFastTailCall())
278+
{
279+
MarkAsyncContinuationBusyForCall(call);
280+
}
281+
277282
regMaskTP killMask = getKillSetForCall(call);
278283
if (dstCount > 0)
279284
{
@@ -302,11 +307,6 @@ int LinearScan::BuildCall(GenTreeCall* call)
302307
}
303308
#endif // SWIFT_SUPPORT
304309

305-
if (call->IsAsync() && compiler->compIsAsync())
306-
{
307-
MarkAsyncContinuationBusyForCall(call);
308-
}
309-
310310
// No args are placed in registers anymore.
311311
placedArgRegs = RBM_NONE;
312312
numPlacedArgLocals = 0;

src/coreclr/jit/lsrabuild.cpp

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4754,16 +4754,13 @@ void LinearScan::MarkSwiftErrorBusyForCall(GenTreeCall* call)
47544754
//
47554755
void LinearScan::MarkAsyncContinuationBusyForCall(GenTreeCall* call)
47564756
{
4757-
// Async calls return an async continuation argument in a separate
4758-
// register. Since we do not have a flexible representation for
4759-
// multiple definitions (multi-reg support is tied into promotion) we
4760-
// have to utilize a hack here to make it work. We expect the return
4761-
// value to be consumed by an upcoming ASYNC_CONTINUATION node, but we
4762-
// must take care not to overwrite the register until we get to that
4763-
// node. To accomplish that we mark the register as "busy until next
4764-
// kill" when we see the call's kill, and then we have
4765-
// ASYNC_CONTINUATION insert its own kill to free up the register
4766-
// again.
4767-
RefPosition* refPos = addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc + 1);
4768-
refPos->busyUntilNextKill = true;
4757+
// We model the async continuation like the swift error register: we ensure
4758+
// the node follows the call in lowering, and make it delay freed to ensure
4759+
// nothing is allocated into the register between the call and
4760+
// ASYNC_CONTINUATION node. We need to add a kill here in the right spot as
4761+
// not all targets may naturally have one created.
4762+
assert(call->gtNext != nullptr);
4763+
assert(call->gtNext->OperIs(GT_ASYNC_CONTINUATION));
4764+
RefPosition* refPos = addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc + 1);
4765+
setDelayFree(refPos);
47694766
}

src/coreclr/jit/lsraloongarch64.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -565,12 +565,6 @@ int LinearScan::BuildNode(GenTree* tree)
565565

566566
case GT_ASYNC_CONTINUATION:
567567
srcCount = 0;
568-
assert(dstCount == 1);
569-
// We kill the continuation arg here to communicate to the
570-
// selection phase that the argument is no longer busy. This is a
571-
// hack to make sure we do not overwrite the continuation between
572-
// the call and this node.
573-
addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc);
574568
BuildDef(tree, RBM_ASYNC_CONTINUATION_RET.GetIntRegSet());
575569
break;
576570

@@ -793,6 +787,11 @@ int LinearScan::BuildCall(GenTreeCall* call)
793787
buildInternalRegisterUses();
794788

795789
// Now generate defs and kills.
790+
if (call->IsAsync() && compiler->compIsAsync() && !call->IsFastTailCall())
791+
{
792+
MarkAsyncContinuationBusyForCall(call);
793+
}
794+
796795
regMaskTP killMask = getKillSetForCall(call);
797796
if (dstCount > 0)
798797
{
@@ -814,11 +813,6 @@ int LinearScan::BuildCall(GenTreeCall* call)
814813
BuildKills(call, killMask);
815814
}
816815

817-
if (call->IsAsync() && compiler->compIsAsync())
818-
{
819-
MarkAsyncContinuationBusyForCall(call);
820-
}
821-
822816
// No args are placed in registers anymore.
823817
placedArgRegs = RBM_NONE;
824818
numPlacedArgLocals = 0;

src/coreclr/jit/lsrariscv64.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -758,12 +758,6 @@ int LinearScan::BuildNode(GenTree* tree)
758758

759759
case GT_ASYNC_CONTINUATION:
760760
srcCount = 0;
761-
assert(dstCount == 1);
762-
// We kill the continuation arg here to communicate to the
763-
// selection phase that the argument is no longer busy. This is a
764-
// hack to make sure we do not overwrite the continuation between
765-
// the call and this node.
766-
addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc);
767761
BuildDef(tree, RBM_ASYNC_CONTINUATION_RET.GetIntRegSet());
768762
break;
769763

@@ -998,6 +992,11 @@ int LinearScan::BuildCall(GenTreeCall* call)
998992
buildInternalRegisterUses();
999993

1000994
// Now generate defs and kills.
995+
if (call->IsAsync() && compiler->compIsAsync() && !call->IsFastTailCall())
996+
{
997+
MarkAsyncContinuationBusyForCall(call);
998+
}
999+
10011000
regMaskTP killMask = getKillSetForCall(call);
10021001
if (dstCount > 0)
10031002
{
@@ -1019,11 +1018,6 @@ int LinearScan::BuildCall(GenTreeCall* call)
10191018
BuildKills(call, killMask);
10201019
}
10211020

1022-
if (call->IsAsync() && compiler->compIsAsync())
1023-
{
1024-
MarkAsyncContinuationBusyForCall(call);
1025-
}
1026-
10271021
// No args are placed in registers anymore.
10281022
placedArgRegs = RBM_NONE;
10291023
numPlacedArgLocals = 0;

0 commit comments

Comments
 (0)