@@ -57,9 +57,10 @@ class ExistentialSpecializerCloner
57
57
// Use one OpenedArchetypesTracker while cloning.
58
58
SILOpenedArchetypesTracker OpenedArchetypesTracker;
59
59
60
- // Holds an entry for any indirect argument requiring a new alloc_stack
61
- // created during argument cloning.
62
- SmallDenseMap<int , AllocStackInst *> ArgToAllocStackMap;
60
+ // AllocStack instructions introduced in the new prolog that require cleanup.
61
+ SmallVector<AllocStackInst *, 4 > AllocStackInsts;
62
+ // Temporary values introduced in the new prolog that require cleanup.
63
+ SmallVector<SILValue, 4 > CleanupValues;
63
64
64
65
protected:
65
66
void postProcess (SILInstruction *Orig, SILInstruction *Cloned) {
@@ -99,36 +100,24 @@ void ExistentialSpecializerCloner::cloneAndPopulateFunction() {
99
100
auto *NewEntryBB = getBuilder ().getFunction ().getEntryBlock ();
100
101
cloneFunctionBody (&Original, NewEntryBB, entryArgs);
101
102
102
- // / If there is an argument with no DestroyUse, insert DeallocStack
103
- // / before return Instruction.
104
- SmallPtrSet<ReturnInst *, 4 > ReturnInsts;
105
- // / Find the set of return instructions in a function.
106
- for (auto &BB : getBuilder ().getFunction ()) {
107
- TermInst *TI = BB.getTerminator ();
108
- if (auto *RI = dyn_cast<ReturnInst>(TI)) {
109
- ReturnInsts.insert (RI);
110
- }
111
- }
112
-
113
- // / Walk over destroy_addr instructions and perform fixups.
114
- for (auto &ArgDesc : reversed (ArgumentDescList)) {
115
- int ArgIndex = ArgDesc.Index ;
116
- auto iter = ArgToAllocStackMap.find (ArgIndex);
117
- if (iter != ArgToAllocStackMap.end ()) {
118
- // Need to insert DeallocStack before return.
119
- for (auto *I : ReturnInsts) {
120
- SILBuilderWithScope Builder (I);
121
- // A return location can't be used for a non-return instruction.
122
- auto loc = RegularLocation::getAutoGeneratedLocation ();
123
- Builder.createDeallocStack (loc, iter->second );
124
- }
125
- }
103
+ // Cleanup allocations created in the new prolog.
104
+ SmallVector<SILBasicBlock *, 4 > exitingBlocks;
105
+ getBuilder ().getFunction ().findExitingBlocks (exitingBlocks);
106
+ for (auto *exitBB : exitingBlocks) {
107
+ SILBuilderWithScope Builder (exitBB->getTerminator ());
108
+ // A return location can't be used for a non-return instruction.
109
+ auto loc = RegularLocation::getAutoGeneratedLocation ();
110
+ for (SILValue cleanupVal : CleanupValues)
111
+ Builder.createDestroyAddr (loc, cleanupVal);
112
+
113
+ for (auto *ASI : reversed (AllocStackInsts))
114
+ Builder.createDeallocStack (loc, ASI);
126
115
}
127
116
}
128
117
129
118
// Create the entry basic block with the function arguments.
130
- void ExistentialSpecializerCloner::
131
- cloneArguments ( SmallVectorImpl<SILValue> &entryArgs) {
119
+ void ExistentialSpecializerCloner::cloneArguments (
120
+ SmallVectorImpl<SILValue> &entryArgs) {
132
121
auto &M = OrigF->getModule ();
133
122
auto &Ctx = M.getASTContext ();
134
123
@@ -190,38 +179,42 @@ cloneArguments(SmallVectorImpl<SILValue> &entryArgs) {
190
179
// / bb0(%0 : $*T):
191
180
// / %3 = alloc_stack $P
192
181
// / %4 = init_existential_addr %3 : $*P, $T
193
- // / copy_addr %0 to [initialization] %4 : $*T
194
- // / destroy_addr %0 : $*T
182
+ // / copy_addr [take] %0 to [initialization] %4 : $*T
195
183
// / %7 = open_existential_addr immutable_access %3 : $*P to
196
184
// / $*@opened P
197
185
auto *ASI =
198
186
NewFBuilder.createAllocStack (InsertLoc, ArgDesc.Arg ->getType ());
199
- ArgToAllocStackMap.insert (
200
- std::pair<int , AllocStackInst *>(ArgDesc.Index , ASI));
187
+ AllocStackInsts.push_back (ASI);
201
188
202
189
auto *EAI = NewFBuilder.createInitExistentialAddr (
203
190
InsertLoc, ASI, NewArg->getType ().getASTType (), NewArg->getType (),
204
191
Conformances);
205
- // / If DestroyAddr is already there, then do not use [take].
206
- NewFBuilder. createCopyAddr (
207
- InsertLoc, NewArg, EAI,
208
- ExistentialArgDescriptor[ArgDesc. Index ]. AccessType ==
209
- OpenedExistentialAccess::Immutable
210
- ? IsTake_t::IsNotTake
211
- : IsTake_t::IsTake,
212
- IsInitialization_t::IsInitialization);
213
- if (ExistentialArgDescriptor[ArgDesc. Index ]. DestroyAddrUse ) {
214
- NewFBuilder. createDestroyAddr (InsertLoc, NewArg);
215
- }
192
+
193
+ bool origConsumed = ExistentialArgDescriptor[ArgDesc. Index ]. isConsumed ;
194
+ // If the existential is not consumed in the function body, then the one
195
+ // we introduce here needs cleanup.
196
+ if (!origConsumed)
197
+ CleanupValues. push_back (ASI);
198
+
199
+ NewFBuilder. createCopyAddr (InsertLoc, NewArg, EAI,
200
+ origConsumed ? IsTake_t::IsTake
201
+ : IsTake_t::IsNotTake,
202
+ IsInitialization_t::IsInitialization);
216
203
entryArgs.push_back (ASI);
217
204
break ;
218
205
}
219
206
case ExistentialRepresentation::Class: {
207
+ // FIXME_ownership: init_existential_ref always takes ownership of the
208
+ // incoming reference. If the argument convention is borrowed
209
+ // (!isConsumed), then we should create a copy_value here and add this new
210
+ // existential to the CleanupValues vector.
211
+
220
212
// / Simple case: Create an init_existential.
221
213
// / %5 = init_existential_ref %0 : $T : $T, $P
222
214
auto *InitRef = NewFBuilder.createInitExistentialRef (
223
215
InsertLoc, ArgDesc.Arg ->getType (), NewArg->getType ().getASTType (),
224
216
NewArg, Conformances);
217
+
225
218
entryArgs.push_back (InitRef);
226
219
break ;
227
220
}
@@ -399,12 +392,16 @@ void ExistentialTransform::populateThunkBody() {
399
392
// / Determine arguments to Apply.
400
393
// / Generate opened existentials for generics.
401
394
SmallVector<SILValue, 8 > ApplyArgs;
395
+ // Maintain a list of arg values to be destroyed. These are consumed by the
396
+ // convention and require a copy.
397
+ SmallVector<CopyAddrInst *, 8 > TempCopyAddrInsts;
402
398
SmallDenseMap<GenericTypeParamType *, Type> GenericToOpenedTypeMap;
403
399
for (auto &ArgDesc : ArgumentDescList) {
404
400
auto iter = ArgToGenericTypeMap.find (ArgDesc.Index );
405
401
auto it = ExistentialArgDescriptor.find (ArgDesc.Index );
406
402
if (iter != ArgToGenericTypeMap.end () &&
407
403
it != ExistentialArgDescriptor.end ()) {
404
+ ExistentialTransformArgumentDescriptor &ETAD = it->second ;
408
405
OpenedArchetypeType *Opened;
409
406
auto OrigOperand = ThunkBody->getArgument (ArgDesc.Index );
410
407
auto SwiftType = ArgDesc.Arg ->getType ().getASTType ();
@@ -418,11 +415,26 @@ void ExistentialTransform::populateThunkBody() {
418
415
case ExistentialRepresentation::Opaque: {
419
416
archetypeValue = Builder.createOpenExistentialAddr (
420
417
Loc, OrigOperand, OpenedSILType, it->second .AccessType );
421
- ApplyArgs.push_back (archetypeValue);
418
+ SILValue calleeArg = archetypeValue;
419
+ if (ETAD.isConsumed ) {
420
+ // open_existential_addr projects a borrowed address into the
421
+ // existential box. Since the callee consumes the generic value, we
422
+ // must pass in a copy.
423
+ auto *ASI =
424
+ Builder.createAllocStack (Loc, OpenedSILType);
425
+ auto *CAI =
426
+ Builder.createCopyAddr (Loc, archetypeValue, ASI, IsNotTake,
427
+ IsInitialization_t::IsInitialization);
428
+ TempCopyAddrInsts.push_back (CAI);
429
+ calleeArg = ASI;
430
+ }
431
+ ApplyArgs.push_back (calleeArg);
422
432
break ;
423
433
}
424
434
case ExistentialRepresentation::Class: {
425
435
// / If the operand is not object type, we would need an explicit load.
436
+ // OpenExistentialRef forwards ownership, so it does the right thing
437
+ // regardless of whether the argument is borrowed or consumed.
426
438
assert (OrigOperand->getType ().isObject ());
427
439
archetypeValue =
428
440
Builder.createOpenExistentialRef (Loc, OrigOperand, OpenedSILType);
@@ -498,7 +510,24 @@ void ExistentialTransform::populateThunkBody() {
498
510
// / Create the Apply with substitutions
499
511
ReturnValue = Builder.createApply (Loc, FRI, SubMap, ApplyArgs);
500
512
}
501
-
513
+ auto cleanupLoc = RegularLocation::getAutoGeneratedLocation ();
514
+ for (CopyAddrInst *CAI : reversed (TempCopyAddrInsts)) {
515
+ // The original argument was copied into a temporary and consumed by the
516
+ // callee as such:
517
+ // bb (%consumedExistential : $*Protocol)
518
+ // %valAdr = open_existential_addr %consumedExistential
519
+ // %temp = alloc_stack $T
520
+ // copy_addr %valAdr to %temp // <== Temp CopyAddr
521
+ // apply(%temp) // <== Temp is consumed by the apply
522
+ //
523
+ // Destroy the original arument and deallocation the temporary:
524
+ // destroy_addr %consumedExistential : $*Protocol
525
+ // dealloc_stack %temp : $*T
526
+ auto *consumedExistential = cast<SILFunctionArgument>(
527
+ cast<OpenExistentialAddrInst>(CAI->getSrc ())->getOperand ());
528
+ Builder.createDestroyAddr (cleanupLoc, consumedExistential);
529
+ Builder.createDeallocStack (cleanupLoc, CAI->getDest ());
530
+ }
502
531
// / Set up the return results.
503
532
if (NewF->isNoReturnFunction ()) {
504
533
Builder.createUnreachable (Loc);
0 commit comments