Skip to content

Commit 6798ed1

Browse files
authored
Merge pull request #34627 from DougGregor/create-async-task-builtin
[Concurrency] Implement a builtin createAsyncTask() to create a new task
2 parents cdfff19 + d038989 commit 6798ed1

File tree

16 files changed

+178
-7
lines changed

16 files changed

+178
-7
lines changed

include/swift/AST/Builtins.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,13 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(GetCurrentAsyncTask, "getCurrentAsyncTask", "
728728
/// Cancel the given asynchronous task.
729729
BUILTIN_MISC_OPERATION_WITH_SILGEN(CancelAsyncTask, "cancelAsyncTask", "", Special)
730730

731+
/// createAsyncTask(): (
732+
/// Int, Builtin.NativeObject?, @escaping () async throws -> Void
733+
/// ) -> Builtin.NativeObject
734+
///
735+
/// Create a new asynchronous task, given flags, an (optional) parent task, and
736+
/// a function to execute.
737+
BUILTIN_MISC_OPERATION_WITH_SILGEN(CreateAsyncTask, "createAsyncTask", "", Special)
731738

732739
/// globalStringTablePointer has type String -> Builtin.RawPointer.
733740
/// It returns an immortal, global string table pointer for strings constructed

include/swift/AST/Builtins.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ llvm::AtomicOrdering decodeLLVMAtomicOrdering(StringRef O);
135135
/// Returns true if the builtin with ID \p ID has a defined static overload for
136136
/// the type \p Ty.
137137
bool canBuiltinBeOverloadedForType(BuiltinValueKind ID, Type Ty);
138+
139+
/// Retrieve the AST-level AsyncTaskAndContext type, used for the
140+
/// createAsyncTask builtin.
141+
Type getAsyncTaskAndContextType(ASTContext &ctx);
142+
138143
}
139144

140145
#endif

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1489,14 +1489,24 @@ FUNCTION(TaskDealloc,
14891489
ARGS(SwiftTaskPtrTy, Int8PtrTy),
14901490
ATTRS(NoUnwind, ArgMemOnly))
14911491

1492-
// void swift_task_dealloc(AsyncTask *task, void *ptr);
1492+
// void swift_task_cancel(AsyncTask *task);
14931493
FUNCTION(TaskCancel,
14941494
swift_task_cancel, SwiftCC,
14951495
ConcurrencyAvailability,
14961496
RETURNS(VoidTy),
14971497
ARGS(SwiftTaskPtrTy),
14981498
ATTRS(NoUnwind, ArgMemOnly))
14991499

1500+
// AsyncTaskAndContext swift_task_create_f(
1501+
// size_t flags, AsyncTask *task, AsyncFunctionType<void()> *function,
1502+
// size_t initialContextSize);
1503+
FUNCTION(TaskCreateFunc,
1504+
swift_task_create_f, SwiftCC,
1505+
ConcurrencyAvailability,
1506+
RETURNS(AsyncTaskAndContextTy),
1507+
ARGS(SizeTy, SwiftTaskPtrTy, TaskContinuationFunctionPtrTy, SizeTy),
1508+
ATTRS(NoUnwind, ArgMemOnly))
1509+
15001510
#undef RETURNS
15011511
#undef ARGS
15021512
#undef ATTRS

lib/AST/Builtins.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,6 +1350,25 @@ static ValueDecl *getCancelAsyncTask(ASTContext &ctx, Identifier id) {
13501350
id, { ctx.TheNativeObjectType }, ctx.TheEmptyTupleType);
13511351
}
13521352

