Skip to content

Commit 34fc75f

Browse files
committed
Allow normal function results of @yield_once coroutines
1 parent 8c0b056 commit 34fc75f

File tree

71 files changed

+612
-289
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

+612
-289
lines changed

docs/SIL.rst

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

6092+
A coroutine optionally may produce normal results. These do not have
6093+
``@yields`` annotation in the result type tuple.
6094+
::
6095+
(%float, %token) = begin_apply %0() : $@yield_once () -> (@yields Float, Int)
6096+
6097+
Normal results of a coroutine are produced by the corresponding ``end_apply``
6098+
instruction.
6099+
60926100
A ``begin_apply`` must be uniquely either ended or aborted before
60936101
exiting the function or looping to an earlier portion of the function.
60946102

@@ -6118,9 +6126,9 @@ end_apply
61186126
`````````
61196127
::
61206128

6121-
sil-instruction ::= 'end_apply' sil-value
6129+
sil-instruction ::= 'end_apply' sil-value 'as' sil-type
61226130

6123-
end_apply %token
6131+
end_apply %token as $()
61246132

61256133
Ends the given coroutine activation, which is currently suspended at
61266134
a ``yield`` instruction. Transfers control to the coroutine and takes
@@ -6130,8 +6138,8 @@ when the coroutine reaches a ``return`` instruction.
61306138
The operand must always be the token result of a ``begin_apply``
61316139
instruction, which is why it need not specify a type.
61326140

6133-
``end_apply`` currently has no instruction results. If coroutines were
6134-
allowed to have normal results, they would be producted by ``end_apply``.
6141+
If a coroutine produces normal results on ``resume`` path, they
6142+
will be produced by ``end_apply``.
61356143

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

include/swift/AST/Types.h

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

47524752
private:
4753-
unsigned NumParameters;
4753+
unsigned NumParameters = 0;
47544754

4755-
// These are *normal* results if this is not a coroutine and *yield* results
4756-
// otherwise.
4757-
unsigned NumAnyResults; // Not including the ErrorResult.
4758-
unsigned NumAnyIndirectFormalResults; // Subset of NumAnyResults.
4759-
unsigned NumPackResults; // Subset of NumAnyIndirectFormalResults.
4755+
// These are *normal* results
4756+
unsigned NumAnyResults = 0; // Not including the ErrorResult.
4757+
unsigned NumAnyIndirectFormalResults = 0; // Subset of NumAnyResults.
4758+
unsigned NumPackResults = 0; // Subset of NumAnyIndirectFormalResults.
4759+
// These are *yield* results
4760+
unsigned NumAnyYieldResults = 0; // Not including the ErrorResult.
4761+
unsigned NumAnyIndirectFormalYieldResults = 0; // Subset of NumAnyYieldResults.
4762+
unsigned NumPackYieldResults = 0; // Subset of NumAnyIndirectFormalYieldResults.
47604763

47614764
// [NOTE: SILFunctionType-layout]
47624765
// The layout of a SILFunctionType in memory is:
47634766
// SILFunctionType
47644767
// SILParameterInfo[NumParameters]
4765-
// SILResultInfo[isCoroutine() ? 0 : NumAnyResults]
4768+
// SILResultInfo[NumAnyResults]
47664769
// SILResultInfo? // if hasErrorResult()
4767-
// SILYieldInfo[isCoroutine() ? NumAnyResults : 0]
4770+
// SILYieldInfo[NumAnyYieldResults]
47684771
// SubstitutionMap[HasPatternSubs + HasInvocationSubs]
4769-
// CanType? // if !isCoro && NumAnyResults > 1, formal result cache
4770-
// CanType? // if !isCoro && NumAnyResults > 1, all result cache
4772+
// CanType? // if NumAnyResults > 1, formal result cache
4773+
// CanType? // if NumAnyResults > 1, all result cache
47714774

47724775
CanGenericSignature InvocationGenericSig;
47734776
ProtocolConformanceRef WitnessMethodConformance;
@@ -4806,7 +4809,7 @@ class SILFunctionType final
48064809

48074810
/// Do we have slots for caches of the normal-result tuple type?
48084811
bool hasResultCache() const {
4809-
return NumAnyResults > 1 && !isCoroutine();
4812+
return NumAnyResults > 1;
48104813
}
48114814

48124815
CanType &getMutableFormalResultsCache() const {
@@ -4901,14 +4904,14 @@ class SILFunctionType final
49014904
ArrayRef<SILYieldInfo> getYields() const {
49024905
return const_cast<SILFunctionType *>(this)->getMutableYields();
49034906
}
4904-
unsigned getNumYields() const { return isCoroutine() ? NumAnyResults : 0; }
4907+
unsigned getNumYields() const { return NumAnyYieldResults; }
49054908

49064909
/// Return the array of all result information. This may contain inter-mingled
49074910
/// direct and indirect results.
49084911
ArrayRef<SILResultInfo> getResults() const {
49094912
return const_cast<SILFunctionType *>(this)->getMutableResults();
49104913
}
4911-
unsigned getNumResults() const { return isCoroutine() ? 0 : NumAnyResults; }
4914+
unsigned getNumResults() const { return NumAnyResults; }
49124915

49134916
ArrayRef<SILResultInfo> getResultsWithError() const {
49144917
return const_cast<SILFunctionType *>(this)->getMutableResultsWithError();
@@ -4945,17 +4948,17 @@ class SILFunctionType final
49454948
// indirect property, not the SIL indirect property, should be consulted to
49464949
// determine whether function reabstraction is necessary.
49474950
unsigned getNumIndirectFormalResults() const {
4948-
return isCoroutine() ? 0 : NumAnyIndirectFormalResults;
4951+
return NumAnyIndirectFormalResults;
49494952
}
49504953
/// Does this function have any formally indirect results?
49514954
bool hasIndirectFormalResults() const {
49524955
return getNumIndirectFormalResults() != 0;
49534956
}
49544957
unsigned getNumDirectFormalResults() const {
4955-
return isCoroutine() ? 0 : NumAnyResults - NumAnyIndirectFormalResults;
4958+
return NumAnyResults - NumAnyIndirectFormalResults;
49564959
}
49574960
unsigned getNumPackResults() const {
4958-
return isCoroutine() ? 0 : NumPackResults;
4961+
return NumPackResults;
49594962
}
49604963
bool hasIndirectErrorResult() const {
49614964
return hasErrorResult() && getErrorResult().isFormalIndirect();
@@ -5013,17 +5016,17 @@ class SILFunctionType final
50135016
TypeExpansionContext expansion);
50145017

50155018
unsigned getNumIndirectFormalYields() const {
5016-
return isCoroutine() ? NumAnyIndirectFormalResults : 0;
5019+
return NumAnyIndirectFormalYieldResults;
50175020
}
50185021
/// Does this function have any formally indirect yields?
50195022
bool hasIndirectFormalYields() const {
50205023
return getNumIndirectFormalYields() != 0;
50215024
}
50225025
unsigned getNumDirectFormalYields() const {
5023-
return isCoroutine() ? NumAnyResults - NumAnyIndirectFormalResults : 0;
5026+
return NumAnyYieldResults - NumAnyIndirectFormalYieldResults;
50245027
}
50255028
unsigned getNumPackYields() const {
5026-
return isCoroutine() ? NumPackResults : 0;
5029+
return NumPackYieldResults;
50275030
}
50285031

50295032
struct IndirectFormalYieldFilter {

include/swift/SIL/SILBuilder.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -579,11 +579,11 @@ class SILBuilder {
579579
beginApply));
580580
}
581581

582-
EndApplyInst *createEndApply(SILLocation loc, SILValue beginApply) {
582+
EndApplyInst *createEndApply(SILLocation loc, SILValue beginApply, SILType ResultType) {
583583
return insert(new (getModule()) EndApplyInst(getSILDebugLocation(loc),
584-
beginApply));
584+
beginApply, ResultType));
585585
}
586-
586+
587587
BuiltinInst *createBuiltin(SILLocation Loc, Identifier Name, SILType ResultTy,
588588
SubstitutionMap Subs,
589589
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
@@ -3206,11 +3206,12 @@ class AbortApplyInst
32063206
/// normally.
32073207
class EndApplyInst
32083208
: public UnaryInstructionBase<SILInstructionKind::EndApplyInst,
3209-
NonValueInstruction> {
3209+
SingleValueInstruction> {
32103210
friend SILBuilder;
32113211

3212-
EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken)
3213-
: UnaryInstructionBase(debugLoc, beginApplyToken) {
3212+
EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken,
3213+
SILType Ty)
3214+
: UnaryInstructionBase(debugLoc, beginApplyToken, Ty) {
32143215
assert(isaResultOf<BeginApplyInst>(beginApplyToken) &&
32153216
isaResultOf<BeginApplyInst>(beginApplyToken)->isBeginApplyToken());
32163217
}

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
@@ -4624,29 +4624,29 @@ SILFunctionType::SILFunctionType(
46244624
!ext.getLifetimeDependenceInfo().empty();
46254625
Bits.SILFunctionType.CoroutineKind = unsigned(coroutineKind);
46264626
NumParameters = params.size();
4627-
if (coroutineKind == SILCoroutineKind::None) {
4628-
assert(yields.empty());
4629-
NumAnyResults = normalResults.size();
4630-
NumAnyIndirectFormalResults = 0;
4631-
NumPackResults = 0;
4632-
for (auto &resultInfo : normalResults) {
4633-
if (resultInfo.isFormalIndirect())
4634-
NumAnyIndirectFormalResults++;
4635-
if (resultInfo.isPack())
4636-
NumPackResults++;
4637-
}
4638-
memcpy(getMutableResults().data(), normalResults.data(),
4639-
normalResults.size() * sizeof(SILResultInfo));
4640-
} else {
4641-
assert(normalResults.empty());
4642-
NumAnyResults = yields.size();
4643-
NumAnyIndirectFormalResults = 0;
4627+
assert((coroutineKind == SILCoroutineKind::None && yields.empty()) ||
4628+
coroutineKind != SILCoroutineKind::None);
4629+
4630+
NumAnyResults = normalResults.size();
4631+
NumAnyIndirectFormalResults = 0;
4632+
NumPackResults = 0;
4633+
for (auto &resultInfo : normalResults) {
4634+
if (resultInfo.isFormalIndirect())
4635+
NumAnyIndirectFormalResults++;
4636+
if (resultInfo.isPack())
4637+
NumPackResults++;
4638+
}
4639+
memcpy(getMutableResults().data(), normalResults.data(),
4640+
normalResults.size() * sizeof(SILResultInfo));
4641+
if (coroutineKind != SILCoroutineKind::None) {
4642+
NumAnyYieldResults = yields.size();
4643+
NumAnyIndirectFormalYieldResults = 0;
46444644
NumPackResults = 0;
46454645
for (auto &yieldInfo : yields) {
46464646
if (yieldInfo.isFormalIndirect())
4647-
NumAnyIndirectFormalResults++;
4647+
NumAnyIndirectFormalYieldResults++;
46484648
if (yieldInfo.isPack())
4649-
NumPackResults++;
4649+
NumPackYieldResults++;
46504650
}
46514651
memcpy(getMutableYields().data(), yields.data(),
46524652
yields.size() * sizeof(SILYieldInfo));
@@ -4818,7 +4818,6 @@ CanSILFunctionType SILFunctionType::get(
48184818
llvm::Optional<SILResultInfo> errorResult, SubstitutionMap patternSubs,
48194819
SubstitutionMap invocationSubs, const ASTContext &ctx,
48204820
ProtocolConformanceRef witnessMethodConformance) {
4821-
assert(coroutineKind == SILCoroutineKind::None || normalResults.empty());
48224821
assert(coroutineKind != SILCoroutineKind::None || yields.empty());
48234822
assert(!ext.isPseudogeneric() || genericSig ||
48244823
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
}
@@ -5814,6 +5824,53 @@ void irgen::emitAsyncReturn(IRGenFunction &IGF, AsyncContextLayout &asyncLayout,
58145824
emitAsyncReturn(IGF, asyncLayout, fnType, nativeResults);
58155825
}
58165826

5827+
void irgen::emitYieldOnceCoroutineResult(IRGenFunction &IGF, Explosion &result,
5828+
SILType funcResultType, SILType returnResultType) {
5829+
auto &Builder = IGF.Builder;
5830+
auto &IGM = IGF.IGM;
5831+
5832+
// Create coroutine exit block and branch to it.
5833+
auto coroEndBB = IGF.createBasicBlock("coro.end.normal");
5834+
IGF.setCoroutineExitBlock(coroEndBB);
5835+
Builder.CreateBr(coroEndBB);
5836+
5837+
// Emit the block.
5838+
Builder.emitBlock(coroEndBB);
5839+
auto handle = IGF.getCoroutineHandle();
5840+
5841+
llvm::Value *resultToken = nullptr;
5842+
if (result.empty()) {
5843+
assert(IGM.getTypeInfo(returnResultType)
5844+
.nativeReturnValueSchema(IGM)
5845+
.empty() &&
5846+
"Empty explosion must match the native calling convention");
5847+
// No results: just use none token
5848+
resultToken = llvm::ConstantTokenNone::get(Builder.getContext());
5849+
} else {
5850+
// Capture results via `coro_end_results` intrinsic
5851+
result = IGF.coerceValueTo(returnResultType, result, funcResultType);
5852+
auto &nativeSchema =
5853+
IGM.getTypeInfo(funcResultType).nativeReturnValueSchema(IGM);
5854+
assert(!nativeSchema.requiresIndirect());
5855+
5856+
Explosion native = nativeSchema.mapIntoNative(IGM, IGF, result,
5857+
funcResultType,
5858+
false /* isOutlined */);
5859+
SmallVector<llvm::Value *, 1> args;
5860+
for (unsigned i = 0, e = native.size(); i != e; ++i)
5861+
args.push_back(native.claimNext());
5862+
5863+
resultToken =
5864+
Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_end_results, args);
5865+
}
5866+
5867+
Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_end,
5868+
{handle,
5869+
/*is unwind*/ Builder.getFalse(),
5870+
resultToken});
5871+
Builder.CreateUnreachable();
5872+
}
5873+
58175874
FunctionPointer
58185875
IRGenFunction::getFunctionPointerForResumeIntrinsic(llvm::Value *resume) {
58195876
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
@@ -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)