Skip to content

Commit 337f7dd

Browse files
committed
Allow normal function results of @yield_once coroutines
1 parent 2993e3b commit 337f7dd

File tree

70 files changed

+611
-288
lines changed

Some content is hidden

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

70 files changed

+611
-288
lines changed

docs/SIL.rst

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

6047+
A coroutine optionally may produce normal results. These do not have
6048+
``@yields`` annotation in the result type tuple.
6049+
::
6050+
(%float, %token) = begin_apply %0() : $@yield_once () -> (@yields Float, Int)
6051+
6052+
Normal results of a coroutine are produced by the corresponding ``end_apply``
6053+
instruction.
6054+
60476055
A ``begin_apply`` must be uniquely either ended or aborted before
60486056
exiting the function or looping to an earlier portion of the function.
60496057

@@ -6073,9 +6081,9 @@ end_apply
60736081
`````````
60746082
::
60756083

6076-
sil-instruction ::= 'end_apply' sil-value
6084+
sil-instruction ::= 'end_apply' sil-value 'as' sil-type
60776085

6078-
end_apply %token
6086+
end_apply %token as $()
60796087

60806088
Ends the given coroutine activation, which is currently suspended at
60816089
a ``yield`` instruction. Transfers control to the coroutine and takes
@@ -6085,8 +6093,8 @@ when the coroutine reaches a ``return`` instruction.
60856093
The operand must always be the token result of a ``begin_apply``
60866094
instruction, which is why it need not specify a type.
60876095

6088-
``end_apply`` currently has no instruction results. If coroutines were
6089-
allowed to have normal results, they would be producted by ``end_apply``.
6096+
If a coroutine produces normal results on ``resume`` path, they
6097+
will be produced by ``end_apply``.
60906098

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

include/swift/AST/Types.h

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

46724672
private:
4673-
unsigned NumParameters;
4673+
unsigned NumParameters = 0;
46744674

4675-
// These are *normal* results if this is not a coroutine and *yield* results
4676-
// otherwise.
4677-
unsigned NumAnyResults; // Not including the ErrorResult.
4678-
unsigned NumAnyIndirectFormalResults; // Subset of NumAnyResults.
4679-
unsigned NumPackResults; // Subset of NumAnyIndirectFormalResults.
4675+
// These are *normal* results
4676+
unsigned NumAnyResults = 0; // Not including the ErrorResult.
4677+
unsigned NumAnyIndirectFormalResults = 0; // Subset of NumAnyResults.
4678+
unsigned NumPackResults = 0; // Subset of NumAnyIndirectFormalResults.
4679+
// These are *yield* results
4680+
unsigned NumAnyYieldResults = 0; // Not including the ErrorResult.
4681+
unsigned NumAnyIndirectFormalYieldResults = 0; // Subset of NumAnyYieldResults.
4682+
unsigned NumPackYieldResults = 0; // Subset of NumAnyIndirectFormalYieldResults.
46804683

46814684
// [NOTE: SILFunctionType-layout]
46824685
// The layout of a SILFunctionType in memory is:
46834686
// SILFunctionType
46844687
// SILParameterInfo[NumParameters]
4685-
// SILResultInfo[isCoroutine() ? 0 : NumAnyResults]
4688+
// SILResultInfo[NumAnyResults]
46864689
// SILResultInfo? // if hasErrorResult()
4687-
// SILYieldInfo[isCoroutine() ? NumAnyResults : 0]
4690+
// SILYieldInfo[NumAnyYieldResults]
46884691
// SubstitutionMap[HasPatternSubs + HasInvocationSubs]
4689-
// CanType? // if !isCoro && NumAnyResults > 1, formal result cache
4690-
// CanType? // if !isCoro && NumAnyResults > 1, all result cache
4692+
// CanType? // if NumAnyResults > 1, formal result cache
4693+
// CanType? // if NumAnyResults > 1, all result cache
46914694

46924695
CanGenericSignature InvocationGenericSig;
46934696
ProtocolConformanceRef WitnessMethodConformance;
@@ -4726,7 +4729,7 @@ class SILFunctionType final
47264729

47274730
/// Do we have slots for caches of the normal-result tuple type?
47284731
bool hasResultCache() const {
4729-
return NumAnyResults > 1 && !isCoroutine();
4732+
return NumAnyResults > 1;
47304733
}
47314734

47324735
CanType &getMutableFormalResultsCache() const {
@@ -4814,14 +4817,14 @@ class SILFunctionType final
48144817
ArrayRef<SILYieldInfo> getYields() const {
48154818
return const_cast<SILFunctionType *>(this)->getMutableYields();
48164819
}
4817-
unsigned getNumYields() const { return isCoroutine() ? NumAnyResults : 0; }
4820+
unsigned getNumYields() const { return NumAnyYieldResults; }
48184821

48194822
/// Return the array of all result information. This may contain inter-mingled
48204823
/// direct and indirect results.
48214824
ArrayRef<SILResultInfo> getResults() const {
48224825
return const_cast<SILFunctionType *>(this)->getMutableResults();
48234826
}
4824-
unsigned getNumResults() const { return isCoroutine() ? 0 : NumAnyResults; }
4827+
unsigned getNumResults() const { return NumAnyResults; }
48254828

48264829
ArrayRef<SILResultInfo> getResultsWithError() const {
48274830
return const_cast<SILFunctionType *>(this)->getMutableResultsWithError();
@@ -4858,17 +4861,17 @@ class SILFunctionType final
48584861
// indirect property, not the SIL indirect property, should be consulted to
48594862
// determine whether function reabstraction is necessary.
48604863
unsigned getNumIndirectFormalResults() const {
4861-
return isCoroutine() ? 0 : NumAnyIndirectFormalResults;
4864+
return NumAnyIndirectFormalResults;
48624865
}
48634866
/// Does this function have any formally indirect results?
48644867
bool hasIndirectFormalResults() const {
48654868
return getNumIndirectFormalResults() != 0;
48664869
}
48674870
unsigned getNumDirectFormalResults() const {
4868-
return isCoroutine() ? 0 : NumAnyResults - NumAnyIndirectFormalResults;
4871+
return NumAnyResults - NumAnyIndirectFormalResults;
48694872
}
48704873
unsigned getNumPackResults() const {
4871-
return isCoroutine() ? 0 : NumPackResults;
4874+
return NumPackResults;
48724875
}
48734876
bool hasIndirectErrorResult() const {
48744877
return hasErrorResult() && getErrorResult().isFormalIndirect();
@@ -4926,17 +4929,17 @@ class SILFunctionType final
49264929
TypeExpansionContext expansion);
49274930

49284931
unsigned getNumIndirectFormalYields() const {
4929-
return isCoroutine() ? NumAnyIndirectFormalResults : 0;
4932+
return NumAnyIndirectFormalYieldResults;
49304933
}
49314934
/// Does this function have any formally indirect yields?
49324935
bool hasIndirectFormalYields() const {
49334936
return getNumIndirectFormalYields() != 0;
49344937
}
49354938
unsigned getNumDirectFormalYields() const {
4936-
return isCoroutine() ? NumAnyResults - NumAnyIndirectFormalResults : 0;
4939+
return NumAnyYieldResults - NumAnyIndirectFormalYieldResults;
49374940
}
49384941
unsigned getNumPackYields() const {
4939-
return isCoroutine() ? NumPackResults : 0;
4942+
return NumPackYieldResults;
49404943
}
49414944

49424945
struct IndirectFormalYieldFilter {

include/swift/SIL/SILBuilder.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -576,11 +576,11 @@ class SILBuilder {
576576
beginApply));
577577
}
578578

579-
EndApplyInst *createEndApply(SILLocation loc, SILValue beginApply) {
579+
EndApplyInst *createEndApply(SILLocation loc, SILValue beginApply, SILType ResultType) {
580580
return insert(new (getModule()) EndApplyInst(getSILDebugLocation(loc),
581-
beginApply));
581+
beginApply, ResultType));
582582
}
583-
583+
584584
BuiltinInst *createBuiltin(SILLocation Loc, Identifier Name, SILType ResultTy,
585585
SubstitutionMap Subs,
586586
ArrayRef<SILValue> Args) {

include/swift/SIL/SILCloner.h

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

10821083
template<typename ImplClass>

include/swift/SIL/SILInstruction.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3196,11 +3196,12 @@ class AbortApplyInst
31963196
/// normally.
31973197
class EndApplyInst
31983198
: public UnaryInstructionBase<SILInstructionKind::EndApplyInst,
3199-
NonValueInstruction> {
3199+
SingleValueInstruction> {
32003200
friend SILBuilder;
32013201

3202-
EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken)
3203-
: UnaryInstructionBase(debugLoc, beginApplyToken) {
3202+
EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken,
3203+
SILType Ty)
3204+
: UnaryInstructionBase(debugLoc, beginApplyToken, Ty) {
32043205
assert(isaResultOf<BeginApplyInst>(beginApplyToken) &&
32053206
isaResultOf<BeginApplyInst>(beginApplyToken)->isBeginApplyToken());
32063207
}

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

572574
// Metatypes
573575
SINGLE_VALUE_INST(MetatypeInst, metatype,
@@ -871,8 +873,6 @@ NON_VALUE_INST(UncheckedRefCastAddrInst, unchecked_ref_cast_addr,
871873
SILInstruction, MayHaveSideEffects, DoesNotRelease)
872874
NON_VALUE_INST(AllocGlobalInst, alloc_global,
873875
SILInstruction, MayHaveSideEffects, DoesNotRelease)
874-
NON_VALUE_INST(EndApplyInst, end_apply,
875-
SILInstruction, MayHaveSideEffects, MayRelease)
876876
NON_VALUE_INST(AbortApplyInst, abort_apply,
877877
SILInstruction, MayHaveSideEffects, MayRelease)
878878
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
@@ -4390,29 +4390,29 @@ SILFunctionType::SILFunctionType(
43904390
Bits.SILFunctionType.HasClangTypeInfo = !ext.getClangTypeInfo().empty();
43914391
Bits.SILFunctionType.CoroutineKind = unsigned(coroutineKind);
43924392
NumParameters = params.size();
4393-
if (coroutineKind == SILCoroutineKind::None) {
4394-
assert(yields.empty());
4395-
NumAnyResults = normalResults.size();
4396-
NumAnyIndirectFormalResults = 0;
4397-
NumPackResults = 0;
4398-
for (auto &resultInfo : normalResults) {
4399-
if (resultInfo.isFormalIndirect())
4400-
NumAnyIndirectFormalResults++;
4401-
if (resultInfo.isPack())
4402-
NumPackResults++;
4403-
}
4404-
memcpy(getMutableResults().data(), normalResults.data(),
4405-
normalResults.size() * sizeof(SILResultInfo));
4406-
} else {
4407-
assert(normalResults.empty());
4408-
NumAnyResults = yields.size();
4409-
NumAnyIndirectFormalResults = 0;
4393+
assert((coroutineKind == SILCoroutineKind::None && yields.empty()) ||
4394+
coroutineKind != SILCoroutineKind::None);
4395+
4396+
NumAnyResults = normalResults.size();
4397+
NumAnyIndirectFormalResults = 0;
4398+
NumPackResults = 0;
4399+
for (auto &resultInfo : normalResults) {
4400+
if (resultInfo.isFormalIndirect())
4401+
NumAnyIndirectFormalResults++;
4402+
if (resultInfo.isPack())
4403+
NumPackResults++;
4404+
}
4405+
memcpy(getMutableResults().data(), normalResults.data(),
4406+
normalResults.size() * sizeof(SILResultInfo));
4407+
if (coroutineKind != SILCoroutineKind::None) {
4408+
NumAnyYieldResults = yields.size();
4409+
NumAnyIndirectFormalYieldResults = 0;
44104410
NumPackResults = 0;
44114411
for (auto &yieldInfo : yields) {
44124412
if (yieldInfo.isFormalIndirect())
4413-
NumAnyIndirectFormalResults++;
4413+
NumAnyIndirectFormalYieldResults++;
44144414
if (yieldInfo.isPack())
4415-
NumPackResults++;
4415+
NumPackYieldResults++;
44164416
}
44174417
memcpy(getMutableYields().data(), yields.data(),
44184418
yields.size() * sizeof(SILYieldInfo));
@@ -4580,7 +4580,6 @@ CanSILFunctionType SILFunctionType::get(
45804580
llvm::Optional<SILResultInfo> errorResult, SubstitutionMap patternSubs,
45814581
SubstitutionMap invocationSubs, const ASTContext &ctx,
45824582
ProtocolConformanceRef witnessMethodConformance) {
4583-
assert(coroutineKind == SILCoroutineKind::None || normalResults.empty());
45844583
assert(coroutineKind != SILCoroutineKind::None || yields.empty());
45854584
assert(!ext.isPseudogeneric() || genericSig ||
45864585
coroutineKind != SILCoroutineKind::None);

lib/IRGen/GenCall.cpp

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -656,24 +656,34 @@ 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) {
665662
switch (FnType->getCoroutineKind()) {
666663
case SILCoroutineKind::None:
667664
llvm_unreachable("should have been filtered out before here");
668665

669-
// Yield-once coroutines just return void from the continuation.
670-
case SILCoroutineKind::YieldOnce:
671-
ResultIRType = IGM.VoidTy;
666+
// Yield-once coroutines may optionaly return a value from the continuation.
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
}
@@ -5831,6 +5841,53 @@ void irgen::emitAsyncReturn(IRGenFunction &IGF, AsyncContextLayout &asyncLayout,
58315841
emitAsyncReturn(IGF, asyncLayout, fnType, nativeResults);
58325842
}
58335843

5844+
void irgen::emitYieldOnceCoroutineResult(IRGenFunction &IGF, Explosion &result,
5845+
SILType funcResultType, SILType returnResultType) {
5846+
auto &Builder = IGF.Builder;
5847+
auto &IGM = IGF.IGM;
5848+
5849+
// Create coroutine exit block and branch to it.
5850+
auto coroEndBB = IGF.createBasicBlock("coro.end.normal");
5851+
IGF.setCoroutineExitBlock(coroEndBB);
5852+
Builder.CreateBr(coroEndBB);
5853+
5854+
// Emit the block.
5855+
Builder.emitBlock(coroEndBB);
5856+
auto handle = IGF.getCoroutineHandle();
5857+
5858+
llvm::Value *resultToken = nullptr;
5859+
if (result.empty()) {
5860+
assert(IGM.getTypeInfo(returnResultType)
5861+
.nativeReturnValueSchema(IGM)
5862+
.empty() &&
5863+
"Empty explosion must match the native calling convention");
5864+
// No results: just use none token
5865+
resultToken = llvm::ConstantTokenNone::get(Builder.getContext());
5866+
} else {
5867+
// Capture results via `coro_end_results` intrinsic
5868+
result = IGF.coerceValueTo(returnResultType, result, funcResultType);
5869+
auto &nativeSchema =
5870+
IGM.getTypeInfo(funcResultType).nativeReturnValueSchema(IGM);
5871+
assert(!nativeSchema.requiresIndirect());
5872+
5873+
Explosion native = nativeSchema.mapIntoNative(IGM, IGF, result,
5874+
funcResultType,
5875+
false /* isOutlined */);
5876+
SmallVector<llvm::Value *, 1> args;
5877+
for (unsigned i = 0, e = native.size(); i != e; ++i)
5878+
args.push_back(native.claimNext());
5879+
5880+
resultToken =
5881+
Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_end_results, args);
5882+
}
5883+
5884+
Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_end,
5885+
{handle,
5886+
/*is unwind*/ Builder.getFalse(),
5887+
resultToken});
5888+
Builder.CreateUnreachable();
5889+
}
5890+
58345891
FunctionPointer
58355892
IRGenFunction::getFunctionPointerForResumeIntrinsic(llvm::Value *resume) {
58365893
auto *fnTy = llvm::FunctionType::get(

lib/IRGen/GenCall.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ namespace irgen {
266266
SILType funcResultTypeInContext,
267267
CanSILFunctionType fnType, Explosion &result,
268268
Explosion &error);
269+
void emitYieldOnceCoroutineResult(IRGenFunction &IGF, Explosion &result,
270+
SILType funcResultType, SILType returnResultType);
269271

270272
Address emitAutoDiffCreateLinearMapContextWithType(
271273
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
@@ -153,6 +153,16 @@ class IRGenFunction {
153153
CoroutineHandle = handle;
154154
}
155155

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

236246
public:
237-
void emitCoroutineOrAsyncExit();
247+
void emitCoroutineOrAsyncExit(bool isUnwind);
238248

239249
//--- Helper methods -----------------------------------------------------------
240250
public:

0 commit comments

Comments
 (0)