1353+
Type swift::getAsyncTaskAndContextType(ASTContext &ctx) {
1354+
TupleTypeElt resultTupleElements[2] = {
1355+
ctx.TheNativeObjectType, // task,
1356+
ctx.TheRawPointerType // initial context
1357+
};
1358+
1359+
return TupleType::get(resultTupleElements, ctx);
1360+
}
1361+
1362+
static ValueDecl *getCreateAsyncTask(ASTContext &ctx, Identifier id) {
1363+
auto extInfo = ASTExtInfoBuilder().withAsync().withThrows().build();
1364+
return getBuiltinFunction(
1365+
id,
1366+
{ ctx.getIntDecl()->getDeclaredInterfaceType(),
1367+
OptionalType::get(ctx.TheNativeObjectType),
1368+
FunctionType::get({ }, ctx.TheEmptyTupleType, extInfo) },
1369+
getAsyncTaskAndContextType(ctx));
1370+
}
1371+
13531372
static ValueDecl *getPoundAssert(ASTContext &Context, Identifier Id) {
13541373
auto int1Type = BuiltinIntegerType::get(1, Context);
13551374
auto optionalRawPointerType = BoundGenericEnumType::get(
@@ -2482,6 +2501,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
24822501
case BuiltinValueKind::CancelAsyncTask:
24832502
return getCancelAsyncTask(Context, Id);
24842503

2504+
case BuiltinValueKind::CreateAsyncTask:
2505+
return getCreateAsyncTask(Context, Id);
2506+
24852507
case BuiltinValueKind::PoundAssert:
24862508
return getPoundAssert(Context, Id);
24872509

lib/IRGen/GenBuiltin.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,30 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
225225
return;
226226
}
227227

228+
if (Builtin.ID == BuiltinValueKind::CreateAsyncTask) {
229+
auto flags = args.claimNext();
230+
auto parentTask = args.claimNext();
231+
auto taskFunction = args.claimNext();
232+
auto taskContext = args.claimNext();
233+
234+
// FIXME: SIL treats the function/context parameter as "guaranteed", but
235+
// the runtime entry point assumes it is owned. Introduce an extra retain
236+
// of the context to balance things out.
237+
IGF.emitNativeStrongRetain(taskContext, IGF.getDefaultAtomicity());
238+
239+
auto newTaskAndContext = emitTaskCreate(
240+
IGF, flags, parentTask, taskFunction, taskContext);
241+
242+
// Cast back to NativeObject/RawPointer.
243+
auto newTask = IGF.Builder.CreateExtractValue(newTaskAndContext, { 0 });
244+
newTask = IGF.Builder.CreateBitCast(newTask, IGF.IGM.RefCountedPtrTy);
245+
auto newContext = IGF.Builder.CreateExtractValue(newTaskAndContext, { 1 });
246+
newContext = IGF.Builder.CreateBitCast(newContext, IGF.IGM.Int8PtrTy);
247+
out.add(newTask);
248+
out.add(newContext);
249+
return;
250+
}
251+
228252
// If this is an LLVM IR intrinsic, lower it to an intrinsic call.
229253
const IntrinsicInfo &IInfo = IGF.getSILModule().getIntrinsicInfo(FnId);
230254
llvm::Intrinsic::ID IID = IInfo.ID;

lib/IRGen/GenCall.cpp

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3466,8 +3466,52 @@ void irgen::emitTaskCancel(IRGenFunction &IGF, llvm::Value *task) {
34663466
auto *call = IGF.Builder.CreateCall(IGF.IGM.getTaskCancelFn(), {task});
34673467
call->setDoesNotThrow();
34683468
call->setCallingConv(IGF.IGM.SwiftCC);
3469-
call->addAttribute(llvm::AttributeList::FunctionIndex,
3470-
llvm::Attribute::ReadNone);
3469+
}
3470+
3471+
llvm::Value *irgen::emitTaskCreate(
3472+
IRGenFunction &IGF, llvm::Value *flags, llvm::Value *parentTask,
3473+
llvm::Value *taskFunction, llvm::Value *localContextInfo) {
3474+
parentTask = IGF.Builder.CreateBitOrPointerCast(
3475+
parentTask, IGF.IGM.SwiftTaskPtrTy);
3476+
taskFunction = IGF.Builder.CreateBitOrPointerCast(
3477+
taskFunction, IGF.IGM.TaskContinuationFunctionPtrTy);
3478+
3479+
// Determine the size of the async context for the closure.
3480+
// FIXME: If the task function comes in as an AsyncFunctionPointer, we might
3481+
// want to use swift_task_create instead of swift_task_create_f.
3482+
ASTContext &ctx = IGF.IGM.IRGen.SIL.getASTContext();
3483+
auto extInfo = ASTExtInfoBuilder().withAsync().withThrows().build();
3484+
auto taskFunctionType = FunctionType::get(
3485+
{ }, ctx.TheEmptyTupleType, extInfo);
3486+
CanSILFunctionType taskFunctionCanSILType =
3487+
IGF.IGM.getLoweredType(taskFunctionType).castTo<SILFunctionType>();
3488+
auto layout = getAsyncContextLayout(
3489+
IGF.IGM, taskFunctionCanSILType, taskFunctionCanSILType,
3490+
SubstitutionMap());
3491+
auto layoutSize = getAsyncContextSize(layout);
3492+
auto layoutSizeVal = llvm::ConstantInt::get(
3493+
IGF.IGM.SizeTy, layoutSize.getValue());
3494+
3495+
// Call the function.
3496+
auto *result = IGF.Builder.CreateCall(
3497+
IGF.IGM.getTaskCreateFuncFn(),
3498+
{ flags, parentTask, taskFunction, layoutSizeVal });
3499+
result->setDoesNotThrow();
3500+
result->setCallingConv(IGF.IGM.SwiftCC);
3501+
3502+
// Write the local context information into the initial context for the task.
3503+
if (layout.hasLocalContext()) {
3504+
// Dig out the initial context returned from task creation.
3505+
auto initialContext = IGF.Builder.CreateExtractValue(result, { 1 });
3506+
Address initialContextAddr = layout.emitCastTo(IGF, initialContext);
3507+
3508+
auto localContextLayout = layout.getLocalContextLayout();
3509+
auto localContextAddr = localContextLayout.project(
3510+
IGF, initialContextAddr, llvm::None);
3511+
IGF.Builder.CreateStore(localContextInfo, localContextAddr);
3512+
}
3513+
3514+
return result;
34713515
}
34723516

34733517
std::pair<Address, Size> irgen::emitAllocAsyncContext(IRGenFunction &IGF,

lib/IRGen/GenCall.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,12 @@ namespace irgen {
390390
void emitTaskDealloc(IRGenFunction &IGF, Address address, llvm::Value *size);
391391
void emitTaskCancel(IRGenFunction &IGF, llvm::Value *task);
392392

393+
/// Emit a class to swift_task_create[_f] with the given flags, parent task,
394+
/// and task function.
395+
llvm::Value *emitTaskCreate(
396+
IRGenFunction &IGF, llvm::Value *flags, llvm::Value *parentTask,
397+
llvm::Value *taskFunction, llvm::Value *localContextInfo);
398+
393399
/// Allocate task local storage for the specified layout but using the
394400
/// provided dynamic size. Allowing the size to be specified dynamically is
395401
/// necessary for applies of thick functions the sizes of whose async contexts

lib/IRGen/IRGenModule.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,10 @@ IRGenModule::IRGenModule(IRGenerator &irgen,
602602
/*isVarArg*/ false);
603603
TaskContinuationFunctionPtrTy = TaskContinuationFunctionTy->getPointerTo();
604604

605+
AsyncTaskAndContextTy = createStructType(
606+
*this, "swift.async_task_and_context",
607+
{ SwiftTaskPtrTy, SwiftContextPtrTy });
608+
605609
DifferentiabilityWitnessTy = createStructType(
606610
*this, "swift.differentiability_witness", {Int8PtrTy, Int8PtrTy});
607611
}

lib/IRGen/IRGenModule.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ class IRGenModule {
731731
llvm::PointerType *SwiftExecutorPtrTy;
732732
llvm::FunctionType *TaskContinuationFunctionTy;
733733
llvm::PointerType *TaskContinuationFunctionPtrTy;
734-
734+
llvm::StructType *AsyncTaskAndContextTy;
735735
llvm::StructType *DifferentiabilityWitnessTy; // { i8*, i8* }
736736

737737
llvm::GlobalVariable *TheTrivialPropertyDescriptor = nullptr;

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,7 @@ ANY_OWNERSHIP_BUILTIN(IntInstrprofIncrement)
918918
CONSTANT_OWNERSHIP_BUILTIN(Owned, LifetimeEnding, COWBufferForReading)
919919
CONSTANT_OWNERSHIP_BUILTIN(Owned, LifetimeEnding, UnsafeGuaranteed)
920920
CONSTANT_OWNERSHIP_BUILTIN(Guaranteed, NonLifetimeEnding, CancelAsyncTask)
921+
CONSTANT_OWNERSHIP_BUILTIN(Guaranteed, NonLifetimeEnding, CreateAsyncTask)
921922

922923
#undef CONSTANT_OWNERSHIP_BUILTIN
923924

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ CONSTANT_OWNERSHIP_BUILTIN(None, IntInstrprofIncrement)
543543
CONSTANT_OWNERSHIP_BUILTIN(None, GlobalStringTablePointer)
544544
CONSTANT_OWNERSHIP_BUILTIN(Owned, GetCurrentAsyncTask)
545545
CONSTANT_OWNERSHIP_BUILTIN(None, CancelAsyncTask)
546+
CONSTANT_OWNERSHIP_BUILTIN(Owned, CreateAsyncTask)
546547

547548
#undef CONSTANT_OWNERSHIP_BUILTIN
548549

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,6 +1800,7 @@ static void visitBuiltinAddress(BuiltinInst *builtin,
18001800
case BuiltinValueKind::IntInstrprofIncrement:
18011801
case BuiltinValueKind::TSanInoutAccess:
18021802
case BuiltinValueKind::CancelAsyncTask:
1803+
case BuiltinValueKind::CreateAsyncTask:
18031804
return;
18041805

18051806
// General memory access to a pointer in first operand position.

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1405,7 +1405,7 @@ static ManagedValue emitBuiltinGetCurrentAsyncTask(
14051405
return SGF.emitManagedRValueWithEndLifetimeCleanup(apply);
14061406
}
14071407

1408-
// Emit SIL for the named builtin: getCurrentAsyncTask.
1408+
// Emit SIL for the named builtin: cancelAsyncTask.
14091409
static ManagedValue emitBuiltinCancelAsyncTask(
14101410
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
14111411
ArrayRef<ManagedValue> args, SGFContext C) {
@@ -1419,6 +1419,22 @@ static ManagedValue emitBuiltinCancelAsyncTask(
14191419
return ManagedValue::forUnmanaged(apply);
14201420
}
14211421

1422+
// Emit SIL for the named builtin: createAsyncTask.
1423+
static ManagedValue emitBuiltinCreateAsyncTask(
1424+
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
1425+
ArrayRef<ManagedValue> args, SGFContext C) {
1426+
ASTContext &ctx = SGF.getASTContext();
1427+
auto flags = args[0].forward(SGF);
1428+
auto parentTask = args[1].borrow(SGF, loc).forward(SGF);
1429+
auto function = args[2].borrow(SGF, loc).forward(SGF);
1430+
auto apply = SGF.B.createBuiltin(
1431+
loc,
1432+
ctx.getIdentifier(getBuiltinName(BuiltinValueKind::CreateAsyncTask)),
1433+
SGF.getLoweredType(getAsyncTaskAndContextType(ctx)), SubstitutionMap(),
1434+
{ flags, parentTask, function });
1435+
return SGF.emitManagedRValueWithCleanup(apply);
1436+
}
1437+
14221438
Optional<SpecializedEmitter>
14231439
SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) {
14241440
// Only consider standalone declarations in the Builtin module.

lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ static bool isBarrier(SILInstruction *inst) {
166166
case BuiltinValueKind::UnsafeGuaranteed:
167167
case BuiltinValueKind::UnsafeGuaranteedEnd:
168168
case BuiltinValueKind::CancelAsyncTask:
169+
case BuiltinValueKind::CreateAsyncTask:
169170
return true;
170171
}
171172
}

test/IRGen/async/builtins.sil

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
sil_stage canonical
77

88
import Builtin
9+
import Swift
910

1011
// CHECK-LABEL: define hidden swiftcc void @get_task(%swift.task* %0, %swift.executor* %1, %swift.context* %2)
1112
sil hidden [ossa] @get_task : $@async @convention(thin) () -> @owned Builtin.NativeObject {
@@ -21,10 +22,26 @@ bb0:
2122
// CHECK-LABEL: define hidden swiftcc void @cancel_task(%swift.refcounted* %0)
2223
// CHECK: [[TASK:%.*]] = bitcast %swift.refcounted* %0 to %swift.task*
2324
// CHECK-NEXT: call swiftcc void @swift_task_cancel(%swift.task* [[TASK]])
24-
2525
sil hidden [ossa] @cancel_task : $@convention(method) (@guaranteed Builtin.NativeObject) -> () {
2626
bb0(%0 : @guaranteed $Builtin.NativeObject):
2727
%4 = builtin "cancelAsyncTask"(%0 : $Builtin.NativeObject) : $()
2828
%5 = tuple ()
2929
return %5 : $()
30+
}
31+
32+
// CHECK-LABEL: define hidden swiftcc void @launch_task
33+
sil hidden [ossa] @launch_task : $@convention(method) @async (Int, Optional<Builtin.NativeObject>, @guaranteed @async @callee_guaranteed () -> (@error Error)) -> () {
34+
bb0(%0 : $Int, %1: @unowned $Optional<Builtin.NativeObject>, %2: @guaranteed $@async @callee_guaranteed () -> (@error Error)):
35+
%3 = begin_borrow %1 : $Optional<Builtin.NativeObject>
36+
// CHECK: call %swift.refcounted* @swift_retain(%swift.refcounted* returned [[FN_CONTEXT:%.*]])
37+
// CHECK: [[NEW_TASK_AND_CONTEXT:%.*]] = call swiftcc %swift.async_task_and_context @swift_task_create_f
38+
// CHECK-NEXT: [[NEW_CONTEXT_RAW:%.*]] = extractvalue %swift.async_task_and_context [[NEW_TASK_AND_CONTEXT]], 1
39+
// CHECK-NEXT: [[NEW_CONTEXT:%.*]] = bitcast %swift.context* [[NEW_CONTEXT_RAW]] to
40+
// CHECK-NEXT: [[CONTEXT_INFO_LOC:%.*]] = getelementptr inbounds <{{.*}}>* [[NEW_CONTEXT]]
41+
// CHECK-NEXT: store %swift.refcounted* [[FN_CONTEXT]], %swift.refcounted** [[CONTEXT_INFO_LOC]]
42+
%20 = builtin "createAsyncTask"(%0 : $Int, %3 : $Optional<Builtin.NativeObject>, %2 : $@async @callee_guaranteed () -> (@error Error)) : $(Builtin.NativeObject, Builtin.RawPointer)
43+
end_borrow %3 : $Optional<Builtin.NativeObject>
44+
destroy_value %20 : $(Builtin.NativeObject, Builtin.RawPointer)
45+
%21 = tuple ()
46+
return %21 : $()
3047
}

test/SILGen/async_builtins.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency -parse-stdlib -sil-verify-all | %FileCheck %s
22
// REQUIRES: concurrency
33

4-
struct X {
4+
import Swift
5+
6+
public struct X {
57
// CHECK-LABEL: sil hidden [ossa] @$s4test1XV14getCurrentTaskBoyYF
68
func getCurrentTask() async -> Builtin.NativeObject {
79
// CHECK: builtin "getCurrentAsyncTask"() : $Builtin.NativeObject
@@ -13,4 +15,14 @@ struct X {
1315
// CHECK: builtin "cancelAsyncTask"(%0 : $Builtin.NativeObject) : $()
1416
Builtin.cancelAsyncTask(task)
1517
}
18+
19+
// CHECK-LABEL: sil hidden [ossa] @$s4test1XV10launchTaskyyYF : $@convention(method) @async (X) -> ()
20+
func launchTask() async {
21+
// CHECK: builtin "createAsyncTask"([[FLAGS:%.*]] : $Int, [[PARENT:%.*]] : $Optional<Builtin.NativeObject>, [[FN:%.*]] : $@async @callee_guaranteed () -> @error Error) : $(Builtin.NativeObject, Builtin.RawPointer)
22+
let task = Builtin.getCurrentAsyncTask()
23+
let childTask = Builtin.createAsyncTask(0, task) {
24+
await launchTask()
25+
print("child is done")
26+
}
27+
}
1628
}

0 commit comments

Comments
 (0)