@@ -107,8 +107,13 @@ void ExistentialSpecializerCloner::cloneAndPopulateFunction() {
107
107
SILBuilderWithScope Builder (exitBB->getTerminator ());
108
108
// A return location can't be used for a non-return instruction.
109
109
auto loc = RegularLocation::getAutoGeneratedLocation ();
110
- for (SILValue cleanupVal : CleanupValues)
111
- Builder.createDestroyAddr (loc, cleanupVal);
110
+ for (SILValue cleanupVal : CleanupValues) {
111
+ if (cleanupVal.getOwnershipKind () == ValueOwnershipKind::Guaranteed) {
112
+ Builder.emitEndBorrowOperation (loc, cleanupVal);
113
+ } else {
114
+ Builder.emitDestroyOperation (loc, cleanupVal);
115
+ }
116
+ }
112
117
113
118
for (auto *ASI : llvm::reverse (AllocStackInsts))
114
119
Builder.createDeallocStack (loc, ASI);
@@ -180,10 +185,10 @@ void ExistentialSpecializerCloner::cloneArguments(
180
185
NewF.getLoweredType (NewF.mapTypeIntoContext (GenericParam));
181
186
GenericSILType = GenericSILType.getCategoryType (
182
187
ArgDesc.Arg ->getType ().getCategory ());
183
- auto *NewArg =
184
- ClonedEntryBB-> createFunctionArgument ( GenericSILType, ArgDesc.Decl );
185
- NewArg-> setOwnershipKind ( ValueOwnershipKind (
186
- NewF, GenericSILType, ArgDesc.Arg ->getArgumentConvention ()));
188
+ auto *NewArg = ClonedEntryBB-> createFunctionArgument (
189
+ GenericSILType, ArgDesc.Decl ,
190
+ ValueOwnershipKind (NewF, GenericSILType,
191
+ ArgDesc.Arg ->getArgumentConvention ()));
187
192
// Determine the Conformances.
188
193
SILType ExistentialType = ArgDesc.Arg ->getType ().getObjectType ();
189
194
CanType OpenedType = NewArg->getType ().getASTType ();
@@ -224,35 +229,54 @@ void ExistentialSpecializerCloner::cloneArguments(
224
229
}
225
230
case ExistentialRepresentation::Class: {
226
231
SILValue NewArgValue = NewArg;
232
+ bool origConsumed = EAD.isConsumed ;
233
+
234
+ // Load our object if needed and if our original value was not consumed,
235
+ // make a copy in ossa. Do not perturb code-gen in non-ossa code though.
227
236
if (!NewArg->getType ().isObject ()) {
228
- NewArgValue = NewFBuilder.createLoad (InsertLoc, NewArg,
229
- LoadOwnershipQualifier::Unqualified);
237
+ auto qual = LoadOwnershipQualifier::Take;
238
+ if (NewFBuilder.hasOwnership () && !origConsumed) {
239
+ qual = LoadOwnershipQualifier::Copy;
240
+ }
241
+ NewArgValue =
242
+ NewFBuilder.emitLoadValueOperation (InsertLoc, NewArg, qual);
243
+ } else {
244
+ if (NewFBuilder.hasOwnership () && !origConsumed) {
245
+ NewArgValue = NewFBuilder.emitCopyValueOperation (InsertLoc, NewArg);
246
+ }
230
247
}
231
-
232
- // FIXME_ownership: init_existential_ref always takes ownership of the
233
- // incoming reference. If the argument convention is borrowed
234
- // (!isConsumed), then we should create a copy_value here and add this new
235
- // existential to the CleanupValues vector.
236
248
237
249
// / Simple case: Create an init_existential.
238
250
// / %5 = init_existential_ref %0 : $T : $T, $P
239
251
SILValue InitRef = NewFBuilder.createInitExistentialRef (
240
252
InsertLoc, ArgDesc.Arg ->getType ().getObjectType (),
241
253
NewArg->getType ().getASTType (),
242
254
NewArgValue, Conformances);
243
-
255
+
256
+ // If we don't have an object and we are in ossa, the store will consume
257
+ // the InitRef.
244
258
if (!NewArg->getType ().isObject ()) {
245
259
auto alloc = NewFBuilder.createAllocStack (InsertLoc,
246
260
InitRef->getType ());
247
- NewFBuilder.createStore (InsertLoc, InitRef, alloc,
248
- StoreOwnershipQualifier::Unqualified );
261
+ NewFBuilder.emitStoreValueOperation (InsertLoc, InitRef, alloc,
262
+ StoreOwnershipQualifier::Init );
249
263
InitRef = alloc;
250
264
AllocStackInsts.push_back (alloc);
265
+ } else {
266
+ // Otherwise in ossa, we need to add init existential ref as something
267
+ // to be cleaned up. In non-ossa, we do not insert the copies, so we do
268
+ // not need to do it then.
269
+ //
270
+ // TODO: This would be simpler if we had managed value/cleanup scopes.
271
+ if (NewFBuilder.hasOwnership () && !origConsumed) {
272
+ CleanupValues.push_back (InitRef);
273
+ }
251
274
}
252
275
253
276
entryArgs.push_back (InitRef);
254
277
break ;
255
278
}
279
+
256
280
default : {
257
281
llvm_unreachable (" Unhandled existential type in ExistentialTransform!" );
258
282
break ;
@@ -451,12 +475,13 @@ void ExistentialTransform::populateThunkBody() {
451
475
SILValue archetypeValue;
452
476
auto ExistentialRepr =
453
477
ArgDesc.Arg ->getType ().getPreferredExistentialRepresentation ();
478
+ bool OriginallyConsumed = ETAD.isConsumed ;
454
479
switch (ExistentialRepr) {
455
480
case ExistentialRepresentation::Opaque: {
456
481
archetypeValue = Builder.createOpenExistentialAddr (
457
482
Loc, OrigOperand, OpenedSILType, it->second .AccessType );
458
483
SILValue calleeArg = archetypeValue;
459
- if (ETAD. isConsumed ) {
484
+ if (OriginallyConsumed ) {
460
485
// open_existential_addr projects a borrowed address into the
461
486
// existential box. Since the callee consumes the generic value, we
462
487
// must pass in a copy.
@@ -474,19 +499,39 @@ void ExistentialTransform::populateThunkBody() {
474
499
// If the operand is not object type, we need an explicit load.
475
500
SILValue OrigValue = OrigOperand;
476
501
if (!OrigOperand->getType ().isObject ()) {
477
- OrigValue = Builder.createLoad (Loc, OrigValue,
478
- LoadOwnershipQualifier::Unqualified);
502
+ auto qual = LoadOwnershipQualifier::Take;
503
+ if (Builder.hasOwnership () && !OriginallyConsumed) {
504
+ qual = LoadOwnershipQualifier::Copy;
505
+ }
506
+ OrigValue = Builder.emitLoadValueOperation (Loc, OrigValue, qual);
507
+ } else {
508
+ if (Builder.hasOwnership () && !OriginallyConsumed) {
509
+ OrigValue = Builder.emitCopyValueOperation (Loc, OrigValue);
510
+ }
479
511
}
512
+
480
513
// OpenExistentialRef forwards ownership, so it does the right thing
481
514
// regardless of whether the argument is borrowed or consumed.
482
515
archetypeValue =
483
516
Builder.createOpenExistentialRef (Loc, OrigValue, OpenedSILType);
517
+
518
+ // If we don't have an object and we are in ossa, the store will consume
519
+ // the open_existential_ref.
484
520
if (!OrigOperand->getType ().isObject ()) {
485
521
SILValue ASI = Builder.createAllocStack (Loc, OpenedSILType);
486
- Builder.createStore (Loc, archetypeValue, ASI,
487
- StoreOwnershipQualifier::Unqualified );
522
+ Builder.emitStoreValueOperation (Loc, archetypeValue, ASI,
523
+ StoreOwnershipQualifier::Init );
488
524
Temps.push_back ({ASI, SILValue ()});
489
525
archetypeValue = ASI;
526
+ } else {
527
+ // Otherwise in ossa, we need to add open_existential_ref as something
528
+ // to be cleaned up. In non-ossa, we do not insert the copies, so we
529
+ // do not need to do it then.
530
+ //
531
+ // TODO: This would be simpler if we had managed value/cleanup scopes.
532
+ if (Builder.hasOwnership () && !OriginallyConsumed) {
533
+ Temps.push_back ({SILValue (), archetypeValue});
534
+ }
490
535
}
491
536
ApplyArgs.push_back (archetypeValue);
492
537
break ;
@@ -572,11 +617,14 @@ void ExistentialTransform::populateThunkBody() {
572
617
// copy_addr %valAdr to %temp // <== Temp CopyAddr
573
618
// apply(%temp) // <== Temp is consumed by the apply
574
619
//
575
- // Destroy the original argument and deallocation the temporary:
620
+ // Destroy the original argument and deallocation the temporary. If we have
621
+ // an address this becomes:
576
622
// destroy_addr %consumedExistential : $*Protocol
577
623
// dealloc_stack %temp : $*T
624
+ //
625
+ // Otherwise, if we had an object, we just emit a destroy_value.
578
626
if (Temp.DestroyValue )
579
- Builder.createDestroyAddr (cleanupLoc, Temp.DestroyValue );
627
+ Builder.emitDestroyOperation (cleanupLoc, Temp.DestroyValue );
580
628
if (Temp.DeallocStackEntry )
581
629
Builder.createDeallocStack (cleanupLoc, Temp.DeallocStackEntry );
582
630
}
0 commit comments