2020
2121namespace swift {
2222
23- void emitDistributedActorSystemWitnessCall (
23+ // / \returns the result of the call, if returned directly.
24+ std::optional<SILValue> emitDistributedActorSystemWitnessCall (
2425 SILBuilder &B, SILLocation loc, DeclName methodName, SILValue base,
2526 // types to be passed through to SubstitutionMap:
2627 SILType actorType,
2728 // call arguments, except the base which will be passed last
2829 ArrayRef<SILValue> args,
30+ // pre-allocated, uninitialized indirect result storage, if needed
31+ std::optional<SILValue> indirectResult,
2932 std::optional<std::pair<SILBasicBlock *, SILBasicBlock *>> tryTargets) {
3033 auto &F = B.getFunction ();
3134 auto &M = B.getModule ();
@@ -73,7 +76,6 @@ void emitDistributedActorSystemWitnessCall(
7376 KnownProtocolKind::DistributedActor);
7477 assert (actorProto);
7578
76- ProtocolConformanceRef conformance;
7779 auto distributedActorConfRef = lookupConformance (
7880 actorType.getASTType (), actorProto);
7981 assert (!distributedActorConfRef.isInvalid () &&
@@ -85,42 +87,61 @@ void emitDistributedActorSystemWitnessCall(
8587 subs = SubstitutionMap::get (genericSig, subTypes, subConformances);
8688 }
8789
88- std::optional<SILValue> temporaryArgumentBuffer;
89-
90- // If the self parameter is indirect but the base is a value, put it
91- // into a temporary allocation.
9290 auto methodSILFnTy = methodSILTy.castTo <SILFunctionType>();
93- std::optional<SILValue> temporaryActorSystemBuffer;
94- if (methodSILFnTy->getSelfParameter ().isFormalIndirect () &&
95- !base->getType ().isAddress ()) {
96- auto buf = B.createAllocStack (loc, base->getType (), std::nullopt );
97- base = B.emitCopyValueOperation (loc, base);
98- B.emitStoreValueOperation (
99- loc, base, buf, StoreOwnershipQualifier::Init);
100- temporaryActorSystemBuffer = SILValue (buf);
101- }
91+ SILFunctionConventions conv (methodSILFnTy, M);
92+
93+ // Since this code lives outside of SILGen, manage our clean-ups manually.
94+ SmallVector<SILInstruction *, 2 > cleanups;
95+
96+ auto prepareArgument = [&](SILParameterInfo param, SILValue arg) -> SILValue {
97+ if (conv.isSILIndirect (param)) {
98+ // Does it need temporary stack storage?
99+ if (!arg->getType ().isAddress () &&
100+ !dyn_cast<AnyMetatypeType>(arg->getType ().getASTType ())) {
101+ auto buf = B.createAllocStack (loc, arg->getType (), std::nullopt );
102+ cleanups.push_back (buf);
103+
104+ auto copy = B.emitCopyValueOperation (loc, arg);
105+ B.emitStoreValueOperation (
106+ loc, copy, buf, StoreOwnershipQualifier::Init);
107+
108+ return buf;
109+ }
110+ return arg; // no temporary storage needed
111+ }
112+
113+ // Otherwise, it's a direct convention. Borrow if needed.
114+ if (arg->getType ().isAddress ()) {
115+ arg = B.emitLoadBorrowOperation (loc, arg);
116+ cleanups.push_back (arg.getDefiningInstruction ());
117+ }
118+ return arg;
119+ };
120+
121+ SILValue selfArg = prepareArgument (methodSILFnTy->getSelfParameter (), base);
102122
103123 // === Call the method.
104124 // --- Push the arguments
105125 SmallVector<SILValue, 2 > allArgs;
126+
127+ const bool hasIndirectResult = conv.getNumIndirectSILResults () > 0 ;
128+ ASSERT (hasIndirectResult == indirectResult.has_value () && " no indirectResult storage given!" );
129+ ASSERT (conv.getNumIndirectSILResults () <= 1 );
130+
131+ const bool hasDirectResult = conv.getNumDirectSILResults () > 0 ;
132+ ASSERT (!(hasIndirectResult && hasDirectResult) && " indirect AND direct results aren't supported" );
133+ ASSERT (conv.getNumDirectSILResults () <= 1 );
134+
135+ if (hasIndirectResult) {
136+ allArgs.push_back (*indirectResult);
137+ }
138+
106139 auto params = methodSILFnTy->getParameters ();
107140 for (size_t i = 0 ; i < args.size (); ++i) {
108- auto arg = args[i];
109- if (params[i].isFormalIndirect () &&
110- !arg->getType ().isAddress () &&
111- !dyn_cast<AnyMetatypeType>(arg->getType ().getASTType ())) {
112- auto buf = B.createAllocStack (loc, arg->getType (), std::nullopt );
113- auto argCopy = B.emitCopyValueOperation (loc, arg);
114- B.emitStoreValueOperation (
115- loc, argCopy, buf, StoreOwnershipQualifier::Init);
116- temporaryArgumentBuffer = SILValue (buf);
117- allArgs.push_back (*temporaryArgumentBuffer);
118- } else {
119- allArgs.push_back (arg);
120- }
141+ allArgs.push_back (prepareArgument (params[i], args[i]));
121142 }
143+
122144 // Push the self argument
123- auto selfArg = temporaryActorSystemBuffer ? *temporaryActorSystemBuffer : base;
124145 allArgs.push_back (selfArg);
125146
126147 SILInstruction *apply;
@@ -132,7 +153,7 @@ void emitDistributedActorSystemWitnessCall(
132153 apply = B.createApply (loc, witnessMethod, subs, allArgs);
133154 }
134155
135- // Local function to emit a cleanup after the call.
156+ // Local function to emit cleanups after the call in successor blocks .
136157 auto emitCleanup = [&](llvm::function_ref<void (SILBuilder &builder)> fn) {
137158 if (tryTargets) {
138159 {
@@ -148,25 +169,45 @@ void emitDistributedActorSystemWitnessCall(
148169 }
149170 };
150171
151- // ==== If we had to create a buffers we need to clean them up
152- // --- Cleanup id buffer
153- if (temporaryArgumentBuffer) {
154- emitCleanup ([&](SILBuilder & builder) {
155- auto value = builder.emitLoadValueOperation (
156- loc, *temporaryArgumentBuffer, LoadOwnershipQualifier::Take);
157- builder.emitDestroyValueOperation (loc, value);
158- builder.createDeallocStack (loc, *temporaryArgumentBuffer);
159- });
172+ // Emit clean-ups in reverse order, to preserve stack nesting, etc.
173+ for (auto inst : reverse (cleanups)) {
174+ if (auto asi = dyn_cast<AllocStackInst>(inst)) {
175+ auto buf = asi->getResult (0 );
176+ emitCleanup ([&](SILBuilder & builder) {
177+ // FIXME: could do destroy_addr rather than take + destroy_value
178+ auto value = builder.emitLoadValueOperation (
179+ loc, buf, LoadOwnershipQualifier::Take);
180+ builder.emitDestroyValueOperation (loc, value);
181+ builder.createDeallocStack (loc, buf);
182+ });
183+ continue ;
184+ }
185+
186+ if (auto lb = dyn_cast<LoadBorrowInst>(inst)) {
187+ auto borrow = lb->getResult (0 );
188+ emitCleanup ([&](SILBuilder & builder) {
189+ builder.emitEndBorrowOperation (loc, borrow);
190+ });
191+ continue ;
192+ }
193+
194+ if (isa<LoadInst>(inst)) {
195+ // no clean-ups required
196+ continue ;
197+ }
198+
199+ llvm_unreachable (" unknown instruction kind to clean-up!" );
160200 }
161- // --- Cleanup base buffer
162- if (temporaryActorSystemBuffer) {
163- emitCleanup ([&](SILBuilder & builder) {
164- auto value = builder.emitLoadValueOperation (
165- loc, *temporaryActorSystemBuffer, LoadOwnershipQualifier::Take);
166- builder.emitDestroyValueOperation (loc, value);
167- builder.createDeallocStack (loc, *temporaryActorSystemBuffer);
168- });
201+
202+ // If this was a try_apply, then the result is the BB argument of the
203+ // successor block. We let our caller figure that out themselves.
204+ //
205+ // Otherwise, the apply had a single direct result, so we return that.
206+ if (hasDirectResult && !tryTargets) {
207+ return apply->getResult (0 );
169208 }
209+
210+ return std::nullopt ;
170211}
171212
172213void emitActorReadyCall (SILBuilder &B, SILLocation loc, SILValue actor,
0 commit comments