Skip to content

Commit 9566d2e

Browse files
committed
[Concurrency] Add a builtin to get the current task in an async function.
This introduces a new builtin, `getCurrentAsyncTask()`, that produces a reference to the current task. This builtin can only be used within `async` functions, and IR generation merely grabs the task argument and packages it up. The type of this function is `() -> Builtin.NativeObject`, because we don't currently have a Swift-level representation of tasks, and can probably handle everything through builtins or runtime calls.
1 parent 91b969a commit 9566d2e

File tree

8 files changed

+74
-0
lines changed

8 files changed

+74
-0
lines changed

include/swift/AST/Builtins.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,12 @@ BUILTIN_SIL_OPERATION(ConvertStrongToUnownedUnsafe, "convertStrongToUnownedUnsaf
492492
/// now.
493493
BUILTIN_SIL_OPERATION(ConvertUnownedUnsafeToGuaranteed, "convertUnownedUnsafeToGuaranteed", Special)
494494

495+
// getCurrentAsyncTask: () -> Builtin.NativeObject
496+
//
497+
// Retrieve the pointer to the task in which the current asynchronous
498+
// function is executing.
499+
BUILTIN_SIL_OPERATION(GetCurrentAsyncTask, "getCurrentAsyncTask", Special)
500+
495501
/// applyDerivative
496502
BUILTIN_SIL_OPERATION(ApplyDerivative, "applyDerivative", Special)
497503

lib/AST/Builtins.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,6 +1341,10 @@ static ValueDecl *getConvertUnownedUnsafeToGuaranteed(ASTContext &ctx,
13411341
return builder.build(id);
13421342
}
13431343

1344+
static ValueDecl *getGetCurrentAsyncTask(ASTContext &ctx, Identifier id) {
1345+
return getBuiltinFunction(id, { }, ctx.TheNativeObjectType);
1346+
}
1347+
13441348
static ValueDecl *getPoundAssert(ASTContext &Context, Identifier Id) {
13451349
auto int1Type = BuiltinIntegerType::get(1, Context);
13461350
auto optionalRawPointerType = BoundGenericEnumType::get(
@@ -2467,6 +2471,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
24672471
case BuiltinValueKind::ConvertUnownedUnsafeToGuaranteed:
24682472
return getConvertUnownedUnsafeToGuaranteed(Context, Id);
24692473

2474+
case BuiltinValueKind::GetCurrentAsyncTask:
2475+
return getGetCurrentAsyncTask(Context, Id);
2476+
24702477
case BuiltinValueKind::PoundAssert:
24712478
return getPoundAssert(Context, Id);
24722479

lib/IRGen/GenBuiltin.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,13 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
211211
return;
212212
}
213213

214+
// getCurrentAsyncTask has no arguments.
215+
if (Builtin.ID == BuiltinValueKind::GetCurrentAsyncTask) {
216+
auto task = IGF.getAsyncTask();
217+
out.add(IGF.Builder.CreateBitCast(task, IGF.IGM.RefCountedPtrTy));
218+
return;
219+
}
220+
214221
// Everything else cares about the (rvalue) argument.
215222

216223
// If this is an LLVM IR intrinsic, lower it to an intrinsic call.

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,12 @@ UNOWNED_OR_NONE_DEPENDING_ON_RESULT(ZeroInitializer)
570570

571571
ValueOwnershipKind
572572
ValueOwnershipKindClassifier::visitBuiltinInst(BuiltinInst *BI) {
573+
if (auto kind = BI->getBuiltinKind()) {
574+
// The current async task is kept alive by the caller.
575+
if (*kind == BuiltinValueKind::GetCurrentAsyncTask)
576+
return ValueOwnershipKind::Unowned;
577+
}
578+
573579
// For now, just conservatively say builtins are None. We need to use a
574580
// builtin in here to guarantee correctness.
575581
return ValueOwnershipKindBuiltinVisitor().visit(BI);

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,6 +1736,14 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
17361736
verifyLLVMIntrinsic(BI, BI->getIntrinsicInfo().ID);
17371737
return;
17381738
}
1739+
1740+
// Check that 'getCurrentAsyncTask' only occurs within an async function.
1741+
if (BI->getBuiltinKind() &&
1742+
*BI->getBuiltinKind() == BuiltinValueKind::GetCurrentAsyncTask) {
1743+
require(F.isAsync(),
1744+
"getCurrentAsyncTask builtin can only be used in an async function");
1745+
return;
1746+
}
17391747
}
17401748

17411749
void checkFunctionRefBaseInst(FunctionRefBaseInst *FRI) {

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,18 @@ static ManagedValue emitBuiltinConvertUnownedUnsafeToGuaranteed(
13931393
return SGF.B.createMarkDependence(loc, guaranteedNonTrivialRefMV, baseMV);
13941394
}
13951395

1396+
// Emit SIL for the named builtin: getCurrentAsyncTask.
1397+
static ManagedValue emitBuiltinGetCurrentAsyncTask(
1398+
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
1399+
PreparedArguments &&preparedArgs, SGFContext C) {
1400+
ASTContext &ctx = SGF.getASTContext();
1401+
auto apply = SGF.B.createBuiltin(
1402+
loc,
1403+
ctx.getIdentifier(getBuiltinName(BuiltinValueKind::GetCurrentAsyncTask)),
1404+
SGF.getLoweredType(ctx.TheNativeObjectType), SubstitutionMap(), { });
1405+
return ManagedValue::forUnmanaged(apply);
1406+
}
1407+
13961408
Optional<SpecializedEmitter>
13971409
SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) {
13981410
// Only consider standalone declarations in the Builtin module.

test/IRGen/async/builtins.sil

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %target-swift-frontend -enable-experimental-concurrency -enable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-objc
2+
// RUN: %target-swift-frontend -enable-experimental-concurrency -disable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-native
3+
4+
// REQUIRES: concurrency
5+
6+
sil_stage canonical
7+
8+
import Builtin
9+
10+
// CHECK-LABEL: define hidden swiftcc void @get_task(%swift.task* %0, %swift.executor* %1, %swift.context* %2)
11+
sil hidden [ossa] @get_task : $@async @convention(thin) () -> @owned Builtin.NativeObject {
12+
bb0:
13+
// CHECK: [[TASK:%.*]] = bitcast %swift.task* %0 to %swift.refcounted*
14+
%0 = builtin "getCurrentAsyncTask"() : $Builtin.NativeObject
15+
// CHECK-NEXT: [[TASK_COPY:%.*]] = call %swift.refcounted* @swift_retain(%swift.refcounted* returned [[TASK]])
16+
%1 = copy_value %0 : $Builtin.NativeObject
17+
return %1 : $Builtin.NativeObject
18+
}

test/SILGen/async_builtins.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency -parse-stdlib | %FileCheck %s
2+
// REQUIRES: concurrency
3+
4+
struct X {
5+
// CHECK-LABEL: sil hidden [ossa] @$s4test1XV14getCurrentTaskBoyYF
6+
func getCurrentTask() async -> Builtin.NativeObject {
7+
// CHECK: builtin "getCurrentAsyncTask"() : $Builtin.NativeObject
8+
return Builtin.getCurrentAsyncTask()
9+
}
10+
}

0 commit comments

Comments
 (0)