1111// ===----------------------------------------------------------------------===//
1212
1313#define DEBUG_TYPE " insert-hop-to-executor"
14+ #include " swift/Basic/FrozenMultiMap.h"
1415#include " swift/SIL/SILBuilder.h"
1516#include " swift/SIL/SILFunction.h"
1617#include " swift/SIL/Dominance.h"
@@ -51,11 +52,18 @@ class LowerHopToActor {
5152 SILFunction *F;
5253 DominanceInfo *Dominance;
5354
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;
5659
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 ();
5967
6068 SILValue emitGetExecutor (SILBuilderWithScope &B,
6169 SILLocation loc,
@@ -73,21 +81,24 @@ class LowerHopToActor {
7381};
7482
7583bool 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.
7886 auto runOnBlock = [&](SILBasicBlock *block) {
7987 for (auto ii = block->begin (), ie = block->end (); ii != ie; ) {
8088 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);
8690 }
8791 };
88- runInDominanceOrderWithScopes (Dominance, runOnBlock, ExecutorForActor);
92+ runInDominanceOrderWithScopes (Dominance, runOnBlock,
93+ ExecutorDerivationForActor);
8994
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 ;
91102}
92103
93104static bool isOptionalBuiltinExecutor (SILType type) {
@@ -96,49 +107,73 @@ static bool isOptionalBuiltinExecutor(SILType type) {
96107 return false ;
97108}
98109
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+ }
102124
103- // Ignore hops that are already to Optional<Builtin.Executor>.
104125 if (isOptionalBuiltinExecutor (actor->getType ()))
105- return false ;
126+ return ;
106127
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);
115131 } 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);
119134 }
120- assert (executor && " executor not set" );
121135
122- B.createHopToExecutor (hop->getLoc (), executor, /* mandatory*/ false );
136+ return ;
137+ }
123138
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+ }
125158
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+ }
128173
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+ }
136176 }
137-
138- // Unconditionally replace the extract with the executor.
139- extract->replaceAllUsesWith (executor);
140- extract->eraseFromParent ();
141- return true ;
142177}
143178
144179static bool isDefaultActorType (CanType actorType, ModuleDecl *M,
@@ -162,40 +197,40 @@ static AccessorDecl *getUnownedExecutorGetter(ASTContext &ctx,
162197SILValue LowerHopToActor::emitGetExecutor (SILBuilderWithScope &B,
163198 SILLocation loc, SILValue actor,
164199 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-
175200 // This is okay because actor types have to be classes and so never
176201 // have multiple abstraction patterns.
177202 CanType actorType = actor->getType ().getASTType ();
178203
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+ }
185211
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+ }
196232
197233 // Otherwise, go through (Distributed)Actor.unownedExecutor.
198- } else {
199234 auto actorKind = actorType->isDistributedActor () ?
200235 KnownProtocolKind::DistributedActor :
201236 KnownProtocolKind::Actor;
@@ -204,6 +239,14 @@ SILValue LowerHopToActor::emitGetExecutor(SILBuilderWithScope &B,
204239 assert (req && " Concurrency library broken" );
205240 SILDeclRef fn (req, SILDeclRef::Kind::Func);
206241
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+
207250 auto actorConf = module ->lookupConformance (actorType, actorProtocol);
208251 assert (actorConf &&
209252 " hop_to_executor with actor that doesn't conform to Actor or DistributedActor" );
@@ -222,22 +265,64 @@ SILValue LowerHopToActor::emitGetExecutor(SILBuilderWithScope &B,
222265 auto executorDecl = ctx.getUnownedSerialExecutorDecl ();
223266 auto executorProps = executorDecl->getStoredProperties ();
224267 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+ }
227320 }
228321
229322 // Mark the dependence of the resulting value on the actor value to
230323 // 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 );
241326
242327 return executor;
243328}
@@ -250,7 +335,7 @@ class LowerHopToActorPass : public SILFunctionTransform {
250335 auto domTree = getAnalysis<DominanceAnalysis>()->get (fn);
251336 LowerHopToActor pass (getFunction (), domTree);
252337 if (pass.run ())
253- invalidateAnalysis (SILAnalysis::InvalidationKind::Instructions );
338+ invalidateAnalysis (SILAnalysis::InvalidationKind::BranchesAndInstructions );
254339 }
255340};
256341
0 commit comments