Skip to content

Commit 06ba7b3

Browse files
committed
Allow normal function results of @yield_once coroutines
1 parent db7a400 commit 06ba7b3

File tree

68 files changed

+523
-287
lines changed

Some content is hidden

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

68 files changed

+523
-287
lines changed

docs/SIL.rst

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

5977+
A coroutine optionally may produce normal results. These do not have
5978+
``@yields`` annotation in the result type tuple.
5979+
::
5980+
(%float, %token) = begin_apply %0() : $@yield_once () -> (@yields Float, Int)
5981+
5982+
Normal results of a coroutine are produced by the corresponding ``end_apply``
5983+
instruction.
5984+
59775985
A ``begin_apply`` must be uniquely either ended or aborted before
59785986
exiting the function or looping to an earlier portion of the function.
59795987

@@ -6003,9 +6011,9 @@ end_apply
60036011
`````````
60046012
::
60056013

6006-
sil-instruction ::= 'end_apply' sil-value
6014+
sil-instruction ::= 'end_apply' sil-value ':' sil-type
60076015

6008-
end_apply %token
6016+
end_apply %token : $()
60096017

60106018
Ends the given coroutine activation, which is currently suspended at
60116019
a ``yield`` instruction. Transfers control to the coroutine and takes
@@ -6015,8 +6023,8 @@ when the coroutine reaches a ``return`` instruction.
60156023
The operand must always be the token result of a ``begin_apply``
60166024
instruction, which is why it need not specify a type.
60176025

6018-
``end_apply`` currently has no instruction results. If coroutines were
6019-
allowed to have normal results, they would be producted by ``end_apply``.
6026+
If a coroutine produces normal results on ``resume`` path, they
6027+
will be produced by ``end_apply``.
60206028

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

include/swift/AST/Types.h

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

45984598
private:
4599-
unsigned NumParameters;
4599+
unsigned NumParameters = 0;
46004600

4601-
// These are *normal* results if this is not a coroutine and *yield* results
4602-
// otherwise.
4603-
unsigned NumAnyResults; // Not including the ErrorResult.
4604-
unsigned NumAnyIndirectFormalResults; // Subset of NumAnyResults.
4605-
unsigned NumPackResults; // Subset of NumAnyIndirectFormalResults.
4601+
// These are *normal* results
4602+
unsigned NumAnyResults = 0; // Not including the ErrorResult.
4603+
unsigned NumAnyIndirectFormalResults = 0; // Subset of NumAnyResults.
4604+
unsigned NumPackResults = 0; // Subset of NumAnyIndirectFormalResults.
4605+
// These are *yield* results
4606+
unsigned NumAnyYieldResults = 0; // Not including the ErrorResult.
4607+
unsigned NumAnyIndirectFormalYieldResults = 0; // Subset of NumAnyYieldResults.
4608+
unsigned NumPackYieldResults = 0; // Subset of NumAnyIndirectFormalYieldResults.
46064609

46074610
// [NOTE: SILFunctionType-layout]
46084611
// The layout of a SILFunctionType in memory is:
46094612
// SILFunctionType
46104613
// SILParameterInfo[NumParameters]
4611-
// SILResultInfo[isCoroutine() ? 0 : NumAnyResults]
4614+
// SILResultInfo[NumAnyResults]
46124615
// SILResultInfo? // if hasErrorResult()
4613-
// SILYieldInfo[isCoroutine() ? NumAnyResults : 0]
4616+
// SILYieldInfo[NumAnyYieldResults]
46144617
// SubstitutionMap[HasPatternSubs + HasInvocationSubs]
4615-
// CanType? // if !isCoro && NumAnyResults > 1, formal result cache
4616-
// CanType? // if !isCoro && NumAnyResults > 1, all result cache
4618+
// CanType? // if NumAnyResults > 1, formal result cache
4619+
// CanType? // if NumAnyResults > 1, all result cache
46174620

46184621
CanGenericSignature InvocationGenericSig;
46194622
ProtocolConformanceRef WitnessMethodConformance;
@@ -4648,7 +4651,7 @@ class SILFunctionType final
46484651

46494652
/// Do we have slots for caches of the normal-result tuple type?
46504653
bool hasResultCache() const {
4651-
return NumAnyResults > 1 && !isCoroutine();
4654+
return NumAnyResults > 1;
46524655
}
46534656

46544657
CanType &getMutableFormalResultsCache() const {
@@ -4736,14 +4739,14 @@ class SILFunctionType final
47364739
ArrayRef<SILYieldInfo> getYields() const {
47374740
return const_cast<SILFunctionType *>(this)->getMutableYields();
47384741
}
4739-
unsigned getNumYields() const { return isCoroutine() ? NumAnyResults : 0; }
4742+
unsigned getNumYields() const { return NumAnyYieldResults; }
47404743

47414744
/// Return the array of all result information. This may contain inter-mingled
47424745
/// direct and indirect results.
47434746
ArrayRef<SILResultInfo> getResults() const {
47444747
return const_cast<SILFunctionType *>(this)->getMutableResults();
47454748
}
4746-
unsigned getNumResults() const { return isCoroutine() ? 0 : NumAnyResults; }
4749+
unsigned getNumResults() const { return NumAnyResults; }
47474750

47484751
/// Given that this function type has exactly one result, return it.
47494752
/// This is a common situation when working with a function with a known
@@ -4773,17 +4776,17 @@ class SILFunctionType final
47734776
// indirect property, not the SIL indirect property, should be consulted to
47744777
// determine whether function reabstraction is necessary.
47754778
unsigned getNumIndirectFormalResults() const {
4776-
return isCoroutine() ? 0 : NumAnyIndirectFormalResults;
4779+
return NumAnyIndirectFormalResults;
47774780
}
47784781
/// Does this function have any formally indirect results?
47794782
bool hasIndirectFormalResults() const {
47804783
return getNumIndirectFormalResults() != 0;
47814784
}
47824785
unsigned getNumDirectFormalResults() const {
4783-
return isCoroutine() ? 0 : NumAnyResults - NumAnyIndirectFormalResults;
4786+
return NumAnyResults - NumAnyIndirectFormalResults;
47844787
}
47854788
unsigned getNumPackResults() const {
4786-
return isCoroutine() ? 0 : NumPackResults;
4789+
return NumPackResults;
47874790
}
47884791

47894792
struct IndirectFormalResultFilter {
@@ -4838,17 +4841,17 @@ class SILFunctionType final
48384841
TypeExpansionContext expansion);
48394842

48404843
unsigned getNumIndirectFormalYields() const {
4841-
return isCoroutine() ? NumAnyIndirectFormalResults : 0;
4844+
return NumAnyIndirectFormalYieldResults;
48424845
}
48434846
/// Does this function have any formally indirect yields?
48444847
bool hasIndirectFormalYields() const {
48454848
return getNumIndirectFormalYields() != 0;
48464849
}
48474850
unsigned getNumDirectFormalYields() const {
4848-
return isCoroutine() ? NumAnyResults - NumAnyIndirectFormalResults : 0;
4851+
return NumAnyYieldResults - NumAnyIndirectFormalYieldResults;
48494852
}
48504853
unsigned getNumPackYields() const {
4851-
return isCoroutine() ? NumPackResults : 0;
4854+
return NumPackYieldResults;
48524855
}
48534856

48544857
struct IndirectFormalYieldFilter {

include/swift/SIL/SILBuilder.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,11 +564,11 @@ class SILBuilder {
564564
beginApply));
565565
}
566566

567-
EndApplyInst *createEndApply(SILLocation loc, SILValue beginApply) {
567+
EndApplyInst *createEndApply(SILLocation loc, SILValue beginApply, SILType ResultType) {
568568
return insert(new (getModule()) EndApplyInst(getSILDebugLocation(loc),
569-
beginApply));
569+
beginApply, ResultType));
570570
}
571-
571+
572572
BuiltinInst *createBuiltin(SILLocation Loc, Identifier Name, SILType ResultTy,
573573
SubstitutionMap Subs,
574574
ArrayRef<SILValue> Args) {

include/swift/SIL/SILCloner.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1065,7 +1065,8 @@ SILCloner<ImplClass>::visitEndApplyInst(EndApplyInst *Inst) {
10651065
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
10661066
recordClonedInstruction(
10671067
Inst, getBuilder().createEndApply(getOpLocation(Inst->getLoc()),
1068-
getOpValue(Inst->getOperand())));
1068+
getOpValue(Inst->getOperand()),
1069+
getOpType(Inst->getType())));
10691070
}
10701071

10711072
template<typename ImplClass>

include/swift/SIL/SILInstruction.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3116,11 +3116,12 @@ class AbortApplyInst
31163116
/// normally.
31173117
class EndApplyInst
31183118
: public UnaryInstructionBase<SILInstructionKind::EndApplyInst,
3119-
NonValueInstruction> {
3119+
SingleValueInstruction> {
31203120
friend SILBuilder;
31213121

3122-
EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken)
3123-
: UnaryInstructionBase(debugLoc, beginApplyToken) {
3122+
EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken,
3123+
SILType Ty)
3124+
: UnaryInstructionBase(debugLoc, beginApplyToken, Ty) {
31243125
assert(isaResultOf<BeginApplyInst>(beginApplyToken) &&
31253126
isaResultOf<BeginApplyInst>(beginApplyToken)->isBeginApplyToken());
31263127
}

include/swift/SIL/SILNodes.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,8 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
566566
SingleValueInstruction, MayHaveSideEffects, MayRelease)
567567
SINGLE_VALUE_INST(PartialApplyInst, partial_apply,
568568
SingleValueInstruction, MayHaveSideEffects, DoesNotRelease)
569+
SINGLE_VALUE_INST(EndApplyInst, end_apply,
570+
SILInstruction, MayHaveSideEffects, MayRelease)
569571

570572
// Metatypes
571573
SINGLE_VALUE_INST(MetatypeInst, metatype,
@@ -867,8 +869,6 @@ NON_VALUE_INST(UncheckedRefCastAddrInst, unchecked_ref_cast_addr,
867869
SILInstruction, MayHaveSideEffects, DoesNotRelease)
868870
NON_VALUE_INST(AllocGlobalInst, alloc_global,
869871
SILInstruction, MayHaveSideEffects, DoesNotRelease)
870-
NON_VALUE_INST(EndApplyInst, end_apply,
871-
SILInstruction, MayHaveSideEffects, MayRelease)
872872
NON_VALUE_INST(AbortApplyInst, abort_apply,
873873
SILInstruction, MayHaveSideEffects, MayRelease)
874874
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
@@ -4362,29 +4362,29 @@ SILFunctionType::SILFunctionType(
43624362
Bits.SILFunctionType.HasClangTypeInfo = !ext.getClangTypeInfo().empty();
43634363
Bits.SILFunctionType.CoroutineKind = unsigned(coroutineKind);
43644364
NumParameters = params.size();
4365-
if (coroutineKind == SILCoroutineKind::None) {
4366-
assert(yields.empty());
4367-
NumAnyResults = normalResults.size();
4368-
NumAnyIndirectFormalResults = 0;
4369-
NumPackResults = 0;
4370-
for (auto &resultInfo : normalResults) {
4371-
if (resultInfo.isFormalIndirect())
4372-
NumAnyIndirectFormalResults++;
4373-
if (resultInfo.isPack())
4374-
NumPackResults++;
4375-
}
4376-
memcpy(getMutableResults().data(), normalResults.data(),
4377-
normalResults.size() * sizeof(SILResultInfo));
4378-
} else {
4379-
assert(normalResults.empty());
4380-
NumAnyResults = yields.size();
4381-
NumAnyIndirectFormalResults = 0;
4365+
assert((coroutineKind == SILCoroutineKind::None && yields.empty()) ||
4366+
coroutineKind != SILCoroutineKind::None);
4367+
4368+
NumAnyResults = normalResults.size();
4369+
NumAnyIndirectFormalResults = 0;
4370+
NumPackResults = 0;
4371+
for (auto &resultInfo : normalResults) {
4372+
if (resultInfo.isFormalIndirect())
4373+
NumAnyIndirectFormalResults++;
4374+
if (resultInfo.isPack())
4375+
NumPackResults++;
4376+
}
4377+
memcpy(getMutableResults().data(), normalResults.data(),
4378+
normalResults.size() * sizeof(SILResultInfo));
4379+
if (coroutineKind != SILCoroutineKind::None) {
4380+
NumAnyYieldResults = yields.size();
4381+
NumAnyIndirectFormalYieldResults = 0;
43824382
NumPackResults = 0;
43834383
for (auto &yieldInfo : yields) {
43844384
if (yieldInfo.isFormalIndirect())
4385-
NumAnyIndirectFormalResults++;
4385+
NumAnyIndirectFormalYieldResults++;
43864386
if (yieldInfo.isPack())
4387-
NumPackResults++;
4387+
NumPackYieldResults++;
43884388
}
43894389
memcpy(getMutableYields().data(), yields.data(),
43904390
yields.size() * sizeof(SILYieldInfo));
@@ -4554,7 +4554,6 @@ CanSILFunctionType SILFunctionType::get(
45544554
llvm::Optional<SILResultInfo> errorResult, SubstitutionMap patternSubs,
45554555
SubstitutionMap invocationSubs, const ASTContext &ctx,
45564556
ProtocolConformanceRef witnessMethodConformance) {
4557-
assert(coroutineKind == SILCoroutineKind::None || normalResults.empty());
45584557
assert(coroutineKind != SILCoroutineKind::None || yields.empty());
45594558
assert(!ext.isPseudogeneric() || genericSig ||
45604559
coroutineKind != SILCoroutineKind::None);

lib/IRGen/GenCall.cpp

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -656,9 +656,6 @@ namespace {
656656
}
657657

658658
void SignatureExpansion::expandCoroutineResult(bool forContinuation) {
659-
assert(FnType->getNumResults() == 0 &&
660-
"having both normal and yield results is currently unsupported");
661-
662659
// The return type may be different for the ramp function vs. the
663660
// continuations.
664661
if (forContinuation) {
@@ -667,13 +664,26 @@ void SignatureExpansion::expandCoroutineResult(bool forContinuation) {
667664
llvm_unreachable("should have been filtered out before here");
668665

669666
// Yield-once coroutines just return void from the continuation.
670-
case SILCoroutineKind::YieldOnce:
671-
ResultIRType = IGM.VoidTy;
667+
case SILCoroutineKind::YieldOnce: {
668+
auto fnConv = getSILFuncConventions();
669+
670+
assert(fnConv.getNumIndirectSILResults() == 0);
671+
// Ensure that no parameters were added before to correctly record their ABI
672+
// details.
673+
assert(ParamIRTypes.empty());
674+
675+
// Expand the direct result.
676+
const TypeInfo *directResultTypeInfo;
677+
std::tie(ResultIRType, directResultTypeInfo) = expandDirectResult();
678+
672679
return;
680+
}
673681

674682
// Yield-many coroutines yield the same types from the continuation
675683
// as they do from the ramp function.
676684
case SILCoroutineKind::YieldMany:
685+
assert(FnType->getNumResults() == 0 &&
686+
"having both normal and yield results is currently unsupported");
677687
break;
678688
}
679689
}
@@ -5790,6 +5800,53 @@ void irgen::emitAsyncReturn(IRGenFunction &IGF, AsyncContextLayout &asyncLayout,
57905800
emitAsyncReturn(IGF, asyncLayout, fnType, nativeResults);
57915801
}
57925802

5803+
void irgen::emitYieldOnceCoroutineResult(IRGenFunction &IGF, Explosion &result,
5804+
SILType funcResultType, SILType returnResultType) {
5805+
auto &Builder = IGF.Builder;
5806+
auto &IGM = IGF.IGM;
5807+
5808+
// Create coroutine exit block and branch to it.
5809+
auto coroEndBB = IGF.createBasicBlock("coro.end.normal");
5810+
IGF.setCoroutineExitBlock(coroEndBB);
5811+
Builder.CreateBr(coroEndBB);
5812+
5813+
// Emit the block.
5814+
Builder.emitBlock(coroEndBB);
5815+
auto handle = IGF.getCoroutineHandle();
5816+
5817+
llvm::Value *resultToken = nullptr;
5818+
if (result.empty()) {
5819+
assert(IGM.getTypeInfo(returnResultType)
5820+
.nativeReturnValueSchema(IGM)
5821+
.empty() &&
5822+
"Empty explosion must match the native calling convention");
5823+
// No results: just use none token
5824+
resultToken = llvm::ConstantTokenNone::get(Builder.getContext());
5825+
} else {
5826+
// Capture results via `coro_end_results` intrinsic
5827+
result = IGF.coerceValueTo(returnResultType, result, funcResultType);
5828+
auto &nativeSchema =
5829+
IGM.getTypeInfo(funcResultType).nativeReturnValueSchema(IGM);
5830+
assert(!nativeSchema.requiresIndirect());
5831+
5832+
Explosion native = nativeSchema.mapIntoNative(IGM, IGF, result,
5833+
funcResultType,
5834+
false /* isOutlined */);
5835+
SmallVector<llvm::Value *, 1> args;
5836+
for (unsigned i = 0, e = native.size(); i != e; ++i)
5837+
args.push_back(native.claimNext());
5838+
5839+
resultToken =
5840+
Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_end_results, args);
5841+
}
5842+
5843+
Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_end,
5844+
{handle,
5845+
/*is unwind*/ Builder.getFalse(),
5846+
resultToken});
5847+
Builder.CreateUnreachable();
5848+
}
5849+
57935850
FunctionPointer
57945851
IRGenFunction::getFunctionPointerForResumeIntrinsic(llvm::Value *resume) {
57955852
auto *fnTy = llvm::FunctionType::get(

lib/IRGen/GenCall.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ namespace irgen {
264264
SILType funcResultTypeInContext,
265265
CanSILFunctionType fnType, Explosion &result,
266266
Explosion &error);
267+
void emitYieldOnceCoroutineResult(IRGenFunction &IGF, Explosion &result,
268+
SILType funcResultType, SILType returnResultType);
267269

268270
Address emitAutoDiffCreateLinearMapContextWithType(
269271
IRGenFunction &IGF, llvm::Value *topLevelSubcontextMetatype);

lib/IRGen/IRGenFunction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ void IRGenFunction::emitAwaitAsyncContinuation(
701701
// because the continuation result is not available yet. When the
702702
// continuation is later resumed, the task will get scheduled
703703
// starting from the suspension point.
704-
emitCoroutineOrAsyncExit();
704+
emitCoroutineOrAsyncExit(false);
705705
}
706706

707707
Builder.emitBlock(contBB);

lib/IRGen/IRGenFunction.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,16 @@ class IRGenFunction {
152152
CoroutineHandle = handle;
153153
}
154154

155+
llvm::BasicBlock *getCoroutineExitBlock() const {
156+
return CoroutineExitBlock;
157+
}
158+
159+
void setCoroutineExitBlock(llvm::BasicBlock *block) {
160+
assert(CoroutineExitBlock == nullptr && "already set exit BB");
161+
assert(block != nullptr && "setting a null exit BB");
162+
CoroutineExitBlock = block;
163+
}
164+
155165
llvm::Value *getAsyncTask();
156166
llvm::Value *getAsyncContext();
157167
void storeCurrentAsyncContext(llvm::Value *context);
@@ -233,7 +243,7 @@ class IRGenFunction {
233243
bool callsAnyAlwaysInlineThunksWithForeignExceptionTraps = false;
234244

235245
public:
236-
void emitCoroutineOrAsyncExit();
246+
void emitCoroutineOrAsyncExit(bool isUnwind);
237247

238248
//--- Helper methods -----------------------------------------------------------
239249
public:

0 commit comments

Comments
 (0)