Skip to content

Commit 28b2f1e

Browse files
authored
Merge pull request #33295 from gottesmm/pr-b4c03d477d7e5debea7371445387263cc9269360
[ownership] Begin updating existential specializer.
2 parents 8e46880 + 6017ef5 commit 28b2f1e

File tree

3 files changed

+334
-34
lines changed

3 files changed

+334
-34
lines changed

lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,6 @@ class ExistentialSpecializer : public SILFunctionTransform {
7575
return;
7676
}
7777

78-
// FIXME: This pass should be able to support ownership.
79-
if (F->hasOwnership())
80-
return;
81-
8278
/// Get CallerAnalysis information handy.
8379
CA = PM->getAnalysis<CallerAnalysis>();
8480

@@ -220,13 +216,6 @@ bool ExistentialSpecializer::canSpecializeCalleeFunction(FullApplySite &Apply) {
220216
if (!Callee->isDefinition())
221217
return false;
222218

223-
// If the callee has ownership enabled, bail.
224-
//
225-
// FIXME: We should be able to handle callees that have ownership, but the
226-
// pass has not been updated yet.
227-
if (Callee->hasOwnership())
228-
return false;
229-
230219
// Ignore generic functions. Generic functions should be fully specialized
231220
// before attempting to introduce new generic parameters for existential
232221
// arguments. Otherwise, there's no guarantee that the generic specializer

lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,13 @@ void ExistentialSpecializerCloner::cloneAndPopulateFunction() {
107107
SILBuilderWithScope Builder(exitBB->getTerminator());
108108
// A return location can't be used for a non-return instruction.
109109
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+
}
112117

113118
for (auto *ASI : llvm::reverse(AllocStackInsts))
114119
Builder.createDeallocStack(loc, ASI);
@@ -180,10 +185,10 @@ void ExistentialSpecializerCloner::cloneArguments(
180185
NewF.getLoweredType(NewF.mapTypeIntoContext(GenericParam));
181186
GenericSILType = GenericSILType.getCategoryType(
182187
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()));
187192
// Determine the Conformances.
188193
SILType ExistentialType = ArgDesc.Arg->getType().getObjectType();
189194
CanType OpenedType = NewArg->getType().getASTType();
@@ -224,35 +229,54 @@ void ExistentialSpecializerCloner::cloneArguments(
224229
}
225230
case ExistentialRepresentation::Class: {
226231
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.
227236
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+
}
230247
}
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.
236248

237249
/// Simple case: Create an init_existential.
238250
/// %5 = init_existential_ref %0 : $T : $T, $P
239251
SILValue InitRef = NewFBuilder.createInitExistentialRef(
240252
InsertLoc, ArgDesc.Arg->getType().getObjectType(),
241253
NewArg->getType().getASTType(),
242254
NewArgValue, Conformances);
243-
255+
256+
// If we don't have an object and we are in ossa, the store will consume
257+
// the InitRef.
244258
if (!NewArg->getType().isObject()) {
245259
auto alloc = NewFBuilder.createAllocStack(InsertLoc,
246260
InitRef->getType());
247-
NewFBuilder.createStore(InsertLoc, InitRef, alloc,
248-
StoreOwnershipQualifier::Unqualified);
261+
NewFBuilder.emitStoreValueOperation(InsertLoc, InitRef, alloc,
262+
StoreOwnershipQualifier::Init);
249263
InitRef = alloc;
250264
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+
}
251274
}
252275

253276
entryArgs.push_back(InitRef);
254277
break;
255278
}
279+
256280
default: {
257281
llvm_unreachable("Unhandled existential type in ExistentialTransform!");
258282
break;
@@ -451,12 +475,13 @@ void ExistentialTransform::populateThunkBody() {
451475
SILValue archetypeValue;
452476
auto ExistentialRepr =
453477
ArgDesc.Arg->getType().getPreferredExistentialRepresentation();
478+
bool OriginallyConsumed = ETAD.isConsumed;
454479
switch (ExistentialRepr) {
455480
case ExistentialRepresentation::Opaque: {
456481
archetypeValue = Builder.createOpenExistentialAddr(
457482
Loc, OrigOperand, OpenedSILType, it->second.AccessType);
458483
SILValue calleeArg = archetypeValue;
459-
if (ETAD.isConsumed) {
484+
if (OriginallyConsumed) {
460485
// open_existential_addr projects a borrowed address into the
461486
// existential box. Since the callee consumes the generic value, we
462487
// must pass in a copy.
@@ -474,19 +499,39 @@ void ExistentialTransform::populateThunkBody() {
474499
// If the operand is not object type, we need an explicit load.
475500
SILValue OrigValue = OrigOperand;
476501
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+
}
479511
}
512+
480513
// OpenExistentialRef forwards ownership, so it does the right thing
481514
// regardless of whether the argument is borrowed or consumed.
482515
archetypeValue =
483516
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.
484520
if (!OrigOperand->getType().isObject()) {
485521
SILValue ASI = Builder.createAllocStack(Loc, OpenedSILType);
486-
Builder.createStore(Loc, archetypeValue, ASI,
487-
StoreOwnershipQualifier::Unqualified);
522+
Builder.emitStoreValueOperation(Loc, archetypeValue, ASI,
523+
StoreOwnershipQualifier::Init);
488524
Temps.push_back({ASI, SILValue()});
489525
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+
}
490535
}
491536
ApplyArgs.push_back(archetypeValue);
492537
break;
@@ -572,11 +617,14 @@ void ExistentialTransform::populateThunkBody() {
572617
// copy_addr %valAdr to %temp // <== Temp CopyAddr
573618
// apply(%temp) // <== Temp is consumed by the apply
574619
//
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:
576622
// destroy_addr %consumedExistential : $*Protocol
577623
// dealloc_stack %temp : $*T
624+
//
625+
// Otherwise, if we had an object, we just emit a destroy_value.
578626
if (Temp.DestroyValue)
579-
Builder.createDestroyAddr(cleanupLoc, Temp.DestroyValue);
627+
Builder.emitDestroyOperation(cleanupLoc, Temp.DestroyValue);
580628
if (Temp.DeallocStackEntry)
581629
Builder.createDeallocStack(cleanupLoc, Temp.DeallocStackEntry);
582630
}

0 commit comments

Comments
 (0)