Skip to content

Commit f1c2334

Browse files
committed
[Concurrency] Enable TaskGroup/DiscardingTaskGroup in Embedded Swift
1 parent f787e8f commit f1c2334

File tree

11 files changed

+249
-33
lines changed

11 files changed

+249
-33
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2792,6 +2792,10 @@ class TaskGroupFlags : public FlagSet<uint32_t> {
27922792
/// Request the TaskGroup to immediately release completed tasks,
27932793
/// and not store their results. This also effectively disables `next()`.
27942794
TaskGroup_DiscardResults = 8,
2795+
/// The `Metadata *T` in swift_taskGroup_initialize and in
2796+
/// swift_taskGroup_initializeWithFlags pointer is actually a
2797+
/// TaskOptionRecord pointer. Used only in Embedded Swift.
2798+
TaskGroup_MetadataAsOptionRecord = 9,
27952799
};
27962800

27972801
explicit TaskGroupFlags(uint32_t bits) : FlagSet(bits) {}
@@ -2800,6 +2804,10 @@ class TaskGroupFlags : public FlagSet<uint32_t> {
28002804
FLAGSET_DEFINE_FLAG_ACCESSORS(TaskGroup_DiscardResults,
28012805
isDiscardResults,
28022806
setIsDiscardResults)
2807+
2808+
FLAGSET_DEFINE_FLAG_ACCESSORS(TaskGroup_MetadataAsOptionRecord,
2809+
isMetadataAsOptionRecord,
2810+
setIsMetadataAsOptionRecord)
28032811
};
28042812

28052813
/// Flags for cancellation records.

include/swift/AST/Builtins.def

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -800,18 +800,6 @@ BUILTIN_MISC_OPERATION(ResumeThrowingContinuationReturning,
800800
BUILTIN_MISC_OPERATION(ResumeThrowingContinuationThrowing,
801801
"resumeThrowingContinuationThrowing", "", Special)
802802

803-
/// Create a task group.
804-
BUILTIN_MISC_OPERATION(CreateTaskGroup,
805-
"createTaskGroup", "", Special)
806-
807-
/// Create a task group, with options.
808-
BUILTIN_MISC_OPERATION(CreateTaskGroupWithFlags,
809-
"createTaskGroupWithFlags", "", Special)
810-
811-
/// Destroy a task group.
812-
BUILTIN_MISC_OPERATION(DestroyTaskGroup,
813-
"destroyTaskGroup", "", Special)
814-
815803
/// Unchecked pointer alignment assertion. Allows the compiler to assume
816804
/// alignment of the pointer to emit more efficient code.
817805
///
@@ -1054,6 +1042,15 @@ BUILTIN_SIL_OPERATION(CreateAsyncDiscardingTaskInGroupWithExecutor,
10541042
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildOrdinaryTaskExecutorRef,
10551043
"buildOrdinaryTaskExecutorRef", "n", Special)
10561044

1045+
/// Create a task group.
1046+
BUILTIN_MISC_OPERATION_WITH_SILGEN(CreateTaskGroup, "createTaskGroup", "", Special)
1047+
1048+
/// Create a task group, with options.
1049+
BUILTIN_MISC_OPERATION_WITH_SILGEN(CreateTaskGroupWithFlags, "createTaskGroupWithFlags", "", Special)
1050+
1051+
/// Destroy a task group.
1052+
BUILTIN_MISC_OPERATION(DestroyTaskGroup, "destroyTaskGroup", "", Special)
1053+
10571054
/// globalStringTablePointer has type String -> Builtin.RawPointer.
10581055
/// It returns an immortal, global string table pointer for strings constructed
10591056
/// from string literals. We consider it effects as readnone meaning that it

lib/IRGen/GenBuiltin.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -295,20 +295,14 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
295295

296296
if (Builtin.ID == BuiltinValueKind::CreateTaskGroup) {
297297
llvm::Value *groupFlags = nullptr;
298-
// Claim metadata pointer.
299-
(void)args.claimAll();
298+
assert(args.size() == 0);
300299
out.add(emitCreateTaskGroup(IGF, substitutions, groupFlags));
301300
return;
302301
}
303302

304303
if (Builtin.ID == BuiltinValueKind::CreateTaskGroupWithFlags) {
305304
auto groupFlags = args.claimNext();
306-
// Claim the remaining metadata pointer.
307-
if (args.size() == 1) {
308-
(void)args.claimNext();
309-
} else if (args.size() > 1) {
310-
llvm_unreachable("createTaskGroupWithFlags expects 1 or 2 arguments");
311-
}
305+
assert(args.size() == 0);
312306
out.add(emitCreateTaskGroup(IGF, substitutions, groupFlags));
313307
return;
314308
}

lib/IRGen/GenConcurrency.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,39 @@ llvm::Value *irgen::emitCreateTaskGroup(IRGenFunction &IGF,
335335
assert(subs.getReplacementTypes().size() == 1 &&
336336
"createTaskGroup should have a type substitution");
337337
auto resultType = subs.getReplacementTypes()[0]->getCanonicalType();
338-
auto resultTypeMetadata = IGF.emitAbstractTypeMetadataRef(resultType);
338+
339+
// In desktop Swift, we pass a Metadata pointer as the last argument. In
340+
// Embedded Swift, we pass a TaskOptionRecord list, and mark a bit in
341+
// groupFlags.
342+
llvm::Value *metadataOrTypeOptionRecord;
343+
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
344+
metadataOrTypeOptionRecord =
345+
llvm::ConstantPointerNull::get(IGF.IGM.Int8PtrTy);
346+
metadataOrTypeOptionRecord = maybeAddEmbeddedSwiftResultTypeInfo(
347+
IGF, metadataOrTypeOptionRecord, resultType);
348+
llvm::IntegerType *IntPtrTy = IGF.IGM.IntPtrTy;
349+
TaskGroupFlags taskgroupFlags(0);
350+
taskgroupFlags.setIsMetadataAsOptionRecord(true);
351+
auto flagValue = llvm::ConstantInt::get(IGF.IGM.IntPtrTy,
352+
taskgroupFlags.getOpaqueValue());
353+
if (!groupFlags) {
354+
groupFlags = flagValue;
355+
} else {
356+
groupFlags = IGF.Builder.CreateOr(groupFlags, flagValue);
357+
}
358+
} else {
359+
metadataOrTypeOptionRecord = IGF.emitAbstractTypeMetadataRef(resultType);
360+
}
339361

340362
llvm::CallInst *call;
341363
if (groupFlags) {
342-
call = IGF.Builder.CreateCall(IGF.IGM.getTaskGroupInitializeWithFlagsFunctionPointer(),
343-
{groupFlags, group, resultTypeMetadata});
364+
call = IGF.Builder.CreateCall(
365+
IGF.IGM.getTaskGroupInitializeWithFlagsFunctionPointer(),
366+
{groupFlags, group, metadataOrTypeOptionRecord});
344367
} else {
345-
call = IGF.Builder.CreateCall(IGF.IGM.getTaskGroupInitializeFunctionPointer(),
346-
{group, resultTypeMetadata});
368+
call =
369+
IGF.Builder.CreateCall(IGF.IGM.getTaskGroupInitializeFunctionPointer(),
370+
{group, metadataOrTypeOptionRecord});
347371
}
348372
call->setDoesNotThrow();
349373
call->setCallingConv(IGF.IGM.SwiftCC);

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,6 +1750,38 @@ static ManagedValue emitBuiltinCreateDiscardingTask(
17501750
CreateTaskOptions::Discarding });
17511751
}
17521752

