Skip to content

Commit 8b7a75c

Browse files
authored
[IRGen] Support indirect results for coroutines (swiftlang#75322)
1 parent 65bf2d0 commit 8b7a75c

File tree

3 files changed

+86
-21
lines changed

3 files changed

+86
-21
lines changed

lib/IRGen/GenCall.cpp

+16-9
Original file line numberDiff line numberDiff line change
@@ -760,9 +760,6 @@ void SignatureExpansion::expandCoroutineResult(bool forContinuation) {
760760

761761
// Yield-once coroutines may optionaly return a value from the continuation.
762762
case SILCoroutineKind::YieldOnce: {
763-
auto fnConv = getSILFuncConventions();
764-
765-
assert(fnConv.getNumIndirectSILResults() == 0);
766763
// Ensure that no parameters were added before to correctly record their ABI
767764
// details.
768765
assert(ParamIRTypes.empty());
@@ -1930,6 +1927,17 @@ void SignatureExpansion::expandParameters(
19301927
case SILCoroutineKind::YieldOnce:
19311928
case SILCoroutineKind::YieldMany:
19321929
addCoroutineContextParameter();
1930+
1931+
// Add indirect results as parameters. Similar to
1932+
// expandIndirectResults, but it doesn't add sret attribute,
1933+
// because the function has direct results (a continuation pointer
1934+
// and yield results).
1935+
auto fnConv = getSILFuncConventions();
1936+
for (auto indirectResultType : fnConv.getIndirectSILResultTypes(
1937+
IGM.getMaximalTypeExpansionContext())) {
1938+
auto storageTy = IGM.getStorageType(indirectResultType);
1939+
addPointerParameter(storageTy);
1940+
}
19331941
break;
19341942
}
19351943

@@ -6330,13 +6338,12 @@ void irgen::emitYieldOnceCoroutineResult(IRGenFunction &IGF, Explosion &result,
63306338
// Capture results via result token
63316339
resultToken =
63326340
Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_end_results, coroResults);
6333-
6334-
Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_end,
6335-
{handle,
6336-
/*is unwind*/ Builder.getFalse(),
6337-
resultToken});
6338-
Builder.CreateUnreachable();
63396341
}
6342+
Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_end,
6343+
{handle,
6344+
/*is unwind*/ Builder.getFalse(),
6345+
resultToken});
6346+
Builder.CreateUnreachable();
63406347
} else {
63416348
if (coroResults.empty()) {
63426349
// No results, we do not need to change anything around existing coro.end

lib/IRGen/IRGenSIL.cpp

+13-12
Original file line numberDiff line numberDiff line change
@@ -2114,6 +2114,19 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF,
21142114
getNativeCCEntryPointArgumentEmission(IGF, *entry, allParamValues);
21152115
auto funcTy = IGF.CurSILFn->getLoweredFunctionType();
21162116

2117+
// Coroutine context should be the first parameter.
2118+
// Indirect returns (if present) follow it.
2119+
switch (funcTy->getCoroutineKind()) {
2120+
case SILCoroutineKind::None:
2121+
break;
2122+
case SILCoroutineKind::YieldOnce:
2123+
emitYieldOnceCoroutineEntry(IGF, funcTy, *emission);
2124+
break;
2125+
case SILCoroutineKind::YieldMany:
2126+
emitYieldManyCoroutineEntry(IGF, funcTy, *emission);
2127+
break;
2128+
}
2129+
21172130
// Map the indirect return if present.
21182131
ArrayRef<SILArgument *> params = emitEntryPointIndirectReturn(
21192132
*emission, IGF, entry, funcTy, [&](SILType retType) -> bool {
@@ -2130,18 +2143,6 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF,
21302143
witnessMetadata);
21312144
}
21322145

2133-
// The coroutine context should be the first parameter.
2134-
switch (funcTy->getCoroutineKind()) {
2135-
case SILCoroutineKind::None:
2136-
break;
2137-
case SILCoroutineKind::YieldOnce:
2138-
emitYieldOnceCoroutineEntry(IGF, funcTy, *emission);
2139-
break;
2140-
case SILCoroutineKind::YieldMany:
2141-
emitYieldManyCoroutineEntry(IGF, funcTy, *emission);
2142-
break;
2143-
}
2144-
21452146
SILFunctionConventions fnConv(funcTy, IGF.getSILModule());
21462147
if (funcTy->isAsync()) {
21472148
emitAsyncFunctionEntry(IGF, getAsyncContextLayout(IGF.IGM, IGF.CurSILFn),

test/IRGen/yield_result.sil

+57
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,60 @@ entry:
9191
%retf = tuple (%ret : $Builtin.Int64, %ret2_1 : $Builtin.Int64, %ret2_2 : $Builtin.Int64)
9292
return %retf : $(Builtin.Int64, Builtin.Int64, Builtin.Int64)
9393
}
94+
95+
// CHECK-LABEL: coro_ret_indirect
96+
// CHECK-SAME: ptr{{.*}} [[CTX:%.*]], ptr{{.*}} [[INDIRECT_RET:%.*]], ptr{{.*}} [[ARG:%.*]], ptr{{.*}} [[TYPE:%.*]])
97+
sil @coro_ret_indirect : $@yield_once @convention(thin) <T> (@in T) -> (@yields @in T, @out T) {
98+
bb0(%outt : $*T, %t : $*T):
99+
// CHECK-32: [[ID:%.*]] = call token @llvm.coro.id.retcon.once(i32 [[BUFFER_SIZE]], i32 [[BUFFER_ALIGN:4]], ptr [[CTX]], ptr @"$sxxxlIetAirYi_TC{{(.ptrauth)?}}", ptr @malloc, ptr @free)
100+
// CHECK-64: [[ID:%.*]] = call token @llvm.coro.id.retcon.once(i32 [[BUFFER_SIZE]], i32 [[BUFFER_ALIGN:8]], ptr [[CTX]], ptr @"$sxxxlIetAirYi_TC{{(.ptrauth)?}}", ptr @malloc, ptr @free)
101+
102+
// CHECK: [[IS_UNWIND:%.*]] = call i1 (...) @llvm.coro.suspend.retcon.i1(ptr [[ARG]])
103+
// CHECK: br i1 [[IS_UNWIND]], label %[[UNWIND_BB:.*]], label %[[RESUME_BB:.*]]
104+
105+
// CHECK:[[RESUME_BB]]:
106+
// CHECK: [[VW_PTR:%.*]] = getelementptr inbounds ptr, ptr [[TYPE]], i64 -1
107+
// CHECK: [[VW:%.*]] = load ptr, ptr [[VW_PTR]]
108+
// CHECK: [[ASSIGN_PTR:%.*]] = getelementptr inbounds ptr, ptr [[VW]], i32 3
109+
// CHECK: [[ASSIGN:%.*]] = load ptr, ptr [[ASSIGN_PTR]]
110+
// CHECK: call ptr [[ASSIGN]](ptr [[INDIRECT_RET]], ptr [[ARG]], ptr [[TYPE]]) #2
111+
112+
yield (%t : $*T), resume bb1, unwind bb2
113+
114+
bb1:
115+
copy_addr %t to %outt : $*T
116+
%r = tuple ()
117+
return %r : $()
118+
119+
bb2:
120+
unwind
121+
}
122+
123+
// CHECK-LABEL: @test_coro_ret_indirect
124+
// CHECK-SAME: (i64 [[ARG:%.*]])
125+
sil [ossa] @test_coro_ret_indirect : $(Builtin.Int64) -> () {
126+
bb0(%0 : $Builtin.Int64):
127+
// CHECK: [[ARG_COPY:%.*]] = alloca i64
128+
// CHECK: [[INDIRECT_RET:%.*]] = alloca i64
129+
// CHECK: [[FRAME:%.*]] = alloca [32 x i8]
130+
%coro = function_ref @coro_ret_indirect : $@yield_once @convention(thin) <T> (@in T) -> (@yields @in T, @out T)
131+
%temp = alloc_stack $Builtin.Int64
132+
store %0 to [trivial] %temp : $*Builtin.Int64
133+
134+
%out = alloc_stack $Builtin.Int64
135+
136+
// CHECK: store i64 [[ARG]], ptr [[ARG_COPY]]
137+
// CHECK: [[CTX:%.*]] = getelementptr inbounds [32 x i8], ptr [[FRAME]], i32 0, i32 0
138+
// CHECK: [[CORO:%.*]] = call ptr @llvm.coro.prepare.retcon(ptr @coro_ret_indirect)
139+
// CHECK: [[FRAME:%.*]] = call swiftcc { ptr, ptr } [[CORO]](ptr{{.*}} [[CTX]], ptr [[INDIRECT_RET]], ptr noalias [[ARG_COPY]], ptr getelementptr inbounds (%swift.full_existential_type, ptr @{{.*}}
140+
141+
(%f1, %token) = begin_apply %coro<Builtin.Int64>(%out, %temp) : $@yield_once @convention(thin) <T> (@in T) -> (@yields @in T, @out T)
142+
143+
%f2 = end_apply %token as $()
144+
145+
dealloc_stack %out : $*Builtin.Int64
146+
dealloc_stack %temp : $*Builtin.Int64
147+
148+
%r = tuple ()
149+
return %r : $()
150+
}

0 commit comments

Comments
 (0)