Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@ class ExistentialSpecializer : public SILFunctionTransform {
return;
}

// FIXME: This pass should be able to support ownership.
if (F->hasOwnership())
return;

/// Get CallerAnalysis information handy.
CA = PM->getAnalysis<CallerAnalysis>();

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

// If the callee has ownership enabled, bail.
//
// FIXME: We should be able to handle callees that have ownership, but the
// pass has not been updated yet.
if (Callee->hasOwnership())
return false;

// Ignore generic functions. Generic functions should be fully specialized
// before attempting to introduce new generic parameters for existential
// arguments. Otherwise, there's no guarantee that the generic specializer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,13 @@ void ExistentialSpecializerCloner::cloneAndPopulateFunction() {
SILBuilderWithScope Builder(exitBB->getTerminator());
// A return location can't be used for a non-return instruction.
auto loc = RegularLocation::getAutoGeneratedLocation();
for (SILValue cleanupVal : CleanupValues)
Builder.createDestroyAddr(loc, cleanupVal);
for (SILValue cleanupVal : CleanupValues) {
if (cleanupVal.getOwnershipKind() == ValueOwnershipKind::Guaranteed) {
Builder.emitEndBorrowOperation(loc, cleanupVal);
} else {
Builder.emitDestroyOperation(loc, cleanupVal);
}
}

