11
11
// ===----------------------------------------------------------------------===//
12
12
13
13
#define DEBUG_TYPE " insert-hop-to-executor"
14
+ #include " swift/Basic/FrozenMultiMap.h"
14
15
#include " swift/SIL/SILBuilder.h"
15
16
#include " swift/SIL/SILFunction.h"
16
17
#include " swift/SIL/Dominance.h"
@@ -51,11 +52,18 @@ class LowerHopToActor {
51
52
SILFunction *F;
52
53
DominanceInfo *Dominance;
53
54
54
- // / A map from an actor value to the executor we've derived for it.
55
- llvm::ScopedHashTable<SILValue, SILValue> ExecutorForActor;
55
+ // / A map from an actor value to the dominating instruction that
56
+ // / will derive the executor.
57
+ llvm::ScopedHashTable<SILValue, SILInstruction *>
58
+ ExecutorDerivationForActor;
56
59
57
- bool processHop (HopToExecutorInst *hop);
58
- bool processExtract (ExtractExecutorInst *extract);
60
+ // / A multi-map from a dominating {hop_to_|extract_}executor instruction
61
+ // / to other reachable {hop_to_|extract_}executor instructions.
62
+ SmallFrozenMultiMap<SILInstruction *, SILInstruction *, 4 >
63
+ DominatingActorHops;
64
+
65
+ void recordDominatingInstFor (SILInstruction *inst);
66
+ void rewriteInstructions ();
59
67
60
68
SILValue emitGetExecutor (SILBuilderWithScope &B,
61
69
SILLocation loc,
@@ -73,21 +81,24 @@ class LowerHopToActor {
73
81
};
74
82
75
83
bool LowerHopToActor::run () {
76
- bool changed = false ;
77
-
84
+ // Record all actor operands to hop_to_executor and extract_executor
85
+ // and the dominating instruction that will derive the executor.
78
86
auto runOnBlock = [&](SILBasicBlock *block) {
79
87
for (auto ii = block->begin (), ie = block->end (); ii != ie; ) {
80
88
SILInstruction *inst = &*ii++;
81
- if (auto *hop = dyn_cast<HopToExecutorInst>(inst)) {
82
- changed |= processHop (hop);
83
- } else if (auto *extract = dyn_cast<ExtractExecutorInst>(inst)) {
84
- changed |= processExtract (extract);
85
- }
89
+ recordDominatingInstFor (inst);
86
90
}
87
91
};
88
- runInDominanceOrderWithScopes (Dominance, runOnBlock, ExecutorForActor);
92
+ runInDominanceOrderWithScopes (Dominance, runOnBlock,
93
+ ExecutorDerivationForActor);
89
94
90
- return changed;
95
+ // If we didn't record any dominating actor hops that need
96
+ // transformation, we're done.
97
+ if (DominatingActorHops.empty ())
98
+ return false ;
99
+
100
+ rewriteInstructions ();
101
+ return true ;
91
102
}
92
103
93
104
static bool isOptionalBuiltinExecutor (SILType type) {
@@ -96,49 +107,73 @@ static bool isOptionalBuiltinExecutor(SILType type) {
96
107
return false ;
97
108
}
98
109
99
- // / Search for hop_to_executor instructions with actor-typed operands.
100
- bool LowerHopToActor::processHop (HopToExecutorInst *hop) {
101
- auto actor = hop->getTargetExecutor ();
110
+ void LowerHopToActor::recordDominatingInstFor (SILInstruction *inst) {
111
+ SILValue actor;
112
+ if (auto *hop = dyn_cast<HopToExecutorInst>(inst)) {
113
+ actor = hop->getTargetExecutor ();
114
+ // If hop_to_executor was emitted with an optional executor operand,
115
+ // there's nothing to derive.
116
+ if (isOptionalBuiltinExecutor (actor->getType ())) {
117
+ return ;
118
+ }
119
+ } else if (auto *extract = dyn_cast<ExtractExecutorInst>(inst)) {
120
+ actor = extract->getExpectedExecutor ();
121
+ } else {
122
+ return ;
123
+ }
102
124
103
- // Ignore hops that are already to Optional<Builtin.Executor>.
104
125
if (isOptionalBuiltinExecutor (actor->getType ()))
105
- return false ;
126
+ return ;
106
127
107
- SILBuilderWithScope B (hop);
108
- SILValue executor;
109
- if (actor->getType ().is <BuiltinExecutorType>()) {
110
- // IRGen expects an optional Builtin.Executor, not a Builtin.Executor
111
- // but we can wrap it nicely
112
- executor = B.createOptionalSome (
113
- hop->getLoc (), actor,
114
- SILType::getOptionalType (actor->getType ()));
128
+ auto *dominatingInst = ExecutorDerivationForActor.lookup (actor);
129
+ if (dominatingInst) {
130
+ DominatingActorHops.insert (dominatingInst, inst);
115
131
} else {
116
- // Get the dominating executor value for this actor, if available,
117
- // or else emit code to derive it.
118
- executor = emitGetExecutor (B, hop->getLoc (), actor, /* optional*/ true );
132
+ DominatingActorHops.insert (inst, inst);
133
+ ExecutorDerivationForActor.insert (actor, inst);
119
134
}
120
- assert (executor && " executor not set" );
121
135
122
- B.createHopToExecutor (hop->getLoc (), executor, /* mandatory*/ false );
136
+ return ;
137
+ }
123
138
124
- hop->eraseFromParent ();
139
+ void LowerHopToActor::rewriteInstructions () {
140
+ // Lower the actor operands to executors. Dominating instructions
141
+ // will perform the derivation, and the result will be reused in
142
+ // all reachable instructions.
143
+ DominatingActorHops.setFrozen ();
144
+ for (auto domInst : DominatingActorHops.getRange ()) {
145
+ auto derivationInst = domInst.first ;
146
+
147
+ SILValue actor;
148
+ bool makeOptional;
149
+ if (auto *hop = dyn_cast<HopToExecutorInst>(derivationInst)) {
150
+ actor = hop->getTargetExecutor ();
151
+ makeOptional = true ;
152
+ } else if (auto *extract = dyn_cast<ExtractExecutorInst>(derivationInst)) {
153
+ actor = extract->getExpectedExecutor ();
154
+ makeOptional = false ;
155
+ } else {
156
+ continue ;
157
+ }
125
158
126
- return true ;
127
- }
159
+ // Emit the executor derivation at the dominating instruction.
160
+ SILBuilderWithScope builder (derivationInst);
161
+ auto executor = emitGetExecutor (
162
+ builder, derivationInst->getLoc (), actor, makeOptional);
163
+ derivationInst->setOperand (0 , executor);
164
+
165
+ // Set the executor value as the operand for all reachable instructions.
166
+ auto reachableInsts = domInst.second ;
167
+ for (auto inst : reachableInsts) {
168
+ if (auto *extract = dyn_cast<ExtractExecutorInst>(inst)) {
169
+ extract->replaceAllUsesWith (executor);
170
+ extract->eraseFromParent ();
171
+ continue ;
172
+ }
128
173
129
- bool LowerHopToActor::processExtract (ExtractExecutorInst *extract) {
130
- // Dig out the executor.
131
- auto executor = extract->getExpectedExecutor ();
132
- if (!isOptionalBuiltinExecutor (executor->getType ())) {
133
- SILBuilderWithScope B (extract);
134
- executor =
135
- emitGetExecutor (B, extract->getLoc (), executor, /* optional*/ false );
174
+ inst->setOperand (0 , executor);
175
+ }
136
176
}
137
-
138
- // Unconditionally replace the extract with the executor.
139
- extract->replaceAllUsesWith (executor);
140
- extract->eraseFromParent ();
141
- return true ;
142
177
}
143
178
144
179
static bool isDefaultActorType (CanType actorType, ModuleDecl *M,
@@ -162,40 +197,40 @@ static AccessorDecl *getUnownedExecutorGetter(ASTContext &ctx,
162
197
SILValue LowerHopToActor::emitGetExecutor (SILBuilderWithScope &B,
163
198
SILLocation loc, SILValue actor,
164
199
bool makeOptional) {
165
- // Get the dominating executor value for this actor, if available,
166
- // or else emit code to derive it.
167
- SILValue executor = ExecutorForActor.lookup (actor);
168
- if (executor) {
169
- if (makeOptional)
170
- executor = B.createOptionalSome (loc, executor,
171
- SILType::getOptionalType (executor->getType ()));
172
- return executor;
173
- }
174
-
175
200
// This is okay because actor types have to be classes and so never
176
201
// have multiple abstraction patterns.
177
202
CanType actorType = actor->getType ().getASTType ();
178
203
179
- auto &ctx = F->getASTContext ();
180
- auto resultType = SILType::getPrimitiveObjectType (ctx.TheExecutorType );
181
-
182
- // If the actor type is a default actor, go ahead and devirtualize here.
183
- auto module = F->getModule ().getSwiftModule ();
184
- SILValue unmarkedExecutor;
204
+ // If the operand is already a BuiltinExecutorType, just wrap it
205
+ // in an optional.
206
+ if (makeOptional && actor->getType ().is <BuiltinExecutorType>()) {
207
+ return B.createOptionalSome (
208
+ loc, actor,
209
+ SILType::getOptionalType (actor->getType ()));
210
+ }
185
211
186
- // Determine if the actor is a "default actor" in which case we'll build a default
187
- // actor executor ref inline, rather than calling out to the user-provided executor function.
188
- if (isDefaultActorType (actorType, module , F->getResilienceExpansion ())) {
189
- auto builtinName = ctx.getIdentifier (
190
- getBuiltinName (BuiltinValueKind::BuildDefaultActorExecutorRef));
191
- auto builtinDecl = cast<FuncDecl>(getBuiltinValueDecl (ctx, builtinName));
192
- auto subs = SubstitutionMap::get (builtinDecl->getGenericSignature (),
193
- {actorType}, {});
194
- unmarkedExecutor =
195
- B.createBuiltin (loc, builtinName, resultType, subs, {actor});
212
+ auto &ctx = F->getASTContext ();
213
+ auto executorType = SILType::getPrimitiveObjectType (ctx.TheExecutorType );
214
+ auto optionalExecutorType = SILType::getOptionalType (executorType);
215
+
216
+ // / Emit the instructions to derive an executor value from an actor value.
217
+ auto getExecutorFor = [&](SILValue actor) -> SILValue {
218
+ // If the actor type is a default actor, go ahead and devirtualize here.
219
+ auto module = F->getModule ().getSwiftModule ();
220
+ CanType actorType = actor->getType ().getASTType ();
221
+
222
+ // Determine if the actor is a "default actor" in which case we'll build a default
223
+ // actor executor ref inline, rather than calling out to the user-provided executor function.
224
+ if (isDefaultActorType (actorType, module , F->getResilienceExpansion ())) {
225
+ auto builtinName = ctx.getIdentifier (
226
+ getBuiltinName (BuiltinValueKind::BuildDefaultActorExecutorRef));
227
+ auto builtinDecl = cast<FuncDecl>(getBuiltinValueDecl (ctx, builtinName));
228
+ auto subs = SubstitutionMap::get (builtinDecl->getGenericSignature (),
229
+ {actorType}, {});
230
+ return B.createBuiltin (loc, builtinName, executorType, subs, {actor});
231
+ }
196
232
197
233
// Otherwise, go through (Distributed)Actor.unownedExecutor.
198
- } else {
199
234
auto actorKind = actorType->isDistributedActor () ?
200
235
KnownProtocolKind::DistributedActor :
201
236
KnownProtocolKind::Actor;
@@ -204,6 +239,14 @@ SILValue LowerHopToActor::emitGetExecutor(SILBuilderWithScope &B,
204
239
assert (req && " Concurrency library broken" );
205
240
SILDeclRef fn (req, SILDeclRef::Kind::Func);
206
241
242
+ // Open an existential actor type.
243
+ if (actorType->isExistentialType ()) {
244
+ actorType = OpenedArchetypeType::get (
245
+ actorType, F->getGenericSignature ())->getCanonicalType ();
246
+ SILType loweredActorType = F->getLoweredType (actorType);
247
+ actor = B.createOpenExistentialRef (loc, actor, loweredActorType);
248
+ }
249
+
207
250
auto actorConf = module ->lookupConformance (actorType, actorProtocol);
208
251
assert (actorConf &&
209
252
" hop_to_executor with actor that doesn't conform to Actor or DistributedActor" );
@@ -222,22 +265,64 @@ SILValue LowerHopToActor::emitGetExecutor(SILBuilderWithScope &B,
222
265
auto executorDecl = ctx.getUnownedSerialExecutorDecl ();
223
266
auto executorProps = executorDecl->getStoredProperties ();
224
267
assert (executorProps.size () == 1 );
225
- unmarkedExecutor =
226
- B.createStructExtract (loc, witnessCall, executorProps[0 ]);
268
+ return B.createStructExtract (loc, witnessCall, executorProps[0 ]);
269
+ };
270
+
271
+ SILValue unmarkedExecutor;
272
+ if (auto wrappedActor = actorType->getOptionalObjectType ()) {
273
+ assert (makeOptional);
274
+
275
+ // Unwrap the optional and call 'unownedExecutor'.
276
+ auto *someDecl = B.getASTContext ().getOptionalSomeDecl ();
277
+ auto *curBB = B.getInsertionPoint ()->getParent ();
278
+ auto *contBB = curBB->split (B.getInsertionPoint ());
279
+ auto *someBB = B.getFunction ().createBasicBlockAfter (curBB);
280
+ auto *noneBB = B.getFunction ().createBasicBlockAfter (someBB);
281
+
282
+ unmarkedExecutor = contBB->createPhiArgument (
283
+ optionalExecutorType, actor->getOwnershipKind ());
284
+
285
+ SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 1 > caseBBs;
286
+ caseBBs.push_back (std::make_pair (someDecl, someBB));
287
+ B.setInsertionPoint (curBB);
288
+ auto *switchEnum = B.createSwitchEnum (loc, actor, noneBB, caseBBs);
289
+
290
+ SILValue unwrappedActor;
291
+ if (B.hasOwnership ()) {
292
+ unwrappedActor = switchEnum->createOptionalSomeResult ();
293
+ B.setInsertionPoint (someBB);
294
+ } else {
295
+ B.setInsertionPoint (someBB);
296
+ unwrappedActor = B.createUncheckedEnumData (loc, actor, someDecl);
297
+ }
298
+
299
+ // Call 'unownedExecutor' in the some block and wrap the result into
300
+ // an optional.
301
+ SILValue unwrappedExecutor = getExecutorFor (unwrappedActor);
302
+ SILValue someValue =
303
+ B.createOptionalSome (loc, unwrappedExecutor, optionalExecutorType);
304
+ B.createBranch (loc, contBB, {someValue});
305
+
306
+ // In the none case, create a nil executor value, which represents
307
+ // the generic executor.
308
+ B.setInsertionPoint (noneBB);
309
+ SILValue noneValue = B.createOptionalNone (loc, optionalExecutorType);
310
+ B.createBranch (loc, contBB, {noneValue});
311
+ B.setInsertionPoint (contBB->begin ());
312
+ } else {
313
+ unmarkedExecutor = getExecutorFor (actor);
314
+
315
+ // Inject the result into an optional if requested.
316
+ if (makeOptional) {
317
+ unmarkedExecutor = B.createOptionalSome (loc, unmarkedExecutor,
318
+ SILType::getOptionalType (unmarkedExecutor->getType ()));
319
+ }
227
320
}
228
321
229
322
// Mark the dependence of the resulting value on the actor value to
230
323
// force the actor to stay alive.
231
- executor = B.createMarkDependence (loc, unmarkedExecutor, actor,
232
- /* isNonEscaping*/ false );
233
-
234
- // Cache the non-optional result for later.
235
- ExecutorForActor.insert (actor, executor);
236
-
237
- // Inject the result into an optional if requested.
238
- if (makeOptional)
239
- executor = B.createOptionalSome (loc, executor,
240
- SILType::getOptionalType (executor->getType ()));
324
+ SILValue executor = B.createMarkDependence (loc, unmarkedExecutor, actor,
325
+ /* isNonEscaping*/ false );
241
326
242
327
return executor;
243
328
}
@@ -250,7 +335,7 @@ class LowerHopToActorPass : public SILFunctionTransform {
250
335
auto domTree = getAnalysis<DominanceAnalysis>()->get (fn);
251
336
LowerHopToActor pass (getFunction (), domTree);
252
337
if (pass.run ())
253
- invalidateAnalysis (SILAnalysis::InvalidationKind::Instructions );
338
+ invalidateAnalysis (SILAnalysis::InvalidationKind::BranchesAndInstructions );
254
339
}
255
340
};
256
341
0 commit comments