Skip to content

Commit 6017ef5

Browse files
committed
[ownership] Begin updating existential specializer.
I can not update all of the tests until I fix SILCombine and we move the ownership lowering to right before the existential specializer (the sil tests depend on the former and the swift tests depend on both). But this at least begins updating the tests and ensures that the updates do not break the pass when we run it on non-ossa code.
1 parent 9f8ba0a commit 6017ef5

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)