1753+
// Emit SIL for the named builtin: createTaskGroup.
1754+
// These formally take a metatype argument that's never actually used, so
1755+
// we ignore it.
1756+
static ManagedValue emitBuiltinCreateTaskGroup(SILGenFunction &SGF,
1757+
SILLocation loc,
1758+
SubstitutionMap subs,
1759+
ArrayRef<ManagedValue> args,
1760+
SGFContext C) {
1761+
auto &ctx = SGF.getASTContext();
1762+
auto resultType = SILType::getRawPointerType(ctx);
1763+
auto value = SGF.B.createBuiltin(
1764+
loc, ctx.getIdentifier(getBuiltinName(BuiltinValueKind::CreateTaskGroup)),
1765+
resultType, subs, {});
1766+
return ManagedValue::forObjectRValueWithoutOwnership(value);
1767+
}
1768+
1769+
// Emit SIL for the named builtin: createTaskGroupWithFlags.
1770+
// These formally take a metatype argument that's never actually used, so
1771+
// we ignore it.
1772+
static ManagedValue emitBuiltinCreateTaskGroupWithFlags(
1773+
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
1774+
ArrayRef<ManagedValue> args, SGFContext C) {
1775+
auto &ctx = SGF.getASTContext();
1776+
auto resultType = SILType::getRawPointerType(ctx);
1777+
auto value = SGF.B.createBuiltin(
1778+
loc,
1779+
ctx.getIdentifier(
1780+
getBuiltinName(BuiltinValueKind::CreateTaskGroupWithFlags)),
1781+
resultType, subs, {args[0].getValue()});
1782+
return ManagedValue::forObjectRValueWithoutOwnership(value);
1783+
}
1784+
17531785
ManagedValue
17541786
SILGenFunction::emitCreateAsyncMainTask(SILLocation loc, SubstitutionMap subs,
17551787
ManagedValue flags,

stdlib/public/Concurrency/DiscardingTaskGroup.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ import Swift
6767
///
6868
/// - SeeAlso: ``withThrowingDiscardingTaskGroup(returning:body:)``
6969
@available(SwiftStdlib 5.9, *)
70+
#if !$Embedded
7071
@backDeployed(before: SwiftStdlib 6.0)
72+
#endif
7173
@inlinable
7274
public func withDiscardingTaskGroup<GroupResult>(
7375
returning returnType: GroupResult.Type = GroupResult.self,
@@ -609,7 +611,9 @@ extension DiscardingTaskGroup: Sendable { }
609611
/// }
610612
/// ```
611613
@available(SwiftStdlib 5.9, *)
614+
#if !$Embedded
612615
@backDeployed(before: SwiftStdlib 6.0)
616+
#endif
613617
@inlinable
614618
public func withThrowingDiscardingTaskGroup<GroupResult>(
615619
returning returnType: GroupResult.Type = GroupResult.self,

stdlib/public/Concurrency/TaskGroup.cpp

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "swift/ABI/Metadata.h"
2727
#include "swift/ABI/Task.h"
2828
#include "swift/ABI/TaskGroup.h"
29+
#include "swift/ABI/TaskOptions.h"
2930
#include "swift/Basic/RelativePointer.h"
3031
#include "swift/Basic/STLExtras.h"
3132
#include "swift/Runtime/Concurrency.h"
@@ -964,16 +965,37 @@ static void swift_taskGroup_initializeImpl(TaskGroup *group, const Metadata *T)
964965
SWIFT_CC(swift)
965966
static void swift_taskGroup_initializeWithFlagsImpl(size_t rawGroupFlags,
966967
TaskGroup *group, const Metadata *T) {
967-
968-
#if !SWIFT_CONCURRENCY_EMBEDDED
969-
ResultTypeInfo resultType;
970-
resultType.metadata = T;
971-
972968
TaskGroupFlags groupFlags(rawGroupFlags);
973969
SWIFT_TASK_GROUP_DEBUG_LOG_0(group, "create group, from task:%p; flags: isDiscardingResults=%d",
974970
swift_task_getCurrent(),
975971
groupFlags.isDiscardResults());
976972

973+
ResultTypeInfo resultType;
974+
#if !SWIFT_CONCURRENCY_EMBEDDED
975+
resultType.metadata = T;
976+
assert(!groupFlags.isMetadataAsOptionRecord());
977+
#else
978+
assert(groupFlags.isMetadataAsOptionRecord());
979+
TaskOptionRecord *options = (TaskOptionRecord *)T;
980+
for (auto option = options; option; option = option->getParent()) {
981+
switch (option->getKind()) {
982+
case TaskOptionRecordKind::ResultTypeInfo: {
983+
auto *typeInfo = cast<ResultTypeInfoTaskOptionRecord>(option);
984+
resultType = {
985+
.size = typeInfo->size,
986+
.alignMask = typeInfo->alignMask,
987+
.initializeWithCopy = typeInfo->initializeWithCopy,
988+
.storeEnumTagSinglePayload = typeInfo->storeEnumTagSinglePayload,
989+
.destroy = typeInfo->destroy,
990+
};
991+
break;
992+
}
993+
default:
994+
swift_unreachable("only ResultTypeInfo expected");
995+
}
996+
}
997+
#endif
998+
977999
TaskGroupBase *impl;
9781000
if (groupFlags.isDiscardResults()) {
9791001
impl = ::new(group) DiscardingTaskGroup(resultType);
@@ -993,9 +1015,6 @@ static void swift_taskGroup_initializeWithFlagsImpl(size_t rawGroupFlags,
9931015
}
9941016
return true;
9951017
});
996-
#else
997-
swift_unreachable("task groups not supported yet in embedded Swift");
998-
#endif
9991018
}
10001019

10011020
// =============================================================================

stdlib/public/Concurrency/TaskGroup.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ import Swift
6363
/// For tasks that need to handle cancellation by throwing an error,
6464
/// use the `withThrowingTaskGroup(of:returning:body:)` method instead.
6565
@available(SwiftStdlib 5.1, *)
66+
#if !$Embedded
6667
@backDeployed(before: SwiftStdlib 6.0)
68+
#endif
6769
@inlinable
6870
public func withTaskGroup<ChildTaskResult, GroupResult>(
6971
of childTaskResultType: ChildTaskResult.Type = ChildTaskResult.self,
@@ -198,7 +200,9 @@ public func _unsafeInheritExecutor_withTaskGroup<ChildTaskResult, GroupResult>(
198200
/// which gives you a chance to handle the individual error
199201
/// or to let the group rethrow the error.
200202
@available(SwiftStdlib 5.1, *)
203+
#if !$Embedded
201204
@backDeployed(before: SwiftStdlib 6.0)
205+
#endif
202206
@inlinable
203207
public func withThrowingTaskGroup<ChildTaskResult, GroupResult>(
204208
of childTaskResultType: ChildTaskResult.Type = ChildTaskResult.self,
@@ -592,7 +596,9 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
592596
///
593597
/// - Returns: The value returned by the next child task that completes.
594598
@available(SwiftStdlib 5.1, *)
599+
#if !$Embedded
595600
@backDeployed(before: SwiftStdlib 6.0)
601+
#endif
596602
public mutating func next(isolation: isolated (any Actor)? = #isolation) async -> ChildTaskResult? {
597603
// try!-safe because this function only exists for Failure == Never,
598604
// and as such, it is impossible to spawn a throwing child task.
@@ -610,7 +616,9 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
610616
/// Await all of the pending tasks added this group.
611617
@usableFromInline
612618
@available(SwiftStdlib 5.1, *)
619+
#if !$Embedded
613620
@backDeployed(before: SwiftStdlib 6.0)
621+
#endif
614622
internal mutating func awaitAllRemainingTasks(isolation: isolated (any Actor)? = #isolation) async {
615623
while let _ = await next(isolation: isolation) {}
616624
}
@@ -750,7 +758,9 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {
750758
/// Await all the remaining tasks on this group.
751759
@usableFromInline
752760
@available(SwiftStdlib 5.1, *)
761+
#if !$Embedded
753762
@backDeployed(before: SwiftStdlib 6.0)
763+
#endif
754764
internal mutating func awaitAllRemainingTasks(isolation: isolated (any Actor)? = #isolation) async {
755765
while true {
756766
do {
@@ -1028,7 +1038,9 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {
10281038
///
10291039
/// - SeeAlso: `nextResult()`
10301040
@available(SwiftStdlib 5.1, *)
1041+
#if !$Embedded
10311042
@backDeployed(before: SwiftStdlib 6.0)
1043+
#endif
10321044
public mutating func next(isolation: isolated (any Actor)? = #isolation) async throws -> ChildTaskResult? {
10331045
return try await _taskGroupWaitNext(group: _group)
10341046
}

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,12 @@ class TaskFutureWaitAsyncContext : public AsyncContext {
322322
fillWithError(future->getError());
323323
}
324324
void fillWithError(SwiftError *error) {
325+
#if SWIFT_CONCURRENCY_EMBEDDED
326+
swift_unreachable("untyped error used in embedded Swift");
327+
#else
325328
errorResult = error;
326329
swift_errorRetain(error);
330+
#endif
327331
}
328332
};
329333

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -enable-experimental-feature Embedded -target %target-cpu-apple-macos14 -parse-as-library %s -c -o %t/a.o
3+
// RUN: %target-clang -x c -c %S/Inputs/print.c -o %t/print.o
4+
// RUN: %target-clang %t/a.o %t/print.o -o %t/a.out %swift_obj_root/lib/swift/embedded/%target-cpu-apple-macos/libswift_Concurrency.a -dead_strip
5+
// RUN: %target-run %t/a.out | %FileCheck %s
6+
7+
// REQUIRES: executable_test
8+
// REQUIRES: swift_in_compiler
9+
// REQUIRES: optimized_stdlib
10+
// REQUIRES: OS=macosx
11+
12+
import _Concurrency
13+
14+
protocol Go: Actor {
15+
var name: String { get }
16+
func go(times: Int) async -> Int
17+
}
18+
19+
extension Go {
20+
func go(times: Int) async -> Int {
21+
for i in 0..<times {
22+
print("\(name) @ \(i)")
23+
await Task.yield()
24+
}
25+
return times
26+
}
27+
}
28+
29+
actor One: Go { var name = "One" }
30+
actor Two: Go { var name = "Two" }
31+
32+
func yielding() async {
33+
let one = One()
34+
let two = Two()
35+
await withTaskGroup(of: Int.self) { group in
36+
group.addTask {
37+
await one.go(times: 5)
38+
}
39+
group.addTask {
40+
await two.go(times: 5)
41+
}
42+
}
43+
}
44+
45+
@main struct Main {
46+
static func main() async {
47+
await yielding()
48+
print("All done!")
49+
// CHECK: One @ 0
50+
// CHECK: Two @ 0
51+
// CHECK: One @ 1
52+
// CHECK: Two @ 1
53+
// CHECK: One @ 2
54+
// CHECK: Two @ 2
55+
// CHECK: One @ 3
56+
// CHECK: Two @ 3
57+
// CHECK: One @ 4
58+
// CHECK: Two @ 4
59+
// CHECK: All done!
60+
}
61+
}

0 commit comments

Comments
 (0)