Skip to content

Commit 924e363

Browse files
committed
Allow normal function results of @yield_once coroutines
1 parent 2f6c4ad commit 924e363

File tree

71 files changed

+615
-292
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+615
-292
lines changed

docs/SIL.rst

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6100,6 +6100,14 @@ executing the ``begin_apply``) were being "called" by the ``yield``:
61006100
or move the value from that position before ending or aborting the
61016101
coroutine.
61026102

6103+
A coroutine optionally may produce normal results. These do not have
6104+
``@yields`` annotation in the result type tuple.
6105+
::
6106+
(%float, %token) = begin_apply %0() : $@yield_once () -> (@yields Float, Int)
6107+
6108+
Normal results of a coroutine are produced by the corresponding ``end_apply``
6109+
instruction.
6110+
61036111
A ``begin_apply`` must be uniquely either ended or aborted before
61046112
exiting the function or looping to an earlier portion of the function.
61056113

@@ -6129,9 +6137,9 @@ end_apply
61296137
`````````
61306138
::
61316139

6132-
sil-instruction ::= 'end_apply' sil-value
6140+
sil-instruction ::= 'end_apply' sil-value 'as' sil-type
61336141

6134-
end_apply %token
6142+
end_apply %token as $()
61356143

61366144
Ends the given coroutine activation, which is currently suspended at
61376145
a ``yield`` instruction. Transfers control to the coroutine and takes
@@ -6141,8 +6149,8 @@ when the coroutine reaches a ``return`` instruction.
61416149
The operand must always be the token result of a ``begin_apply``
61426150
instruction, which is why it need not specify a type.
61436151

6144-
``end_apply`` currently has no instruction results. If coroutines were
6145-
allowed to have normal results, they would be producted by ``end_apply``.
6152+
If a coroutine produces normal results on ``resume`` path, they
6153+
will be produced by ``end_apply``.
61466154

61476155
When throwing coroutines are supported, there will need to be a
61486156
``try_end_apply`` instruction.

include/swift/AST/Types.h

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4789,24 +4789,27 @@ class SILFunctionType final
47894789
using Representation = SILExtInfoBuilder::Representation;
47904790

47914791
private:
4792-
unsigned NumParameters;
4792+
unsigned NumParameters = 0;
47934793

4794-
// These are *normal* results if this is not a coroutine and *yield* results
4795-
// otherwise.
4796-
unsigned NumAnyResults; // Not including the ErrorResult.
4797-
unsigned NumAnyIndirectFormalResults; // Subset of NumAnyResults.
4798-
unsigned NumPackResults; // Subset of NumAnyIndirectFormalResults.
4794+
// These are *normal* results
4795+
unsigned NumAnyResults = 0; // Not including the ErrorResult.
4796+
unsigned NumAnyIndirectFormalResults = 0; // Subset of NumAnyResults.
4797+
unsigned NumPackResults = 0; // Subset of NumAnyIndirectFormalResults.
4798+
// These are *yield* results
4799+
unsigned NumAnyYieldResults = 0; // Not including the ErrorResult.
4800+
unsigned NumAnyIndirectFormalYieldResults = 0; // Subset of NumAnyYieldResults.
4801+
unsigned NumPackYieldResults = 0; // Subset of NumAnyIndirectFormalYieldResults.
47994802

48004803
// [NOTE: SILFunctionType-layout]
48014804
// The layout of a SILFunctionType in memory is:
48024805
// SILFunctionType
48034806
// SILParameterInfo[NumParameters]
4804-
// SILResultInfo[isCoroutine() ? 0 : NumAnyResults]
4807+
// SILResultInfo[NumAnyResults]
48054808
// SILResultInfo? // if hasErrorResult()
4806-
// SILYieldInfo[isCoroutine() ? NumAnyResults : 0]
4809+
// SILYieldInfo[NumAnyYieldResults]
48074810
// SubstitutionMap[HasPatternSubs + HasInvocationSubs]
4808-
// CanType? // if !isCoro && NumAnyResults > 1, formal result cache
4809-
// CanType? // if !isCoro && NumAnyResults > 1, all result cache
4811+
// CanType? // if NumAnyResults > 1, formal result cache
4812+
// CanType? // if NumAnyResults > 1, all result cache
48104813

48114814
CanGenericSignature InvocationGenericSig;
48124815
ProtocolConformanceRef WitnessMethodConformance;
@@ -4845,7 +4848,7 @@ class SILFunctionType final
48454848

48464849
/// Do we have slots for caches of the normal-result tuple type?
48474850
bool hasResultCache() const {
4848-
return NumAnyResults > 1 && !isCoroutine();
4851+
return NumAnyResults > 1;
48494852
}
48504853

48514854
CanType &getMutableFormalResultsCache() const {
@@ -4945,14 +4948,14 @@ class SILFunctionType final
49454948
ArrayRef<SILYieldInfo> getYields() const {
49464949
return const_cast<SILFunctionType *>(this)->getMutableYields();
49474950
}
4948-
unsigned getNumYields() const { return isCoroutine() ? NumAnyResults : 0; }
4951+
unsigned getNumYields() const { return NumAnyYieldResults; }
49494952

49504953
/// Return the array of all result information. This may contain inter-mingled
49514954
/// direct and indirect results.
49524955
ArrayRef<SILResultInfo> getResults() const {
49534956
return const_cast<SILFunctionType *>(this)->getMutableResults();
49544957
}
4955-
unsigned getNumResults() const { return isCoroutine() ? 0 : NumAnyResults; }
4958+
unsigned getNumResults() const { return NumAnyResults; }
49564959

49574960
ArrayRef<SILResultInfo> getResultsWithError() const {
49584961
return const_cast<SILFunctionType *>(this)->getMutableResultsWithError();
@@ -4989,17 +4992,17 @@ class SILFunctionType final
49894992
// indirect property, not the SIL indirect property, should be consulted to
49904993
// determine whether function reabstraction is necessary.
49914994
unsigned getNumIndirectFormalResults() const {
4992-
return isCoroutine() ? 0 : NumAnyIndirectFormalResults;
4995+
return NumAnyIndirectFormalResults;
49934996
}
49944997
/// Does this function have any formally indirect results?
49954998
bool hasIndirectFormalResults() const {
49964999
return getNumIndirectFormalResults() != 0;
49975000
}
49985001
unsigned getNumDirectFormalResults() const {
4999-
return isCoroutine() ? 0 : NumAnyResults - NumAnyIndirectFormalResults;
5002+
return NumAnyResults - NumAnyIndirectFormalResults;
50005003
}
50015004
unsigned getNumPackResults() const {
5002-
return isCoroutine() ? 0 : NumPackResults;
5005+
return NumPackResults;
50035006
}
50045007
bool hasIndirectErrorResult() const {
50055008
return hasErrorResult() && getErrorResult().isFormalIndirect();
@@ -5057,17 +5060,17 @@ class SILFunctionType final
50575060
TypeExpansionContext expansion);
50585061

50595062
unsigned getNumIndirectFormalYields() const {
5060-
return isCoroutine() ? NumAnyIndirectFormalResults : 0;
5063+
return NumAnyIndirectFormalYieldResults;
50615064
}
50625065
/// Does this function have any formally indirect yields?
50635066
bool hasIndirectFormalYields() const {
50645067
return getNumIndirectFormalYields() != 0;
50655068
}
50665069
unsigned getNumDirectFormalYields() const {
5067-
return isCoroutine() ? NumAnyResults - NumAnyIndirectFormalResults : 0;
5070+
return NumAnyYieldResults - NumAnyIndirectFormalYieldResults;
50685071
}
50695072
unsigned getNumPackYields() const {
5070-
return isCoroutine() ? NumPackResults : 0;
5073+
return NumPackYieldResults;
50715074
}
50725075

50735076
struct IndirectFormalYieldFilter {

include/swift/SIL/SILBuilder.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -589,11 +589,11 @@ class SILBuilder {
589589
beginApply));
590590
}
591591

592-
EndApplyInst *createEndApply(SILLocation loc, SILValue beginApply) {
592+
EndApplyInst *createEndApply(SILLocation loc, SILValue beginApply, SILType ResultType) {
593593
return insert(new (getModule()) EndApplyInst(getSILDebugLocation(loc),
594-
beginApply));
594+
beginApply, ResultType));
595595
}
596-
596+
597597
BuiltinInst *createBuiltin(SILLocation Loc, Identifier Name, SILType ResultTy,
598598
SubstitutionMap Subs,
599599
ArrayRef<SILValue> Args) {

include/swift/SIL/SILCloner.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,8 @@ SILCloner<ImplClass>::visitEndApplyInst(EndApplyInst *Inst) {
10771077
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
10781078
recordClonedInstruction(
10791079
Inst, getBuilder().createEndApply(getOpLocation(Inst->getLoc()),
1080-
getOpValue(Inst->getOperand())));
1080+
getOpValue(Inst->getOperand()),
1081+
getOpType(Inst->getType())));
10811082
}
10821083

10831084
template<typename ImplClass>

include/swift/SIL/SILInstruction.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3253,11 +3253,12 @@ class AbortApplyInst
32533253
/// normally.
32543254
class EndApplyInst
32553255
: public UnaryInstructionBase<SILInstructionKind::EndApplyInst,
3256-
NonValueInstruction> {
3256+
SingleValueInstruction> {
32573257
friend SILBuilder;
32583258

3259-
EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken)
3260-
: UnaryInstructionBase(debugLoc, beginApplyToken) {
3259+
EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken,
3260+
SILType Ty)
3261+
: UnaryInstructionBase(debugLoc, beginApplyToken, Ty) {
32613262
assert(isaResultOf<BeginApplyInst>(beginApplyToken) &&
32623263
isaResultOf<BeginApplyInst>(beginApplyToken)->isBeginApplyToken());
32633264
}

include/swift/SIL/SILNodes.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,8 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
568568
SingleValueInstruction, MayHaveSideEffects, MayRelease)
569569
SINGLE_VALUE_INST(PartialApplyInst, partial_apply,
570570
SingleValueInstruction, MayHaveSideEffects, DoesNotRelease)
571+
SINGLE_VALUE_INST(EndApplyInst, end_apply,
572+
SILInstruction, MayHaveSideEffects, MayRelease)
571573
SINGLE_VALUE_INST(FunctionExtractIsolationInst, function_extract_isolation,
572574
SingleValueInstruction, None, DoesNotRelease)
573575

@@ -873,8 +875,6 @@ NON_VALUE_INST(UncheckedRefCastAddrInst, unchecked_ref_cast_addr,
873875
SILInstruction, MayHaveSideEffects, DoesNotRelease)
874876
NON_VALUE_INST(AllocGlobalInst, alloc_global,
875877
SILInstruction, MayHaveSideEffects, DoesNotRelease)
876-
NON_VALUE_INST(EndApplyInst, end_apply,
877-
SILInstruction, MayHaveSideEffects, MayRelease)
878878
NON_VALUE_INST(AbortApplyInst, abort_apply,
879879
SILInstruction, MayHaveSideEffects, MayRelease)
880880
NON_VALUE_INST(PackElementSetInst, pack_element_set,

lib/AST/ASTContext.cpp

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4524,29 +4524,29 @@ SILFunctionType::SILFunctionType(
45244524
!ext.getLifetimeDependenceInfo().empty();
45254525
Bits.SILFunctionType.CoroutineKind = unsigned(coroutineKind);
45264526
NumParameters = params.size();
4527-
if (coroutineKind == SILCoroutineKind::None) {
4528-
assert(yields.empty());
4529-
NumAnyResults = normalResults.size();
4530-
NumAnyIndirectFormalResults = 0;
4531-
NumPackResults = 0;
4532-
for (auto &resultInfo : normalResults) {
4533-
if (resultInfo.isFormalIndirect())
4534-
NumAnyIndirectFormalResults++;
4535-
if (resultInfo.isPack())
4536-
NumPackResults++;
4537-
}
4538-
memcpy(getMutableResults().data(), normalResults.data(),
4539-
normalResults.size() * sizeof(SILResultInfo));
4540-
} else {
4541-
assert(normalResults.empty());
4542-
NumAnyResults = yields.size();
4543-
NumAnyIndirectFormalResults = 0;
4527+
assert((coroutineKind == SILCoroutineKind::None && yields.empty()) ||
4528+
coroutineKind != SILCoroutineKind::None);
4529+
4530+
NumAnyResults = normalResults.size();
4531+
NumAnyIndirectFormalResults = 0;
4532+
NumPackResults = 0;
4533+
for (auto &resultInfo : normalResults) {
4534+
if (resultInfo.isFormalIndirect())
4535+
NumAnyIndirectFormalResults++;
4536+
if (resultInfo.isPack())
4537+
NumPackResults++;
4538+
}
4539+
memcpy(getMutableResults().data(), normalResults.data(),
4540+
normalResults.size() * sizeof(SILResultInfo));
4541+
if (coroutineKind != SILCoroutineKind::None) {
4542+
NumAnyYieldResults = yields.size();
4543+
NumAnyIndirectFormalYieldResults = 0;
45444544
NumPackResults = 0;
45454545
for (auto &yieldInfo : yields) {
45464546
if (yieldInfo.isFormalIndirect())
4547-
NumAnyIndirectFormalResults++;
4547+
NumAnyIndirectFormalYieldResults++;
45484548
if (yieldInfo.isPack())
4549-
NumPackResults++;
4549+
NumPackYieldResults++;
45504550
}
45514551
memcpy(getMutableYields().data(), yields.data(),
45524552
yields.size() * sizeof(SILYieldInfo));
@@ -4718,7 +4718,6 @@ CanSILFunctionType SILFunctionType::get(
47184718
std::optional<SILResultInfo> errorResult, SubstitutionMap patternSubs,
47194719
SubstitutionMap invocationSubs, const ASTContext &ctx,
47204720
ProtocolConformanceRef witnessMethodConformance) {
4721-
assert(coroutineKind == SILCoroutineKind::None || normalResults.empty());
47224721
assert(coroutineKind != SILCoroutineKind::None || yields.empty());
47234722
assert(!ext.isPseudogeneric() || genericSig ||
47244723
coroutineKind != SILCoroutineKind::None);

lib/IRGen/GenCall.cpp

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -661,24 +661,34 @@ namespace {
661661
}
662662

663663
void SignatureExpansion::expandCoroutineResult(bool forContinuation) {
664-
assert(FnType->getNumResults() == 0 &&
665-
"having both normal and yield results is currently unsupported");
666-
667664
// The return type may be different for the ramp function vs. the
668665
// continuations.
669666
if (forContinuation) {
670667
switch (FnType->getCoroutineKind()) {
671668
case SILCoroutineKind::None:
672669
llvm_unreachable("should have been filtered out before here");
673670

674-
// Yield-once coroutines just return void from the continuation.
675-
case SILCoroutineKind::YieldOnce:
676-
ResultIRType = IGM.VoidTy;
671+
// Yield-once coroutines may optionaly return a value from the continuation.
672+
case SILCoroutineKind::YieldOnce: {
673+
auto fnConv = getSILFuncConventions();
674+
675+
assert(fnConv.getNumIndirectSILResults() == 0);
676+
// Ensure that no parameters were added before to correctly record their ABI
677+
// details.
678+
assert(ParamIRTypes.empty());
679+
680+
// Expand the direct result.
681+
const TypeInfo *directResultTypeInfo;
682+
std::tie(ResultIRType, directResultTypeInfo) = expandDirectResult();
683+
677684
return;
685+
}
678686

679687
// Yield-many coroutines yield the same types from the continuation
680688
// as they do from the ramp function.
681689
case SILCoroutineKind::YieldMany:
690+
assert(FnType->getNumResults() == 0 &&
691+
"having both normal and yield results is currently unsupported");
682692
break;
683693
}
684694
}
@@ -5703,6 +5713,53 @@ void irgen::emitAsyncReturn(IRGenFunction &IGF, AsyncContextLayout &asyncLayout,
57035713
emitAsyncReturn(IGF, asyncLayout, fnType, nativeResults);
57045714
}
57055715

5716+
void irgen::emitYieldOnceCoroutineResult(IRGenFunction &IGF, Explosion &result,
5717+
SILType funcResultType, SILType returnResultType) {
5718+
auto &Builder = IGF.Builder;
5719+
auto &IGM = IGF.IGM;
5720+
5721+
// Create coroutine exit block and branch to it.
5722+
auto coroEndBB = IGF.createBasicBlock("coro.end.normal");
5723+
IGF.setCoroutineExitBlock(coroEndBB);
5724+
Builder.CreateBr(coroEndBB);
5725+
5726+
// Emit the block.
5727+
Builder.emitBlock(coroEndBB);
5728+
auto handle = IGF.getCoroutineHandle();
5729+
5730+
llvm::Value *resultToken = nullptr;
5731+
if (result.empty()) {
5732+
assert(IGM.getTypeInfo(returnResultType)
5733+
.nativeReturnValueSchema(IGM)
5734+
.empty() &&
5735+
"Empty explosion must match the native calling convention");
5736+
// No results: just use none token
5737+
resultToken = llvm::ConstantTokenNone::get(Builder.getContext());
5738+
} else {
5739+
// Capture results via `coro_end_results` intrinsic
5740+
result = IGF.coerceValueTo(returnResultType, result, funcResultType);
5741+
auto &nativeSchema =
5742+
IGM.getTypeInfo(funcResultType).nativeReturnValueSchema(IGM);
5743+
assert(!nativeSchema.requiresIndirect());
5744+
5745+
Explosion native = nativeSchema.mapIntoNative(IGM, IGF, result,
5746+
funcResultType,
5747+
false /* isOutlined */);
5748+
SmallVector<llvm::Value *, 1> args;
5749+
for (unsigned i = 0, e = native.size(); i != e; ++i)
5750+
args.push_back(native.claimNext());
5751+
5752+
resultToken =
5753+
Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_end_results, args);
5754+
}
5755+
5756+
Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_end,
5757+
{handle,
5758+
/*is unwind*/ Builder.getFalse(),
5759+
resultToken});
5760+
Builder.CreateUnreachable();
5761+
}
5762+
57065763
FunctionPointer
57075764
IRGenFunction::getFunctionPointerForResumeIntrinsic(llvm::Value *resume) {
57085765
auto *fnTy = llvm::FunctionType::get(

lib/IRGen/GenCall.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ namespace irgen {
247247
SILType funcResultTypeInContext,
248248
CanSILFunctionType fnType, Explosion &result,
249249
Explosion &error);
250+
void emitYieldOnceCoroutineResult(IRGenFunction &IGF, Explosion &result,
251+
SILType funcResultType, SILType returnResultType);
250252

251253
Address emitAutoDiffCreateLinearMapContextWithType(
252254
IRGenFunction &IGF, llvm::Value *topLevelSubcontextMetatype);

lib/IRGen/IRGenFunction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ void IRGenFunction::emitAwaitAsyncContinuation(
709709
// because the continuation result is not available yet. When the
710710
// continuation is later resumed, the task will get scheduled
711711
// starting from the suspension point.
712-
emitCoroutineOrAsyncExit();
712+
emitCoroutineOrAsyncExit(false);
713713
}
714714

715715
Builder.emitBlock(contBB);

lib/IRGen/IRGenFunction.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,16 @@ class IRGenFunction {
155155
CoroutineHandle = handle;
156156
}
157157

158+
llvm::BasicBlock *getCoroutineExitBlock() const {
159+
return CoroutineExitBlock;
160+
}
161+
162+
void setCoroutineExitBlock(llvm::BasicBlock *block) {
163+
assert(CoroutineExitBlock == nullptr && "already set exit BB");
164+
assert(block != nullptr && "setting a null exit BB");
165+
CoroutineExitBlock = block;
166+
}
167+
158168
llvm::Value *getAsyncTask();
159169
llvm::Value *getAsyncContext();
160170
void storeCurrentAsyncContext(llvm::Value *context);
@@ -236,7 +246,7 @@ class IRGenFunction {
236246
bool callsAnyAlwaysInlineThunksWithForeignExceptionTraps = false;
237247

238248
public:
239-
void emitCoroutineOrAsyncExit();
249+
void emitCoroutineOrAsyncExit(bool isUnwind);
240250

241251
//--- Helper methods -----------------------------------------------------------
242252
public:

0 commit comments

Comments
 (0)