Skip to content

Commit 4e296c0

Browse files
committed
IRGen: emit emit_hop_to_executor instructions.
This basically means to create a suspension point and call the swift_task_switch runtime function. rdar://71099289
1 parent 89007e2 commit 4e296c0

File tree

6 files changed

+193
-3
lines changed

6 files changed

+193
-3
lines changed

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,6 +1518,16 @@ FUNCTION(TaskCreateFutureFunc,
15181518
TaskContinuationFunctionPtrTy, SizeTy),
15191519
ATTRS(NoUnwind, ArgMemOnly))
15201520

1521+
// void swift_task_switch(AsyncTask *task,
1522+
// ExecutorRef currentExecutor,
1523+
// ExecutorRef newExecutor);
1524+
FUNCTION(TaskSwitchFunc,
1525+
swift_task_switch, SwiftCC,
1526+
ConcurrencyAvailability,
1527+
RETURNS(VoidTy),
1528+
ARGS(SwiftTaskPtrTy, SwiftExecutorPtrTy, SwiftExecutorPtrTy),
1529+
ATTRS(NoUnwind))
1530+
15211531
// AutoDiffLinearMapContext *swift_autoDiffCreateLinearMapContext(size_t);
15221532
FUNCTION(AutoDiffCreateLinearMapContext,
15231533
swift_autoDiffCreateLinearMapContext, SwiftCC,

lib/IRGen/GenFunc.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2444,3 +2444,83 @@ IRGenFunction::createAsyncDispatchFn(const FunctionPointer &fnPtr,
24442444
Builder.CreateRetVoid();
24452445
return dispatch;
24462446
}
2447+
2448+
void IRGenFunction::emitSuspensionPoint(llvm::Value *toExecutor,
2449+
llvm::Value *asyncResume) {
2450+
// TODO: pointerauth
2451+
2452+
// Setup the suspend point.
2453+
SmallVector<llvm::Value *, 8> arguments;
2454+
arguments.push_back(asyncResume);
2455+
auto resumeProjFn = getOrCreateResumeFromSuspensionFn();
2456+
arguments.push_back(
2457+
Builder.CreateBitOrPointerCast(resumeProjFn, IGM.Int8PtrTy));
2458+
llvm::Function *suspendFn = createAsyncSuspendFn();
2459+
arguments.push_back(
2460+
Builder.CreateBitOrPointerCast(suspendFn, IGM.Int8PtrTy));
2461+
2462+
arguments.push_back(asyncResume);
2463+
arguments.push_back(
2464+
Builder.CreateBitOrPointerCast(toExecutor, getAsyncExecutor()->getType()));
2465+
arguments.push_back(getAsyncTask());
2466+
arguments.push_back(getAsyncExecutor());
2467+
arguments.push_back(getAsyncContext());
2468+
2469+
emitSuspendAsyncCall(arguments);
2470+
}
2471+
2472+
llvm::Function *IRGenFunction::getOrCreateResumeFromSuspensionFn() {
2473+
auto name = "__swift_async_resume_get_context";
2474+
return cast<llvm::Function>(IGM.getOrCreateHelperFunction(
2475+
name, IGM.Int8PtrTy, {IGM.Int8PtrTy},
2476+
[&](IRGenFunction &IGF) {
2477+
auto &Builder = IGF.Builder;
2478+
Builder.CreateRet(&*IGF.CurFn->arg_begin());
2479+
},
2480+
false /*isNoInline*/));
2481+
}
2482+
2483+
llvm::Function *IRGenFunction::createAsyncSuspendFn() {
2484+
SmallVector<llvm::Type*, 8> argTys;
2485+
argTys.push_back(IGM.Int8PtrTy); // Resume function.
2486+
argTys.push_back(getAsyncExecutor()->getType()); // Executor to hop to.
2487+
argTys.push_back(getAsyncTask()->getType());
2488+
argTys.push_back(getAsyncExecutor()->getType());
2489+
argTys.push_back(getAsyncContext()->getType());
2490+
auto *suspendFnTy =
2491+
llvm::FunctionType::get(IGM.VoidTy, argTys, false /*vaargs*/);
2492+
2493+
StringRef name = "__swift_suspend_point";
2494+
if (llvm::GlobalValue *F = IGM.Module.getNamedValue(name))
2495+
return cast<llvm::Function>(F);
2496+
2497+
llvm::Function *suspendFn =
2498+
llvm::Function::Create(suspendFnTy, llvm::Function::InternalLinkage,
2499+
name, &IGM.Module);
2500+
suspendFn->setCallingConv(IGM.DefaultCC);
2501+
suspendFn->setDoesNotThrow();
2502+
IRGenFunction suspendIGF(IGM, suspendFn);
2503+
if (IGM.DebugInfo)
2504+
IGM.DebugInfo->emitArtificialFunction(suspendIGF, suspendFn);
2505+
auto &Builder = suspendIGF.Builder;
2506+
2507+
llvm::Value *resumeFunction = suspendFn->getArg(0);
2508+
llvm::Value *targetExecutor = suspendFn->getArg(1);
2509+
llvm::Value *task = suspendFn->getArg(2);
2510+
llvm::Value *executor = suspendFn->getArg(3);
2511+
llvm::Value *context = suspendFn->getArg(4);
2512+
2513+
Alignment ptrAlign = IGM.getPointerAlignment();
2514+
auto *resumeAddr = Builder.CreateStructGEP(task, 4);
2515+
Builder.CreateStore(resumeFunction, Address(resumeAddr, ptrAlign));
2516+
auto *contextAddr = Builder.CreateStructGEP(task, 5);
2517+
Builder.CreateStore(context, Address(contextAddr, ptrAlign));
2518+
auto *suspendCall = Builder.CreateCall(
2519+
IGM.getTaskSwitchFuncFn(),
2520+
{ task, executor, targetExecutor });
2521+
suspendCall->setDoesNotThrow();
2522+
suspendCall->setCallingConv(IGM.SwiftCC);
2523+
suspendCall->setTailCall();
2524+
Builder.CreateRetVoid();
2525+
return suspendFn;
2526+
}

lib/IRGen/IRGenFunction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ class IRGenFunction {
153153
FunctionPointer
154154
getFunctionPointerForResumeIntrinsic(llvm::Value *resumeIntrinsic);
155155

156+
void emitSuspensionPoint(llvm::Value *toExecutor, llvm::Value *asyncResume);
157+
llvm::Function *getOrCreateResumeFromSuspensionFn();
158+
llvm::Function *createAsyncSuspendFn();
159+
156160
private:
157161
void emitPrologue();
158162
void emitEpilogue();

lib/IRGen/IRGenSIL.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,9 +1091,7 @@ class IRGenSILFunction :
10911091
void visitGetAsyncContinuationAddrInst(GetAsyncContinuationAddrInst *i);
10921092
void visitAwaitAsyncContinuationInst(AwaitAsyncContinuationInst *i);
10931093

1094-
void visitHopToExecutorInst(HopToExecutorInst *i) {
1095-
//TODO(async)
1096-
}
1094+
void visitHopToExecutorInst(HopToExecutorInst *i);
10971095

10981096
void visitKeyPathInst(KeyPathInst *I);
10991097

@@ -5714,6 +5712,13 @@ void IRGenSILFunction::visitCheckedCastAddrBranchInst(
57145712
getLoweredBB(i->getFailureBB()).bb);
57155713
}
57165714

5715+
void IRGenSILFunction::visitHopToExecutorInst(HopToExecutorInst *i) {
5716+
llvm::Value *resumeFn = Builder.CreateIntrinsicCall(
5717+
llvm::Intrinsic::coro_async_resume, {});
5718+
5719+
emitSuspensionPoint(getLoweredSingletonExplosion(i->getOperand()), resumeFn);
5720+
}
5721+
57175722
void IRGenSILFunction::visitKeyPathInst(swift::KeyPathInst *I) {
57185723
auto pattern = IGM.getAddrOfKeyPathPattern(I->getPattern(), I->getLoc());
57195724
// Build up the argument vector to instantiate the pattern here.

test/IRGen/async/hop_to_executor.sil

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-swift-frontend -enable-experimental-concurrency -primary-file %s -module-name=test -disable-llvm-optzns -disable-swift-specific-llvm-optzns -emit-ir -sil-verify-all | %FileCheck %s
2+
3+
// REQUIRES: concurrency
4+
5+
sil_stage canonical
6+
7+
import Builtin
8+
import Swift
9+
import _Concurrency
10+
11+
final actor class MyActor {
12+
}
13+
14+
// CHECK-LABEL: define{{.*}} void @test_simple(%swift.task* %0, %swift.executor* %1, %swift.context* %2)
15+
// CHECK: [[CTX:%[0-9]+]] = bitcast %swift.context* %2
16+
// CHECK: [[ACTOR_ADDR:%[0-9]+]] = getelementptr {{.*}} [[CTX]], i32 0, i32 6
17+
// CHECK: [[ACTOR:%[0-9]+]] = load %T4test7MyActorC*, %T4test7MyActorC** [[ACTOR_ADDR]]
18+
// CHECK: [[RESUME:%[0-9]+]] = call i8* @llvm.coro.async.resume()
19+
// CHECK: [[CAST_ACTOR:%[0-9]+]] = bitcast %T4test7MyActorC* [[ACTOR]] to %swift.executor*
20+
// CHECK: call {{.*}} @llvm.coro.suspend.async(i8* [[RESUME]], i8* bitcast (i8* (i8*)* @__swift_async_resume_get_context to i8*), i8* bitcast (void (i8*, %swift.executor*, %swift.task*, %swift.executor*, %swift.context*)* @__swift_suspend_point to i8*), i8* [[RESUME]], %swift.executor* [[CAST_ACTOR]], %swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}})
21+
sil @test_simple : $@convention(method) @async (@guaranteed MyActor) -> () {
22+
bb0(%0 : $MyActor):
23+
hop_to_executor %0 : $MyActor
24+
%3 = tuple ()
25+
return %3 : $()
26+
}
27+
28+
// CHECK-LABEL: define internal void @__swift_suspend_point(i8* %0, %swift.executor* %1, %swift.task* %2, %swift.executor* %3, %swift.context* %4)
29+
// CHECK: [[RESUME_ADDR:%[0-9]+]] = getelementptr inbounds %swift.task, %swift.task* %2, i32 0, i32 4
30+
// CHECK: store i8* %0, i8** [[RESUME_ADDR]]
31+
// CHECK: [[CTXT_ADDR:%[0-9]+]] = getelementptr inbounds %swift.task, %swift.task* %2, i32 0, i32 5
32+
// CHECK: store %swift.context* %4, %swift.context** [[CTXT_ADDR]]
33+
// CHECK: tail call swiftcc void @swift_task_switch(%swift.task* %2, %swift.executor* %3, %swift.executor* %1)
34+
// CHECK: ret void
35+
36+
sil_vtable MyActor {
37+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency %s -module-name main -o %t/main
3+
// RUN: %target-codesign %t/main
4+
// RUN: %target-run %t/main | %FileCheck %s
5+
6+
// REQUIRES: executable_test
7+
// REQUIRES: concurrency
8+
// UNSUPPORTED: use_os_stdlib
9+
// UNSUPPORTED: CPU=arm64e
10+
11+
// Currently this test just checks if nothing crashes.
12+
// TODO: also check if the current executor is the correct one.
13+
14+
final actor class MyActor {
15+
var p: Int
16+
17+
@inline(never)
18+
init(p: Int) {
19+
self.p = p
20+
}
21+
22+
@inline(never)
23+
func callee() async -> Int {
24+
print("callee")
25+
return p
26+
}
27+
28+
@inline(never)
29+
func testit() async -> Int {
30+
print("don't switch")
31+
let x = await callee()
32+
let otherActor = MyActor(p: 12)
33+
print("switch")
34+
let y = await otherActor.callee()
35+
print("switch back")
36+
return x + y + p
37+
}
38+
}
39+
40+
// CHECK: run
41+
// CHECK: don't switch
42+
// CHECK: callee
43+
// CHECK: switch
44+
// CHECK: callee
45+
// CHECK: switch back
46+
// CHECK: 66
47+
48+
runAsync {
49+
let a = MyActor(p: 27)
50+
print("run")
51+
await print(a.testit())
52+
}
53+
54+

0 commit comments

Comments
 (0)