Skip to content

Commit 565198e

Browse files
committed
Default actors carry a null witness-table pointer in Builtin.Executor.
Previously, they were storing a low-bit flag that indicated that they were a default actor. Using an extra inhabitant frees up the low bit for future use without being conspicuously more expensive to check.
1 parent ec5215b commit 565198e

File tree

4 files changed

+70
-32
lines changed

4 files changed

+70
-32
lines changed

include/swift/ABI/Executor.h

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,36 @@ class DefaultActor;
2929
class Job;
3030
class SerialExecutorWitnessTable;
3131

32-
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
33-
SWIFT_EXPORT_FROM(swift_Concurrency)
34-
Metadata* MainActorMetadata;
35-
3632
/// An unmanaged reference to an executor.
3733
///
38-
/// The representation is two words: identity and implementation.
39-
/// The identity word is a reference to the executor object; for
40-
/// default actors, this is the actor object. The implementation
41-
/// word describes how the executor works; it carries a witness table
42-
/// as well as a small number of bits indicating various special
43-
/// implementation properties. As an exception to both of these
44-
/// rules, a null identity represents a generic executor and
45-
/// implies a null implementation word.
34+
/// This type corresponds to the type Optional<Builtin.Executor> in
35+
/// Swift. The representation of nil in Optional<Builtin.Executor>
36+
/// aligns with what this type calls the generic executor, so the
37+
/// notional subtype of this type which is never generic corresponds
38+
/// to the type Builtin.Executor.
39+
///
40+
/// An executor reference is divided into two pieces:
41+
///
42+
/// - The identity, which is just a (potentially ObjC) object
43+
/// reference; when this is null, the reference is generic.
44+
/// Equality of executor references is based solely on equality
45+
/// of identity.
46+
///
47+
/// - The implementation, which is an optional reference to a
48+
/// witness table for the SerialExecutor protocol. When this
49+
/// is null, but the identity is non-null, the reference is to
50+
/// a default actor. The low bits of the implementation pointer
51+
/// are reserved for the use of marking interesting properties
52+
/// about the executor's implementation. The runtime masks these
53+
/// bits off before accessing the witness table, so setting them
54+
/// in the future should back-deploy as long as the witness table
55+
/// reference is still present.
4656
class ExecutorRef {
4757
HeapObject *Identity; // Not necessarily Swift reference-countable
4858
uintptr_t Implementation;
4959

60+
// We future-proof the ABI here by masking the low bits off the
61+
// implementation pointer before using it as a witness table.
5062
enum: uintptr_t {
5163
WitnessTableMask = ~uintptr_t(alignof(void*) - 1)
5264
};
@@ -67,7 +79,7 @@ class ExecutorRef {
6779
/// for it.
6880
static ExecutorRef forDefaultActor(DefaultActor *actor) {
6981
assert(actor);
70-
return ExecutorRef(actor, unsigned(ExecutorRefFlags::DefaultActor));
82+
return ExecutorRef(actor, 0);
7183
}
7284

7385
HeapObject *getIdentity() const {
@@ -81,7 +93,7 @@ class ExecutorRef {
8193

8294
/// Is this a default-actor executor reference?
8395
bool isDefaultActor() const {
84-
return Implementation & unsigned(ExecutorRefFlags::DefaultActor);
96+
return !isGeneric() && Implementation == 0;
8597
}
8698
DefaultActor *getDefaultActor() const {
8799
assert(isDefaultActor());

include/swift/ABI/MetadataValues.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,17 +2187,6 @@ enum class ContinuationStatus : size_t {
21872187
Resumed = 2
21882188
};
21892189

2190-
/// Flags describing the executor implementation that are stored
2191-
/// in the ExecutorRef.
2192-
enum class ExecutorRefFlags : size_t {
2193-
// The number of bits available here is very limited because it's
2194-
// potentially just the alignment bits of a protocol witness table
2195-
// pointer
2196-
2197-
/// The executor is a default actor.
2198-
DefaultActor = 0x1
2199-
};
2200-
22012190
} // end namespace swift
22022191

22032192
#endif // SWIFT_ABI_METADATAVALUES_H

lib/IRGen/GenConcurrency.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,10 @@ void irgen::emitBuildMainActorExecutorRef(IRGenFunction &IGF,
133133
void irgen::emitBuildDefaultActorExecutorRef(IRGenFunction &IGF,
134134
llvm::Value *actor,
135135
Explosion &out) {
136-
unsigned flags = unsigned(ExecutorRefFlags::DefaultActor);
137-
136+
// The implementation word of a default actor is just a null pointer.
138137
llvm::Value *identity =
139138
IGF.Builder.CreatePtrToInt(actor, IGF.IGM.ExecutorFirstTy);
140-
llvm::Value *impl =
141-
llvm::ConstantInt::get(IGF.IGM.ExecutorSecondTy, flags);
139+
llvm::Value *impl = llvm::ConstantInt::get(IGF.IGM.ExecutorSecondTy, 0);
142140

143141
out.add(identity);
144142
out.add(impl);
@@ -149,6 +147,8 @@ void irgen::emitBuildOrdinarySerialExecutorRef(IRGenFunction &IGF,
149147
CanType executorType,
150148
ProtocolConformanceRef executorConf,
151149
Explosion &out) {
150+
// The implementation word of an "ordinary" serial executor is
151+
// just the witness table pointer with no flags set.
152152
llvm::Value *identity =
153153
IGF.Builder.CreatePtrToInt(executor, IGF.IGM.ExecutorFirstTy);
154154
llvm::Value *impl =

test/IRGen/async/builtin_executor.sil

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,19 @@ import Builtin
88
import Swift
99
import _Concurrency
1010

11+
final actor MyDefaultActor {
12+
@_semantics("defaultActor")
13+
nonisolated var unownedExecutor: UnownedSerialExecutor { get }
14+
}
15+
sil_vtable MyDefaultActor {}
16+
17+
actor MyCustomActor : SerialExecutor {
18+
final nonisolated var unownedExecutor: UnownedSerialExecutor { get }
19+
final nonisolated func enqueue(_ job: UnownedJob)
20+
final nonisolated func asUnownedSerialExecutor() -> UnownedSerialExecutor
21+
}
22+
sil_vtable MyCustomActor {}
23+
1124
// CHECK: define{{.*}} swiftcc { [[INT]], [[INT]] } @test_none()
1225
sil @test_none : $() -> Optional<Builtin.Executor> {
1326
bb0:
@@ -19,9 +32,33 @@ bb0:
1932
// CHECK: define{{.*}} swiftcc { [[INT]], [[INT]] } @test_some([[INT]] %0, [[INT]] %1)
2033
sil @test_some : $(Builtin.Executor) -> Optional<Builtin.Executor> {
2134
bb0(%0 : $Builtin.Executor):
22-
// CHECK: [[T0:%.*]] = insertvalue { [[INT]], [[INT]] } undef, [[INT]] %0, 0
23-
// CHECK-NEXT: [[T1:%.*]] = insertvalue { [[INT]], [[INT]] } [[T0]], [[INT]] %1, 1
24-
// CHECK-NEXT: ret { [[INT]], [[INT]] } [[T1]]
35+
// CHECK: [[ONE:%.*]] = insertvalue { [[INT]], [[INT]] } undef, [[INT]] %0, 0
36+
// CHECK-NEXT: [[TWO:%.*]] = insertvalue { [[INT]], [[INT]] } [[ONE]], [[INT]] %1, 1
37+
// CHECK-NEXT: ret { [[INT]], [[INT]] } [[TWO]]
2538
%1 = enum $Optional<Builtin.Executor>, #Optional.some, %0 : $Builtin.Executor
2639
return %1 : $Optional<Builtin.Executor>
2740
}
41+
42+
// CHECK: define{{.*}} swiftcc { [[INT]], [[INT]] } @test_build_default_actor(%T4test14MyDefaultActorC* %0)
43+
sil @test_build_default_actor : $(@guaranteed MyDefaultActor) -> Builtin.Executor {
44+
bb0(%0 : $MyDefaultActor):
45+
// CHECK: [[T0:%.*]] = ptrtoint %T4test14MyDefaultActorC* %0 to [[INT]]
46+
// CHECK-NEXT: [[ONE:%.*]] = insertvalue { [[INT]], [[INT]] } undef, [[INT]] [[T0]], 0
47+
// CHECK-NEXT: [[TWO:%.*]] = insertvalue { [[INT]], [[INT]] } [[ONE]], [[INT]] 0, 1
48+
// CHECK-NEXT: ret { [[INT]], [[INT]] } [[TWO]]
49+
%1 = builtin "buildDefaultActorExecutorRef"<MyDefaultActor>(%0 : $MyDefaultActor) : $Builtin.Executor
50+
return %1 : $Builtin.Executor
51+
}
52+
53+
// CHECK: define{{.*}} swiftcc { [[INT]], [[INT]] } @test_build_custom_actor(%T4test13MyCustomActorC* %0)
54+
sil @test_build_custom_actor : $(@guaranteed MyCustomActor) -> Builtin.Executor {
55+
bb0(%0 : $MyCustomActor):
56+
// CHECK: [[T0:%.*]] = ptrtoint %T4test13MyCustomActorC* %0 to [[INT]]
57+
// CHECK-NEXT: [[T1:%.*]] = call i8** @"$s4test13MyCustomActorCACs14SerialExecutorAAWl"()
58+
// CHECK-NEXT: [[T2:%.*]] = ptrtoint i8** [[T1]] to [[INT]]
59+
// CHECK: [[ONE:%.*]] = insertvalue { [[INT]], [[INT]] } undef, [[INT]] [[T0]], 0
60+
// CHECK-NEXT: [[TWO:%.*]] = insertvalue { [[INT]], [[INT]] } [[ONE]], [[INT]] [[T2]], 1
61+
// CHECK-NEXT: ret { [[INT]], [[INT]] } [[TWO]]
62+
%1 = builtin "buildOrdinarySerialExecutorRef"<MyCustomActor>(%0 : $MyCustomActor) : $Builtin.Executor
63+
return %1 : $Builtin.Executor
64+
}

0 commit comments

Comments
 (0)