for (auto *ASI : llvm::reverse(AllocStackInsts))
Builder.createDeallocStack(loc, ASI);
Expand Down Expand Up @@ -180,10 +185,10 @@ void ExistentialSpecializerCloner::cloneArguments(
NewF.getLoweredType(NewF.mapTypeIntoContext(GenericParam));
GenericSILType = GenericSILType.getCategoryType(
ArgDesc.Arg->getType().getCategory());
auto *NewArg =
ClonedEntryBB->createFunctionArgument(GenericSILType, ArgDesc.Decl);
NewArg->setOwnershipKind(ValueOwnershipKind(
NewF, GenericSILType, ArgDesc.Arg->getArgumentConvention()));
auto *NewArg = ClonedEntryBB->createFunctionArgument(
GenericSILType, ArgDesc.Decl,
ValueOwnershipKind(NewF, GenericSILType,
ArgDesc.Arg->getArgumentConvention()));
// Determine the Conformances.
SILType ExistentialType = ArgDesc.Arg->getType().getObjectType();
CanType OpenedType = NewArg->getType().getASTType();
Expand Down Expand Up @@ -224,35 +229,54 @@ void ExistentialSpecializerCloner::cloneArguments(
}
case ExistentialRepresentation::Class: {
SILValue NewArgValue = NewArg;
bool origConsumed = EAD.isConsumed;

// Load our object if needed and if our original value was not consumed,
// make a copy in ossa. Do not perturb code-gen in non-ossa code though.
if (!NewArg->getType().isObject()) {
NewArgValue = NewFBuilder.createLoad(InsertLoc, NewArg,
LoadOwnershipQualifier::Unqualified);
auto qual = LoadOwnershipQualifier::Take;
if (NewFBuilder.hasOwnership() && !origConsumed) {
qual = LoadOwnershipQualifier::Copy;
}
NewArgValue =
NewFBuilder.emitLoadValueOperation(InsertLoc, NewArg, qual);
} else {
if (NewFBuilder.hasOwnership() && !origConsumed) {
NewArgValue = NewFBuilder.emitCopyValueOperation(InsertLoc, NewArg);
}
}

// FIXME_ownership: init_existential_ref always takes ownership of the
// incoming reference. If the argument convention is borrowed
// (!isConsumed), then we should create a copy_value here and add this new
// existential to the CleanupValues vector.

/// Simple case: Create an init_existential.
/// %5 = init_existential_ref %0 : $T : $T, $P
SILValue InitRef = NewFBuilder.createInitExistentialRef(
InsertLoc, ArgDesc.Arg->getType().getObjectType(),
NewArg->getType().getASTType(),
NewArgValue, Conformances);


// If we don't have an object and we are in ossa, the store will consume
// the InitRef.
if (!NewArg->getType().isObject()) {
auto alloc = NewFBuilder.createAllocStack(InsertLoc,
InitRef->getType());
NewFBuilder.createStore(InsertLoc, InitRef, alloc,
StoreOwnershipQualifier::Unqualified);
NewFBuilder.emitStoreValueOperation(InsertLoc, InitRef, alloc,
StoreOwnershipQualifier::Init);
InitRef = alloc;
AllocStackInsts.push_back(alloc);
} else {
// Otherwise in ossa, we need to add init existential ref as something
// to be cleaned up. In non-ossa, we do not insert the copies, so we do
// not need to do it then.
//
// TODO: This would be simpler if we had managed value/cleanup scopes.
if (NewFBuilder.hasOwnership() && !origConsumed) {
CleanupValues.push_back(InitRef);
}
}

entryArgs.push_back(InitRef);
break;
}

default: {
llvm_unreachable("Unhandled existential type in ExistentialTransform!");
break;
Expand Down Expand Up @@ -451,12 +475,13 @@ void ExistentialTransform::populateThunkBody() {
SILValue archetypeValue;
auto ExistentialRepr =
ArgDesc.Arg->getType().getPreferredExistentialRepresentation();
bool OriginallyConsumed = ETAD.isConsumed;
switch (ExistentialRepr) {
case ExistentialRepresentation::Opaque: {
archetypeValue = Builder.createOpenExistentialAddr(
Loc, OrigOperand, OpenedSILType, it->second.AccessType);
SILValue calleeArg = archetypeValue;
if (ETAD.isConsumed) {
if (OriginallyConsumed) {
// open_existential_addr projects a borrowed address into the
// existential box. Since the callee consumes the generic value, we
// must pass in a copy.
Expand All @@ -474,19 +499,39 @@ void ExistentialTransform::populateThunkBody() {
// If the operand is not object type, we need an explicit load.
SILValue OrigValue = OrigOperand;
if (!OrigOperand->getType().isObject()) {
OrigValue = Builder.createLoad(Loc, OrigValue,
LoadOwnershipQualifier::Unqualified);
auto qual = LoadOwnershipQualifier::Take;
if (Builder.hasOwnership() && !OriginallyConsumed) {
qual = LoadOwnershipQualifier::Copy;
}
OrigValue = Builder.emitLoadValueOperation(Loc, OrigValue, qual);
} else {
if (Builder.hasOwnership() && !OriginallyConsumed) {
OrigValue = Builder.emitCopyValueOperation(Loc, OrigValue);
}
}

// OpenExistentialRef forwards ownership, so it does the right thing
// regardless of whether the argument is borrowed or consumed.
archetypeValue =
Builder.createOpenExistentialRef(Loc, OrigValue, OpenedSILType);

// If we don't have an object and we are in ossa, the store will consume
// the open_existential_ref.
if (!OrigOperand->getType().isObject()) {
SILValue ASI = Builder.createAllocStack(Loc, OpenedSILType);
Builder.createStore(Loc, archetypeValue, ASI,
StoreOwnershipQualifier::Unqualified);
Builder.emitStoreValueOperation(Loc, archetypeValue, ASI,
StoreOwnershipQualifier::Init);
Temps.push_back({ASI, SILValue()});
archetypeValue = ASI;
} else {
// Otherwise in ossa, we need to add open_existential_ref as something
// to be cleaned up. In non-ossa, we do not insert the copies, so we
// do not need to do it then.
//
// TODO: This would be simpler if we had managed value/cleanup scopes.
if (Builder.hasOwnership() && !OriginallyConsumed) {
Temps.push_back({SILValue(), archetypeValue});
}
}
ApplyArgs.push_back(archetypeValue);
break;
Expand Down Expand Up @@ -572,11 +617,14 @@ void ExistentialTransform::populateThunkBody() {
// copy_addr %valAdr to %temp // <== Temp CopyAddr
// apply(%temp) // <== Temp is consumed by the apply
//
// Destroy the original argument and deallocation the temporary:
// Destroy the original argument and deallocation the temporary. If we have
// an address this becomes:
// destroy_addr %consumedExistential : $*Protocol
// dealloc_stack %temp : $*T
//
// Otherwise, if we had an object, we just emit a destroy_value.
if (Temp.DestroyValue)
Builder.createDestroyAddr(cleanupLoc, Temp.DestroyValue);
Builder.emitDestroyOperation(cleanupLoc, Temp.DestroyValue);
if (Temp.DeallocStackEntry)
Builder.createDeallocStack(cleanupLoc, Temp.DeallocStackEntry);
}
Expand Down
Loading