Skip to content

Commit 111b201

Browse files
authored
Merge pull request #34595 from DougGregor/get-current-async-task-builtin
[Concurrency] Add a builtin to get the current task in an async function
2 parents 7b00d52 + 81f5528 commit 111b201

File tree

12 files changed

+121
-1
lines changed

12 files changed

+121
-1
lines changed

include/swift/AST/Builtins.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,12 @@ BUILTIN_MISC_OPERATION(IntInstrprofIncrement, "int_instrprof_increment", "", Spe
717717
BUILTIN_MISC_OPERATION(Id, Name, Attrs, Overload)
718718
#endif
719719

720+
// getCurrentAsyncTask: () -> Builtin.NativeObject
721+
//
722+
// Retrieve the pointer to the task in which the current asynchronous
723+
// function is executing.
724+
BUILTIN_MISC_OPERATION_WITH_SILGEN(GetCurrentAsyncTask, "getCurrentAsyncTask", "n", Special)
725+
720726
/// globalStringTablePointer has type String -> Builtin.RawPointer.
721727
/// It returns an immortal, global string table pointer for strings constructed
722728
/// from string literals. We consider it effects as readnone meaning that it

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/OperandOwnership.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,14 @@ CONSTANT_OWNERSHIP_BUILTIN(Owned, MustBeInvalidated, COWBufferForReading)
10351035
CONSTANT_OWNERSHIP_BUILTIN(Owned, MustBeInvalidated, UnsafeGuaranteed)
10361036
#undef CONSTANT_OWNERSHIP_BUILTIN
10371037

1038+
#define SHOULD_NEVER_VISIT_BUILTIN(ID) \
1039+
OperandOwnershipKindMap OperandOwnershipKindBuiltinClassifier::visit##ID( \
1040+
BuiltinInst *, StringRef) { \
1041+
llvm_unreachable("Builtin should never be visited! E.x.: It may not have arguments"); \
1042+
}
1043+
SHOULD_NEVER_VISIT_BUILTIN(GetCurrentAsyncTask)
1044+
#undef SHOULD_NEVER_VISIT_BUILTIN
1045+
10381046
// Builtins that should be lowered to SIL instructions so we should never see
10391047
// them.
10401048
#define BUILTIN_SIL_OPERATION(ID, NAME, CATEGORY) \

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ CONSTANT_OWNERSHIP_BUILTIN(None, PoundAssert)
541541
CONSTANT_OWNERSHIP_BUILTIN(None, TypePtrAuthDiscriminator)
542542
CONSTANT_OWNERSHIP_BUILTIN(None, IntInstrprofIncrement)
543543
CONSTANT_OWNERSHIP_BUILTIN(None, GlobalStringTablePointer)
544+
CONSTANT_OWNERSHIP_BUILTIN(Owned, GetCurrentAsyncTask)
544545

545546
#undef CONSTANT_OWNERSHIP_BUILTIN
546547

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 SGF.emitManagedRValueWithEndLifetimeCleanup(apply);
1406+
}
1407+
13961408
Optional<SpecializedEmitter>
13971409
SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) {
13981410
// Only consider standalone declarations in the Builtin module.

lib/SILGen/SILGenDecl.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,33 @@ CleanupHandle SILGenFunction::enterDestroyCleanup(SILValue valueOrAddr) {
13261326
return Cleanups.getTopCleanup();
13271327
}
13281328

1329+
namespace {
1330+
class EndLifetimeCleanup : public Cleanup {
1331+
SILValue v;
1332+
public:
1333+
EndLifetimeCleanup(SILValue v) : v(v) {}
1334+
1335+
void emit(SILGenFunction &SGF, CleanupLocation l,
1336+
ForUnwind_t forUnwind) override {
1337+
SGF.B.createEndLifetime(l, v);
1338+
}
1339+
1340+
void dump(SILGenFunction &) const override {
1341+
#ifndef NDEBUG
1342+
llvm::errs() << "EndLifetimeCleanup\n"
1343+
<< "State:" << getState() << "\n"
1344+
<< "Value:" << v << "\n";
1345+
#endif
1346+
}
1347+
};
1348+
} // end anonymous namespace
1349+
1350+
ManagedValue SILGenFunction::emitManagedRValueWithEndLifetimeCleanup(
1351+
SILValue value) {
1352+
Cleanups.pushCleanup<EndLifetimeCleanup>(value);
1353+
return ManagedValue::forUnmanaged(value);
1354+
}
1355+
13291356
namespace {
13301357
/// A cleanup that deinitializes an opaque existential container
13311358
/// before a value has been stored into it, or after its value was taken.

lib/SILGen/SILGenFunction.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2000,7 +2000,21 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
20002000

20012001
/// Enter a cleanup to emit a ReleaseValue/DestroyAddr of the specified value.
20022002
CleanupHandle enterDestroyCleanup(SILValue valueOrAddr);
2003-
2003+
2004+
/// Return an owned managed value for \p value that is cleaned up using an end_lifetime instruction.
2005+
///
2006+
/// The end_lifetime cleanup is not placed into the ManagedValue itself and
2007+
/// thus can not be forwarded. This means that the ManagedValue is treated
2008+
/// as a +0 value. This means that the owned value will be copied by SILGen
2009+
/// if it is ever needed as a +1 value (meaning any time that the value
2010+
/// escapes).
2011+
///
2012+
/// DISCUSSION: end_lifetime ends the lifetime of an owned value in OSSA
2013+
/// without resulting in a destroy being emitted. This cleanup should only
2014+
/// be used for owned values that do not need to be destroyed if they do not
2015+
/// escape the current call frame but need to be copied if they escape.
2016+
ManagedValue emitManagedRValueWithEndLifetimeCleanup(SILValue value);
2017+
20042018
/// Enter a cleanup to emit a DeinitExistentialAddr or DeinitExistentialBox
20052019
/// of the specified value.
20062020
CleanupHandle enterDeinitExistentialCleanup(CleanupState state,

lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ static bool isBarrier(SILInstruction *inst) {
140140
case BuiltinValueKind::GlobalStringTablePointer:
141141
case BuiltinValueKind::COWBufferForReading:
142142
case BuiltinValueKind::IntInstrprofIncrement:
143+
case BuiltinValueKind::GetCurrentAsyncTask:
143144
return false;
144145

145146
// Handle some rare builtins that may be sensitive to object lifetime

test/IRGen/async/builtins.sil

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
end_lifetime %0 : $Builtin.NativeObject
18+
return %1 : $Builtin.NativeObject
19+
}

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)