Skip to content

Commit c037b2d

Browse files
committed
Allow normal function results of @yield_once coroutines
1 parent 684739e commit c037b2d

File tree

68 files changed

+528
-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

+528
-287
lines changed

docs/SIL.rst

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

6004+
A coroutine optionally may produce normal results. These do not have
6005+
``@yields`` annotation in the result type tuple.
6006+
::
6007+
(%float, %token) = begin_apply %0() : $@yield_once () -> (@yields Float, Int)
6008+
6009+
Normal results of a coroutine are produced by the corresponding ``end_apply``
6010+
instruction.
6011+
60046012
A ``begin_apply`` must be uniquely either ended or aborted before
60056013
exiting the function or looping to an earlier portion of the function.
60066014

@@ -6030,9 +6038,9 @@ end_apply
60306038
`````````
60316039
::
60326040

6033-
sil-instruction ::= 'end_apply' sil-value
6041+
sil-instruction ::= 'end_apply' sil-value ':' sil-type
60346042

6035-
end_apply %token
6043+
end_apply %token : $()
60366044

60376045
Ends the given coroutine activation, which is currently suspended at
60386046
a ``yield`` instruction. Transfers control to the coroutine and takes
@@ -6042,8 +6050,8 @@ when the coroutine reaches a ``return`` instruction.
60426050
The operand must always be the token result of a ``begin_apply``
60436051
instruction, which is why it need not specify a type.
60446052

6045-
``end_apply`` currently has no instruction results. If coroutines were
6046-
allowed to have normal results, they would be producted by ``end_apply``.
6053+
If a coroutine produces normal results on ``resume`` path, they
6054+
will be produced by ``end_apply``.
60476055

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

include/swift/AST/Types.h

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

45934593
private:
4594-
unsigned NumParameters;
4594+
unsigned NumParameters = 0;
45954595

4596-
// These are *normal* results if this is not a coroutine and *yield* results
4597-
// otherwise.
4598-
unsigned NumAnyResults; // Not including the ErrorResult.
4599-
unsigned NumAnyIndirectFormalResults; // Subset of NumAnyResults.
4600-
unsigned NumPackResults; // Subset of NumAnyIndirectFormalResults.
4596+
// These are *normal* results
4597+
unsigned NumAnyResults = 0; // Not including the ErrorResult.
4598+
unsigned NumAnyIndirectFormalResults = 0; // Subset of NumAnyResults.
4599+
unsigned NumPackResults = 0; // Subset of NumAnyIndirectFormalResults.
4600+
// These are *yield* results
4601+
unsigned NumAnyYieldResults = 0; // Not including the ErrorResult.
4602+
unsigned NumAnyIndirectFormalYieldResults = 0; // Subset of NumAnyYieldResults.
4603+
unsigned NumPackYieldResults = 0; // Subset of NumAnyIndirectFormalYieldResults.
46014604

46024605
// [NOTE: SILFunctionType-layout]
46034606
// The layout of a SILFunctionType in memory is:
46044607
// SILFunctionType
46054608
// SILParameterInfo[NumParameters]
4606-
// SILResultInfo[isCoroutine() ? 0 : NumAnyResults]
4609+
// SILResultInfo[NumAnyResults]
46074610
// SILResultInfo? // if hasErrorResult()
4608-
// SILYieldInfo[isCoroutine() ? NumAnyResults : 0]
4611+
// SILYieldInfo[NumAnyYieldResults]
46094612
// SubstitutionMap[HasPatternSubs + HasInvocationSubs]
4610-
// CanType? // if !isCoro && NumAnyResults > 1, formal result cache
4611-
// CanType? // if !isCoro && NumAnyResults > 1, all result cache
4613+
// CanType? // if NumAnyResults > 1, formal result cache
4614+
// CanType? // if NumAnyResults > 1, all result cache
46124615

46134616
CanGenericSignature InvocationGenericSig;
46144617
ProtocolConformanceRef WitnessMethodConformance;
@@ -4643,7 +4646,7 @@ class SILFunctionType final
46434646

46444647
/// Do we have slots for caches of the normal-result tuple type?
46454648
bool hasResultCache() const {
4646-
return NumAnyResults > 1 && !isCoroutine();
4649+
return NumAnyResults > 1;
46474650
}
46484651

46494652
CanType &getMutableFormalResultsCache() const {
@@ -4731,14 +4734,14 @@ class SILFunctionType final
47314734
ArrayRef<SILYieldInfo> getYields() const {
47324735
return const_cast<SILFunctionType *>(this)->getMutableYields();
47334736
}
4734-
unsigned getNumYields() const { return isCoroutine() ? NumAnyResults : 0; }
4737+
unsigned getNumYields() const { return NumAnyYieldResults; }
47354738

47364739
/// Return the array of all result information. This may contain inter-mingled
47374740
/// direct and indirect results.
47384741
ArrayRef<SILResultInfo> getResults() const {
47394742
return const_cast<SILFunctionType *>(this)->getMutableResults();
47404743
}
4741-
unsigned getNumResults() const { return isCoroutine() ? 0 : NumAnyResults; }
4744+
unsigned getNumResults() const { return NumAnyResults; }
47424745

47434746
/// Given that this function type has exactly one result, return it.
47444747
/// This is a common situation when working with a function with a known
@@ -4768,17 +4771,17 @@ class SILFunctionType final
47684771
// indirect property, not the SIL indirect property, should be consulted to
47694772
// determine whether function reabstraction is necessary.
47704773
unsigned getNumIndirectFormalResults() const {
4771-
return isCoroutine() ? 0 : NumAnyIndirectFormalResults;
4774+
return NumAnyIndirectFormalResults;
47724775
}
47734776
/// Does this function have any formally indirect results?
47744777
bool hasIndirectFormalResults() const {
47754778
return getNumIndirectFormalResults() != 0;
47764779
}
47774780
unsigned getNumDirectFormalResults() const {
4778-
return isCoroutine() ? 0 : NumAnyResults - NumAnyIndirectFormalResults;
4781+
return NumAnyResults - NumAnyIndirectFormalResults;
47794782
}
47804783
unsigned getNumPackResults() const {
4781-
return isCoroutine() ? 0 : NumPackResults;
4784+
return NumPackResults;
47824785
}
47834786
bool hasIndirectErrorResult() const {
47844787
return hasErrorResult() && getErrorResult().isFormalIndirect();
@@ -4836,17 +4839,17 @@ class SILFunctionType final
48364839
TypeExpansionContext expansion);
48374840

48384841
unsigned getNumIndirectFormalYields() const {
4839-
return isCoroutine() ? NumAnyIndirectFormalResults : 0;
4842+
return NumAnyIndirectFormalYieldResults;
48404843
}
48414844
/// Does this function have any formally indirect yields?
48424845
bool hasIndirectFormalYields() const {
48434846
return getNumIndirectFormalYields() != 0;
48444847
}
48454848
unsigned getNumDirectFormalYields() const {
4846-
return isCoroutine() ? NumAnyResults - NumAnyIndirectFormalResults : 0;
4849+
return NumAnyYieldResults - NumAnyIndirectFormalYieldResults;
48474850
}
48484851
unsigned getNumPackYields() const {
4849-
return isCoroutine() ? NumPackResults : 0;
4852+
return NumPackYieldResults;
48504853
}
48514854

48524855
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
@@ -3126,11 +3126,12 @@ class AbortApplyInst
31263126
/// normally.
31273127
class EndApplyInst
31283128
: public UnaryInstructionBase<SILInstructionKind::EndApplyInst,
3129-
NonValueInstruction> {
3129+
SingleValueInstruction> {
31303130
friend SILBuilder;
31313131

3132-
EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken)
3133-
: UnaryInstructionBase(debugLoc, beginApplyToken) {
3132+
EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken,
3133+
SILType Ty)
3134+
: UnaryInstructionBase(debugLoc, beginApplyToken, Ty) {
31343135
assert(isaResultOf<BeginApplyInst>(beginApplyToken) &&
31353136
isaResultOf<BeginApplyInst>(beginApplyToken)->isBeginApplyToken());
31363137
}

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
@@ -4363,29 +4363,29 @@ SILFunctionType::SILFunctionType(
43634363
Bits.SILFunctionType.HasClangTypeInfo = !ext.getClangTypeInfo().empty();
43644364
Bits.SILFunctionType.CoroutineKind = unsigned(coroutineKind);
43654365
NumParameters = params.size();
4366-
if (coroutineKind == SILCoroutineKind::None) {
4367-
assert(yields.empty());
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-
} else {
4380-
assert(normalResults.empty());
4381-
NumAnyResults = yields.size();
4382-
NumAnyIndirectFormalResults = 0;
4366+
assert((coroutineKind == SILCoroutineKind::None && yields.empty()) ||
4367+
coroutineKind != SILCoroutineKind::None);
4368+
4369+
NumAnyResults = normalResults.size();
4370+
NumAnyIndirectFormalResults = 0;
4371+
NumPackResults = 0;
4372+
for (auto &resultInfo : normalResults) {
4373+
if (resultInfo.isFormalIndirect())
4374+
NumAnyIndirectFormalResults++;
4375+
if (resultInfo.isPack())
4376+
NumPackResults++;
4377+
}
4378+
memcpy(getMutableResults().data(), normalResults.data(),
4379+
normalResults.size() * sizeof(SILResultInfo));
4380+
if (coroutineKind != SILCoroutineKind::None) {
4381+
NumAnyYieldResults = yields.size();
4382+
NumAnyIndirectFormalYieldResults = 0;
43834383
NumPackResults = 0;
43844384
for (auto &yieldInfo : yields) {
43854385
if (yieldInfo.isFormalIndirect())
4386-
NumAnyIndirectFormalResults++;
4386+
NumAnyIndirectFormalYieldResults++;
43874387
if (yieldInfo.isPack())
4388-
NumPackResults++;
4388+
NumPackYieldResults++;
43894389
}
43904390
memcpy(getMutableYields().data(), yields.data(),
43914391
yields.size() * sizeof(SILYieldInfo));
@@ -4555,7 +4555,6 @@ CanSILFunctionType SILFunctionType::get(
45554555
llvm::Optional<SILResultInfo> errorResult, SubstitutionMap patternSubs,
45564556
SubstitutionMap invocationSubs, const ASTContext &ctx,
45574557
ProtocolConformanceRef witnessMethodConformance) {
4558-
assert(coroutineKind == SILCoroutineKind::None || normalResults.empty());
45594558
assert(coroutineKind != SILCoroutineKind::None || yields.empty());
45604559
assert(!ext.isPseudogeneric() || genericSig ||
45614560
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
}
@@ -5797,6 +5807,53 @@ void irgen::emitAsyncReturn(IRGenFunction &IGF, AsyncContextLayout &asyncLayout,
57975807
emitAsyncReturn(IGF, asyncLayout, fnType, nativeResults);
57985808
}
57995809

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