mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[ownership] Loosen restrictions around what we specialize and add generic specialization tests behind a flag.
The idea is that this will let me remove these assertions that were in place to make sure we were really conservative around specializing ownership code. For me to remove that I need to be able to actually test out this code (since I think there are some code paths where this will trigger in other parts of the compiler now). So to work out the kinks, I added a flag that allows for the generic specializer to process ownership code and translated most of the .sil test cases/fixed any bugs that I found. This hopefully will expose anything that is missing. NOTE: I have not enabled the generic specializer running in ownership in the pipeline. This is just a step in that direction by adding tests/etc.
This commit is contained in:
@@ -21,9 +21,11 @@
|
||||
#ifndef SWIFT_SIL_APPLYSITE_H
|
||||
#define SWIFT_SIL_APPLYSITE_H
|
||||
|
||||
#include "swift/SIL/SILArgument.h"
|
||||
#include "swift/SIL/SILBasicBlock.h"
|
||||
#include "swift/SIL/SILInstruction.h"
|
||||
#include "swift/SIL/SILFunction.h"
|
||||
#include "swift/SIL/SILInstruction.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
namespace swift {
|
||||
|
||||
@@ -502,6 +504,34 @@ public:
|
||||
return getSubstCalleeConv().hasIndirectSILResults();
|
||||
}
|
||||
|
||||
/// If our apply site has a single direct result SILValue, return that
|
||||
/// SILValue. Return SILValue() otherwise.
|
||||
///
|
||||
/// This means that:
|
||||
///
|
||||
/// 1. If we have an ApplyInst, we just visit the apply.
|
||||
/// 2. If we have a TryApplyInst, we visit the first argument of the normal
|
||||
/// block.
|
||||
/// 3. If we have a BeginApplyInst, we return SILValue() since the begin_apply
|
||||
/// yields values instead of returning them. A returned value should only
|
||||
/// be valid after a full apply site has completely finished executing.
|
||||
SILValue getSingleDirectResult() const {
|
||||
switch (getKind()) {
|
||||
case FullApplySiteKind::ApplyInst:
|
||||
return SILValue(cast<ApplyInst>(getInstruction()));
|
||||
case FullApplySiteKind::BeginApplyInst: {
|
||||
return SILValue();
|
||||
}
|
||||
case FullApplySiteKind::TryApplyInst: {
|
||||
auto *normalBlock = cast<TryApplyInst>(getInstruction())->getNormalBB();
|
||||
assert(normalBlock->getNumArguments() == 1 &&
|
||||
"Expected try apply to have a single result");
|
||||
return normalBlock->getArgument(0);
|
||||
}
|
||||
}
|
||||
llvm_unreachable("Covered switch isn't covered?!");
|
||||
}
|
||||
|
||||
unsigned getNumIndirectSILResults() const {
|
||||
return getSubstCalleeConv().getNumIndirectSILResults();
|
||||
}
|
||||
|
||||
@@ -726,7 +726,8 @@ public:
|
||||
}
|
||||
|
||||
LoadBorrowInst *createLoadBorrow(SILLocation Loc, SILValue LV) {
|
||||
assert(isLoadableOrOpaque(LV->getType()));
|
||||
assert(isLoadableOrOpaque(LV->getType()) &&
|
||||
!LV->getType().isTrivial(getFunction()));
|
||||
return insert(new (getModule())
|
||||
LoadBorrowInst(getSILDebugLocation(Loc), LV));
|
||||
}
|
||||
@@ -737,11 +738,19 @@ public:
|
||||
BeginBorrowInst(getSILDebugLocation(Loc), LV));
|
||||
}
|
||||
|
||||
/// Convenience function for creating a load_borrow on non-trivial values and
|
||||
/// load [trivial] on trivial values. Becomes load unqualified in non-ossa
|
||||
/// functions.
|
||||
SILValue emitLoadBorrowOperation(SILLocation loc, SILValue v) {
|
||||
if (!hasOwnership()) {
|
||||
return emitLoadValueOperation(loc, v,
|
||||
LoadOwnershipQualifier::Unqualified);
|
||||
}
|
||||
|
||||
if (v->getType().isTrivial(getFunction())) {
|
||||
return emitLoadValueOperation(loc, v, LoadOwnershipQualifier::Trivial);
|
||||
}
|
||||
|
||||
return createLoadBorrow(loc, v);
|
||||
}
|
||||
|
||||
@@ -877,6 +886,33 @@ public:
|
||||
StoreBorrowInst(getSILDebugLocation(Loc), Src, DestAddr));
|
||||
}
|
||||
|
||||
/// A helper function for emitting store_borrow in operations where one must
|
||||
/// handle both ossa and non-ossa code.
|
||||
///
|
||||
/// In words:
|
||||
///
|
||||
/// * If the function does not have ownership, this just emits an unqualified
|
||||
/// store.
|
||||
///
|
||||
/// * If the function has ownership, but the type is trivial, use store
|
||||
/// [trivial].
|
||||
///
|
||||
/// * Otherwise, emit an actual store_borrow.
|
||||
void emitStoreBorrowOperation(SILLocation loc, SILValue src,
|
||||
SILValue destAddr) {
|
||||
if (!hasOwnership()) {
|
||||
return emitStoreValueOperation(loc, src, destAddr,
|
||||
StoreOwnershipQualifier::Unqualified);
|
||||
}
|
||||
|
||||
if (src->getType().isTrivial(getFunction())) {
|
||||
return emitStoreValueOperation(loc, src, destAddr,
|
||||
StoreOwnershipQualifier::Trivial);
|
||||
}
|
||||
|
||||
createStoreBorrow(loc, src, destAddr);
|
||||
}
|
||||
|
||||
MarkUninitializedInst *
|
||||
createMarkUninitialized(SILLocation Loc, SILValue src,
|
||||
MarkUninitializedInst::Kind k) {
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
|
||||
using namespace swift;
|
||||
|
||||
// For testing during bring up.
|
||||
static llvm::cl::opt<bool> EnableGenericSpecializerWithOwnership(
|
||||
"sil-generic-specializer-enable-ownership", llvm::cl::init(false));
|
||||
|
||||
namespace {
|
||||
|
||||
class GenericSpecializer : public SILFunctionTransform {
|
||||
@@ -39,7 +43,7 @@ class GenericSpecializer : public SILFunctionTransform {
|
||||
SILFunction &F = *getFunction();
|
||||
|
||||
// TODO: We should be able to handle ownership.
|
||||
if (F.hasOwnership())
|
||||
if (F.hasOwnership() && !EnableGenericSpecializerWithOwnership)
|
||||
return;
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << "***** GenericSpecializer on function:"
|
||||
|
||||
@@ -79,8 +79,9 @@ void GenericCloner::populateCloned() {
|
||||
|
||||
auto createAllocStack = [&]() {
|
||||
// We need an alloc_stack as a replacement for the indirect parameter.
|
||||
assert(mappedType.isAddress());
|
||||
mappedType = mappedType.getObjectType();
|
||||
if (mappedType.isAddress()) {
|
||||
mappedType = mappedType.getObjectType();
|
||||
}
|
||||
auto AllocStackLoc = RegularLocation::getAutoGeneratedLocation();
|
||||
ASI = getBuilder().createAllocStack(AllocStackLoc, mappedType);
|
||||
AllocStacks.push_back(ASI);
|
||||
@@ -106,24 +107,36 @@ void GenericCloner::populateCloned() {
|
||||
// Handle arguments for formal parameters.
|
||||
unsigned paramIdx = ArgIdx - origConv.getSILArgIndexOfFirstParam();
|
||||
if (ReInfo.isParamConverted(paramIdx)) {
|
||||
// Store the new direct parameter to the alloc_stack.
|
||||
createAllocStack();
|
||||
assert(mappedType.isAddress());
|
||||
mappedType = mappedType.getObjectType();
|
||||
auto *NewArg = ClonedEntryBB->createFunctionArgument(
|
||||
mappedType, OrigArg->getDecl());
|
||||
getBuilder().createStore(Loc, NewArg, ASI,
|
||||
StoreOwnershipQualifier::Unqualified);
|
||||
|
||||
// Try to create a new debug_value from an existing debug_value_addr.
|
||||
// Try to create a new debug_value from an existing debug_value_addr
|
||||
// for the argument. We do this before storing to ensure that when we
|
||||
// are cloning code in ossa the argument has not been consumed by the
|
||||
// store below.
|
||||
for (Operand *ArgUse : OrigArg->getUses()) {
|
||||
if (auto *DVAI = dyn_cast<DebugValueAddrInst>(ArgUse->getUser())) {
|
||||
auto *oldScope = getBuilder().getCurrentDebugScope();
|
||||
getBuilder().setCurrentDebugScope(
|
||||
remapScope(DVAI->getDebugScope()));
|
||||
getBuilder().createDebugValue(DVAI->getLoc(), NewArg,
|
||||
*DVAI->getVarInfo());
|
||||
getBuilder().setCurrentDebugScope(nullptr);
|
||||
getBuilder().setCurrentDebugScope(oldScope);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the new direct parameter to an alloc_stack.
|
||||
createAllocStack();
|
||||
if (!NewArg->getArgumentConvention().isGuaranteedConvention()) {
|
||||
getBuilder().emitStoreValueOperation(Loc, NewArg, ASI,
|
||||
StoreOwnershipQualifier::Init);
|
||||
} else {
|
||||
getBuilder().emitStoreBorrowOperation(Loc, NewArg, ASI);
|
||||
}
|
||||
|
||||
entryArgs.push_back(ASI);
|
||||
return true;
|
||||
}
|
||||
@@ -150,9 +163,9 @@ void GenericCloner::visitTerminator(SILBasicBlock *BB) {
|
||||
if (ReturnValueAddr) {
|
||||
// The result is converted from indirect to direct. We have to load the
|
||||
// returned value from the alloc_stack.
|
||||
ReturnValue =
|
||||
getBuilder().createLoad(ReturnValueAddr->getLoc(), ReturnValueAddr,
|
||||
LoadOwnershipQualifier::Unqualified);
|
||||
ReturnValue = getBuilder().emitLoadValueOperation(
|
||||
ReturnValueAddr->getLoc(), ReturnValueAddr,
|
||||
LoadOwnershipQualifier::Take);
|
||||
}
|
||||
for (AllocStackInst *ASI : reverse(AllocStacks)) {
|
||||
getBuilder().createDeallocStack(ASI->getLoc(), ASI);
|
||||
|
||||
@@ -1870,7 +1870,6 @@ SILFunction *GenericFuncSpecializer::tryCreateSpecialization() {
|
||||
SpecializedF->getGenericEnvironment()) ||
|
||||
(!SpecializedF->getLoweredFunctionType()->isPolymorphic() &&
|
||||
!SpecializedF->getGenericEnvironment()));
|
||||
assert(!SpecializedF->hasOwnership());
|
||||
// Store the meta-information about how this specialization was created.
|
||||
auto *Caller = ReInfo.getApply() ? ReInfo.getApply().getFunction() : nullptr;
|
||||
SubstitutionMap Subs = Caller ? ReInfo.getApply().getSubstitutionMap()
|
||||
@@ -1907,10 +1906,18 @@ static void fixUsedVoidType(SILValue VoidVal, SILLocation Loc,
|
||||
}
|
||||
|
||||
/// Prepare call arguments. Perform re-abstraction if required.
|
||||
static void prepareCallArguments(ApplySite AI, SILBuilder &Builder,
|
||||
const ReabstractionInfo &ReInfo,
|
||||
SmallVectorImpl<SILValue> &Arguments,
|
||||
SILValue &StoreResultTo) {
|
||||
///
|
||||
/// \p ArgAtIndexNeedsEndBorrow after return contains indices of arguments that
|
||||
/// need end borrow. The reason why we are doing this in a separate array is
|
||||
/// that we are going to eventually need to pass off Arguments to SILBuilder
|
||||
/// which will want an ArrayRef<SILValue>() so using a composite type here would
|
||||
/// force us to do some sort of conversion then.
|
||||
static void
|
||||
prepareCallArguments(ApplySite AI, SILBuilder &Builder,
|
||||
const ReabstractionInfo &ReInfo,
|
||||
SmallVectorImpl<SILValue> &Arguments,
|
||||
SmallVectorImpl<unsigned> &ArgAtIndexNeedsEndBorrow,
|
||||
SILValue &StoreResultTo) {
|
||||
/// SIL function conventions for the original apply site with substitutions.
|
||||
SILLocation Loc = AI.getLoc();
|
||||
auto substConv = AI.getSubstCalleeConv();
|
||||
@@ -1938,8 +1945,16 @@ static void prepareCallArguments(ApplySite AI, SILBuilder &Builder,
|
||||
if (ReInfo.isParamConverted(paramIdx)) {
|
||||
// An argument is converted from indirect to direct. Instead of the
|
||||
// address we pass the loaded value.
|
||||
SILValue Val = Builder.createLoad(
|
||||
Loc, Op.get(), LoadOwnershipQualifier::Unqualified);
|
||||
auto argConv = substConv.getSILArgumentConvention(ArgIdx);
|
||||
SILValue Val;
|
||||
if (!argConv.isGuaranteedConvention() || isa<PartialApplyInst>(AI)) {
|
||||
Val = Builder.emitLoadValueOperation(Loc, Op.get(),
|
||||
LoadOwnershipQualifier::Take);
|
||||
} else {
|
||||
Val = Builder.emitLoadBorrowOperation(Loc, Op.get());
|
||||
if (Val.getOwnershipKind() == ValueOwnershipKind::Guaranteed)
|
||||
ArgAtIndexNeedsEndBorrow.push_back(Arguments.size());
|
||||
}
|
||||
Arguments.push_back(Val);
|
||||
return true;
|
||||
}
|
||||
@@ -1953,6 +1968,16 @@ static void prepareCallArguments(ApplySite AI, SILBuilder &Builder,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cleanupCallArguments(SILBuilder &builder, SILLocation loc,
|
||||
ArrayRef<SILValue> values,
|
||||
ArrayRef<unsigned> valueIndicesThatNeedEndBorrow) {
|
||||
for (int index : valueIndicesThatNeedEndBorrow) {
|
||||
auto *lbi = cast<LoadBorrowInst>(values[index]);
|
||||
builder.createEndBorrow(loc, lbi);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new apply based on an old one, but with a different
|
||||
/// function being applied.
|
||||
static ApplySite replaceWithSpecializedCallee(ApplySite applySite,
|
||||
@@ -1961,14 +1986,20 @@ static ApplySite replaceWithSpecializedCallee(ApplySite applySite,
|
||||
SILBuilderWithScope builder(applySite.getInstruction());
|
||||
SILLocation loc = applySite.getLoc();
|
||||
SmallVector<SILValue, 4> arguments;
|
||||
SmallVector<unsigned, 4> argsNeedingEndBorrow;
|
||||
SILValue resultOut;
|
||||
|
||||
prepareCallArguments(applySite, builder, reInfo, arguments, resultOut);
|
||||
prepareCallArguments(applySite, builder, reInfo, arguments,
|
||||
argsNeedingEndBorrow, resultOut);
|
||||
|
||||
// Create a substituted callee type.
|
||||
//
|
||||
// NOTE: We do not perform this substitution if we are promoting a full apply
|
||||
// site callee of a partial apply.
|
||||
auto canFnTy = callee->getType().castTo<SILFunctionType>();
|
||||
SubstitutionMap subs;
|
||||
if (reInfo.getSpecializedType()->isPolymorphic()) {
|
||||
if (reInfo.getSpecializedType()->isPolymorphic() &&
|
||||
canFnTy->isPolymorphic()) {
|
||||
subs = reInfo.getCallerParamSubstitutionMap();
|
||||
subs = SubstitutionMap::get(canFnTy->getSubstGenericSignature(), subs);
|
||||
}
|
||||
@@ -1983,6 +2014,13 @@ static ApplySite replaceWithSpecializedCallee(ApplySite applySite,
|
||||
auto *tai = cast<TryApplyInst>(applySite);
|
||||
SILBasicBlock *resultBlock = tai->getNormalBB();
|
||||
assert(resultBlock->getSinglePredecessorBlock() == tai->getParent());
|
||||
// First insert the cleanups for our arguments int he appropriate spot.
|
||||
FullApplySite(tai).insertAfterFullEvaluation(
|
||||
[&](SILBasicBlock::iterator insertPt) {
|
||||
SILBuilderWithScope argBuilder(insertPt);
|
||||
cleanupCallArguments(argBuilder, loc, arguments,
|
||||
argsNeedingEndBorrow);
|
||||
});
|
||||
auto *newTAI = builder.createTryApply(loc, callee, subs, arguments,
|
||||
resultBlock, tai->getErrorBB());
|
||||
if (resultOut) {
|
||||
@@ -1995,23 +2033,28 @@ static ApplySite replaceWithSpecializedCallee(ApplySite applySite,
|
||||
SILArgument *arg = resultBlock->replacePhiArgument(
|
||||
0, resultOut->getType().getObjectType(), ValueOwnershipKind::Owned);
|
||||
// Store the direct result to the original result address.
|
||||
builder.createStore(loc, arg, resultOut,
|
||||
StoreOwnershipQualifier::Unqualified);
|
||||
builder.emitStoreValueOperation(loc, arg, resultOut,
|
||||
StoreOwnershipQualifier::Init);
|
||||
}
|
||||
return newTAI;
|
||||
}
|
||||
case ApplySiteKind::ApplyInst: {
|
||||
auto *ai = cast<ApplyInst>(applySite);
|
||||
FullApplySite(ai).insertAfterFullEvaluation(
|
||||
[&](SILBasicBlock::iterator insertPt) {
|
||||
SILBuilderWithScope argBuilder(insertPt);
|
||||
cleanupCallArguments(argBuilder, loc, arguments,
|
||||
argsNeedingEndBorrow);
|
||||
});
|
||||
auto *newAI =
|
||||
builder.createApply(loc, callee, subs, arguments, ai->isNonThrowing());
|
||||
if (resultOut) {
|
||||
assert(substConv.useLoweredAddresses());
|
||||
if (!calleeSILSubstFnTy.isNoReturnFunction(
|
||||
builder.getModule(), builder.getTypeExpansionContext())) {
|
||||
// Store the direct result to the original result address.
|
||||
fixUsedVoidType(ai, loc, builder);
|
||||
builder.createStore(loc, newAI, resultOut,
|
||||
StoreOwnershipQualifier::Unqualified);
|
||||
builder.emitStoreValueOperation(loc, newAI, resultOut,
|
||||
StoreOwnershipQualifier::Init);
|
||||
} else {
|
||||
builder.createUnreachable(loc);
|
||||
// unreachable should be the terminator instruction.
|
||||
@@ -2027,6 +2070,12 @@ static ApplySite replaceWithSpecializedCallee(ApplySite applySite,
|
||||
case ApplySiteKind::BeginApplyInst: {
|
||||
auto *bai = cast<BeginApplyInst>(applySite);
|
||||
assert(!resultOut);
|
||||
FullApplySite(bai).insertAfterFullEvaluation(
|
||||
[&](SILBasicBlock::iterator insertPt) {
|
||||
SILBuilderWithScope argBuilder(insertPt);
|
||||
cleanupCallArguments(argBuilder, loc, arguments,
|
||||
argsNeedingEndBorrow);
|
||||
});
|
||||
auto *newBAI = builder.createBeginApply(loc, callee, subs, arguments,
|
||||
bai->isNonThrowing());
|
||||
bai->replaceAllUsesPairwiseWith(newBAI);
|
||||
@@ -2038,7 +2087,11 @@ static ApplySite replaceWithSpecializedCallee(ApplySite applySite,
|
||||
loc, callee, subs, arguments,
|
||||
pai->getType().getAs<SILFunctionType>()->getCalleeConvention(),
|
||||
pai->isOnStack());
|
||||
// When we have a partial apply, we should always perform a load [take].
|
||||
pai->replaceAllUsesWith(newPAI);
|
||||
assert(llvm::none_of(arguments,
|
||||
[](SILValue v) { return isa<LoadBorrowInst>(v); }) &&
|
||||
"Partial apply consumes all of its parameters?!");
|
||||
return newPAI;
|
||||
}
|
||||
}
|
||||
@@ -2096,8 +2149,9 @@ public:
|
||||
SILFunction *createThunk();
|
||||
|
||||
protected:
|
||||
SILValue createReabstractionThunkApply(SILBuilder &Builder);
|
||||
SILArgument *convertReabstractionThunkArguments(SILBuilder &Builder);
|
||||
FullApplySite createReabstractionThunkApply(SILBuilder &Builder);
|
||||
SILArgument *convertReabstractionThunkArguments(
|
||||
SILBuilder &Builder, SmallVectorImpl<unsigned> &ArgsNeedingEndBorrows);
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -2132,30 +2186,46 @@ SILFunction *ReabstractionThunkGenerator::createThunk() {
|
||||
SpecArg->getDecl());
|
||||
Arguments.push_back(NewArg);
|
||||
}
|
||||
SILValue ReturnValue = createReabstractionThunkApply(Builder);
|
||||
FullApplySite ApplySite = createReabstractionThunkApply(Builder);
|
||||
SILValue ReturnValue = ApplySite.getSingleDirectResult();
|
||||
assert(ReturnValue && "getSingleDirectResult out of sync with ApplySite?!");
|
||||
Builder.createReturn(Loc, ReturnValue);
|
||||
|
||||
return Thunk;
|
||||
}
|
||||
// Handle lowered addresses.
|
||||
SILArgument *ReturnValueAddr = convertReabstractionThunkArguments(Builder);
|
||||
SmallVector<unsigned, 4> ArgsThatNeedEndBorrow;
|
||||
SILArgument *ReturnValueAddr =
|
||||
convertReabstractionThunkArguments(Builder, ArgsThatNeedEndBorrow);
|
||||
|
||||
SILValue ReturnValue = createReabstractionThunkApply(Builder);
|
||||
FullApplySite ApplySite = createReabstractionThunkApply(Builder);
|
||||
|
||||
SILValue ReturnValue = ApplySite.getSingleDirectResult();
|
||||
assert(ReturnValue && "getSingleDirectResult out of sync with ApplySite?!");
|
||||
|
||||
if (ReturnValueAddr) {
|
||||
// Need to store the direct results to the original indirect address.
|
||||
Builder.createStore(Loc, ReturnValue, ReturnValueAddr,
|
||||
StoreOwnershipQualifier::Unqualified);
|
||||
Builder.emitStoreValueOperation(Loc, ReturnValue, ReturnValueAddr,
|
||||
StoreOwnershipQualifier::Init);
|
||||
SILType VoidTy = OrigPAI->getSubstCalleeType()->getDirectFormalResultsType(
|
||||
M, Builder.getTypeExpansionContext());
|
||||
assert(VoidTy.isVoid());
|
||||
ReturnValue = Builder.createTuple(Loc, VoidTy, {});
|
||||
}
|
||||
Builder.createReturn(Loc, ReturnValue);
|
||||
|
||||
// Now that we have finished constructing our CFG (note the return above),
|
||||
// insert any compensating end borrows that we need.
|
||||
ApplySite.insertAfterFullEvaluation([&](SILBasicBlock::iterator insertPt) {
|
||||
SILBuilderWithScope argBuilder(insertPt);
|
||||
cleanupCallArguments(argBuilder, Loc, Arguments, ArgsThatNeedEndBorrow);
|
||||
});
|
||||
|
||||
return Thunk;
|
||||
}
|
||||
|
||||
/// Create a call to a reabstraction thunk. Return the call's direct result.
|
||||
SILValue ReabstractionThunkGenerator::createReabstractionThunkApply(
|
||||
FullApplySite ReabstractionThunkGenerator::createReabstractionThunkApply(
|
||||
SILBuilder &Builder) {
|
||||
SILFunction *Thunk = &Builder.getFunction();
|
||||
auto *FRI = Builder.createFunctionRef(Loc, SpecializedFunc);
|
||||
@@ -2167,19 +2237,20 @@ SILValue ReabstractionThunkGenerator::createReabstractionThunkApply(
|
||||
// Create the logic for calling a throwing function.
|
||||
SILBasicBlock *NormalBB = Thunk->createBasicBlock();
|
||||
SILBasicBlock *ErrorBB = Thunk->createBasicBlock();
|
||||
Builder.createTryApply(Loc, FRI, Subs, Arguments, NormalBB, ErrorBB);
|
||||
auto *TAI =
|
||||
Builder.createTryApply(Loc, FRI, Subs, Arguments, NormalBB, ErrorBB);
|
||||
auto *ErrorVal = ErrorBB->createPhiArgument(
|
||||
SpecializedFunc->mapTypeIntoContext(
|
||||
specConv.getSILErrorType(Builder.getTypeExpansionContext())),
|
||||
ValueOwnershipKind::Owned);
|
||||
Builder.setInsertionPoint(ErrorBB);
|
||||
Builder.createThrow(Loc, ErrorVal);
|
||||
SILValue ReturnValue = NormalBB->createPhiArgument(
|
||||
NormalBB->createPhiArgument(
|
||||
SpecializedFunc->mapTypeIntoContext(
|
||||
specConv.getSILResultType(Builder.getTypeExpansionContext())),
|
||||
ValueOwnershipKind::Owned);
|
||||
Builder.setInsertionPoint(NormalBB);
|
||||
return ReturnValue;
|
||||
return FullApplySite(TAI);
|
||||
}
|
||||
|
||||
/// Create SIL arguments for a reabstraction thunk with lowered addresses. This
|
||||
@@ -2189,7 +2260,7 @@ SILValue ReabstractionThunkGenerator::createReabstractionThunkApply(
|
||||
/// FIXME: Remove this if we don't need to create reabstraction thunks after
|
||||
/// address lowering.
|
||||
SILArgument *ReabstractionThunkGenerator::convertReabstractionThunkArguments(
|
||||
SILBuilder &Builder) {
|
||||
SILBuilder &Builder, SmallVectorImpl<unsigned> &ArgsThatNeedEndBorrow) {
|
||||
SILFunction *Thunk = &Builder.getFunction();
|
||||
CanSILFunctionType SpecType = SpecializedFunc->getLoweredFunctionType();
|
||||
CanSILFunctionType SubstType = ReInfo.getSubstitutedType();
|
||||
@@ -2251,11 +2322,18 @@ SILArgument *ReabstractionThunkGenerator::convertReabstractionThunkArguments(
|
||||
Builder.getTypeExpansionContext()));
|
||||
assert(ParamTy.isAddress());
|
||||
SILArgument *SpecArg = *SpecArgIter++;
|
||||
SILArgument *NewArg =
|
||||
SILFunctionArgument *NewArg =
|
||||
EntryBB->createFunctionArgument(ParamTy, SpecArg->getDecl());
|
||||
auto *ArgVal =
|
||||
Builder.createLoad(Loc, NewArg, LoadOwnershipQualifier::Unqualified);
|
||||
Arguments.push_back(ArgVal);
|
||||
if (!NewArg->getArgumentConvention().isGuaranteedConvention()) {
|
||||
SILValue argVal = Builder.emitLoadValueOperation(
|
||||
Loc, NewArg, LoadOwnershipQualifier::Take);
|
||||
Arguments.push_back(argVal);
|
||||
} else {
|
||||
SILValue argVal = Builder.emitLoadBorrowOperation(Loc, NewArg);
|
||||
if (argVal.getOwnershipKind() == ValueOwnershipKind::Guaranteed)
|
||||
ArgsThatNeedEndBorrow.push_back(Arguments.size());
|
||||
Arguments.push_back(argVal);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Simply clone unconverted direct or indirect parameters.
|
||||
@@ -2358,15 +2436,6 @@ void swift::trySpecializeApplyOfGeneric(
|
||||
if (shouldNotSpecialize(RefF, F))
|
||||
return;
|
||||
|
||||
// If our callee has ownership, do not specialize for now. This should only
|
||||
// occur with transparent referenced functions.
|
||||
//
|
||||
// FIXME: Support this.
|
||||
if (RefF->hasOwnership()) {
|
||||
assert(RefF->isTransparent());
|
||||
return;
|
||||
}
|
||||
|
||||
// If the caller and callee are both fragile, preserve the fragility when
|
||||
// cloning the callee. Otherwise, strip it off so that we can optimize
|
||||
// the body more.
|
||||
@@ -2408,11 +2477,24 @@ void swift::trySpecializeApplyOfGeneric(
|
||||
// this case we can just skip the existing re-abstraction.
|
||||
// 3) For all other cases we need to create a new re-abstraction thunk.
|
||||
needAdaptUsers = true;
|
||||
for (Operand *Use : PAI->getUses()) {
|
||||
SmallVector<Operand *, 4> worklist(PAI->getUses());
|
||||
while (!worklist.empty()) {
|
||||
auto *Use = worklist.pop_back_val();
|
||||
|
||||
SILInstruction *User = Use->getUser();
|
||||
|
||||
// Look through copy_value.
|
||||
if (auto *cvi = dyn_cast<CopyValueInst>(User)) {
|
||||
llvm::copy(cvi->getUses(), std::back_inserter(worklist));
|
||||
continue;
|
||||
}
|
||||
// Ignore destroy_value.
|
||||
if (isa<DestroyValueInst>(User))
|
||||
continue;
|
||||
// Ignore older ref count instructions.
|
||||
if (isa<RefCountingInst>(User))
|
||||
continue;
|
||||
if (User->isDebugInstruction())
|
||||
if (isIncidentalUse(User))
|
||||
continue;
|
||||
|
||||
auto FAS = FullApplySite::isa(User);
|
||||
@@ -2443,7 +2525,6 @@ void swift::trySpecializeApplyOfGeneric(
|
||||
<< SpecializedF->getName() << "\n"
|
||||
<< "Specialized function type: "
|
||||
<< SpecializedF->getLoweredFunctionType() << "\n");
|
||||
assert(!SpecializedF->hasOwnership());
|
||||
NewFunctions.push_back(SpecializedF);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// RUN: %target-swift-frontend -module-name specialize_anyobject -O -sil-inline-threshold 0 -emit-sil -primary-file %s | %FileCheck %s
|
||||
|
||||
// rdar://problem/20338028
|
||||
|
||||
53
test/SILOptimizer/specialize_default_witness_ossa.sil
Normal file
53
test/SILOptimizer/specialize_default_witness_ossa.sil
Normal file
@@ -0,0 +1,53 @@
|
||||
// RUN: %target-sil-opt -enable-sil-verify-all -sil-generic-specializer-enable-ownership -generic-specializer %s | %FileCheck %s
|
||||
|
||||
sil_stage canonical
|
||||
|
||||
import Builtin
|
||||
import Swift
|
||||
|
||||
public protocol ResilientProtocol {
|
||||
func defaultA()
|
||||
func defaultB()
|
||||
}
|
||||
|
||||
struct ConformingStruct : ResilientProtocol {
|
||||
func defaultA()
|
||||
func defaultB()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil shared [ossa] @$s8defaultA4main16ConformingStructV_Tg5
|
||||
// CHECK: bb0(%0 : $ConformingStruct):
|
||||
// CHECK: [[FN:%.*]] = function_ref @$s8defaultB4main16ConformingStructV_Tg5
|
||||
// CHECK: [[RESULT:%.*]] = apply [[FN]]
|
||||
// CHECK: return [[RESULT]]
|
||||
|
||||
sil [ossa] @defaultA : $@convention(witness_method: ResilientProtocol) <Self where Self : ResilientProtocol> (@in_guaranteed Self) -> () {
|
||||
bb0(%0 : $*Self):
|
||||
%fn = function_ref @defaultB : $@convention(witness_method: ResilientProtocol) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
|
||||
%result = apply %fn<Self>(%0) : $@convention(witness_method: ResilientProtocol) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
|
||||
return %result : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil shared [ossa] @$s8defaultB4main16ConformingStructV_Tg5
|
||||
// CHECK: bb0(%0 : $ConformingStruct):
|
||||
// CHECK: [[RESULT:%.*]] = tuple ()
|
||||
// CHECK: return [[RESULT]]
|
||||
|
||||
sil [ossa] @defaultB : $@convention(witness_method: ResilientProtocol) <Self where Self : ResilientProtocol> (@in_guaranteed Self) -> () {
|
||||
bb0(%0 : $*Self):
|
||||
%result = tuple ()
|
||||
return %result : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden [ossa] @test_specialize_default_witness_method
|
||||
// CHECK: bb0(%0 : $*ConformingStruct):
|
||||
// CHECK: [[FN:%.*]] = function_ref @$s8defaultA4main16ConformingStructV_Tg5
|
||||
// CHECK: [[RESULT:%.*]] = apply [[FN]]
|
||||
// CHECK: return [[RESULT]]
|
||||
|
||||
sil hidden [ossa] @test_specialize_default_witness_method : $@convention(thin) (@in_guaranteed ConformingStruct) -> () {
|
||||
bb0(%0 : $*ConformingStruct):
|
||||
%fn = function_ref @defaultA : $@convention(witness_method: ResilientProtocol) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
|
||||
%result = apply %fn<ConformingStruct>(%0) : $@convention(witness_method: ResilientProtocol) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
|
||||
return %result : $()
|
||||
}
|
||||
101
test/SILOptimizer/specialize_default_witness_resilience_ossa.sil
Normal file
101
test/SILOptimizer/specialize_default_witness_resilience_ossa.sil
Normal file
@@ -0,0 +1,101 @@
|
||||
// RUN: %target-sil-opt -enable-library-evolution -enable-sil-verify-all -generic-specializer -sil-generic-specializer-enable-ownership %s | %FileCheck %s
|
||||
|
||||
sil_stage canonical
|
||||
|
||||
import Builtin
|
||||
import Swift
|
||||
|
||||
public protocol ResilientProtocol {
|
||||
func defaultA()
|
||||
func defaultB()
|
||||
}
|
||||
|
||||
public struct ConformingStruct : ResilientProtocol {
|
||||
public func defaultA()
|
||||
public func defaultB()
|
||||
}
|
||||
|
||||
class Klass {}
|
||||
|
||||
// Used to make sure we also handle non-trivial structs correctly.
|
||||
public struct ConformingNonTrivialStruct : ResilientProtocol {
|
||||
var k: Klass
|
||||
|
||||
public func defaultA()
|
||||
public func defaultB()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil shared [ossa] @$s8defaultA4main16ConformingStructV_Tg5
|
||||
// CHECK: bb0(%0 : $ConformingStruct):
|
||||
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $ConformingStruct
|
||||
// CHECK-NEXT: store %0 to [trivial] [[TMP]] : $*ConformingStruct
|
||||
// CHECK: [[FN:%.*]] = function_ref @$s8defaultB4main16ConformingStructV_Tg5
|
||||
// CHECK-NEXT: [[LOAD:%.*]] = load [trivial] [[TMP]] : $*ConformingStruct
|
||||
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]([[LOAD]])
|
||||
// CHECK-NEXT: dealloc_stack [[TMP]] : $*ConformingStruct
|
||||
// CHECK } // end sil function 's8defaultA4main16ConformingStructV_Tg5'
|
||||
|
||||
// CHECK-LABEL: sil shared [ossa] @$s8defaultA4main26ConformingNonTrivialStructV_Tg5
|
||||
// CHECK: bb0(%0 : @guaranteed $ConformingNonTrivialStruct):
|
||||
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $ConformingNonTrivialStruct
|
||||
// CHECK-NEXT: store_borrow %0 to [[TMP]] : $*ConformingNonTrivialStruct
|
||||
// CHECK: [[FN:%.*]] = function_ref @$s8defaultB4main26ConformingNonTrivialStructV_Tg5
|
||||
// CHECK-NEXT: [[LOAD:%.*]] = load_borrow [[TMP]] : $*ConformingNonTrivialStruct
|
||||
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]([[LOAD]])
|
||||
// CHECK: dealloc_stack [[TMP]] : $*ConformingNonTrivialStruct
|
||||
// CHECK } // end sil function 's8defaultA4main16ConformingNonTrivialStructV_Tg5'
|
||||
|
||||
sil [ossa] @defaultA : $@convention(witness_method: ResilientProtocol) <Self where Self : ResilientProtocol> (@in_guaranteed Self) -> () {
|
||||
bb0(%0 : $*Self):
|
||||
%fn = function_ref @defaultB : $@convention(witness_method: ResilientProtocol) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
|
||||
%result = apply %fn<Self>(%0) : $@convention(witness_method: ResilientProtocol) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
|
||||
return %result : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil shared [ossa] @$s8defaultB4main16ConformingStructV_Tg5 :
|
||||
// CHECK: bb0(%0 : $ConformingStruct):
|
||||
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $ConformingStruct
|
||||
// CHECK-NEXT: store %0 to [trivial] [[TMP]] : $*ConformingStruct
|
||||
// CHECK: dealloc_stack [[TMP]] : $*ConformingStruct
|
||||
// CHECK: } // end sil function '$s8defaultB4main16ConformingStructV_Tg5'
|
||||
|
||||
// CHECK-LABEL: sil shared [ossa] @$s8defaultB4main26ConformingNonTrivialStructV_Tg5 :
|
||||
// CHECK: bb0(%0 : @guaranteed $ConformingNonTrivialStruct):
|
||||
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $ConformingNonTrivialStruct
|
||||
// CHECK-NEXT: store_borrow %0 to [[TMP]] : $*ConformingNonTrivialStruct
|
||||
// CHECK: dealloc_stack [[TMP]] : $*ConformingNonTrivialStruct
|
||||
// CHECK: } // end sil function '$s8defaultB4main26ConformingNonTrivialStructV_Tg5'
|
||||
|
||||
sil [ossa] @defaultB : $@convention(witness_method: ResilientProtocol) <Self where Self : ResilientProtocol> (@in_guaranteed Self) -> () {
|
||||
bb0(%0 : $*Self):
|
||||
%result = tuple ()
|
||||
return %result : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden [ossa] @test_specialize_default_witness_method
|
||||
// CHECK: bb0(%0 : $*ConformingStruct):
|
||||
// CHECK: [[FN:%.*]] = function_ref @$s8defaultA4main16ConformingStructV_Tg5
|
||||
// CHECK-NEXT: [[VALUE:%.*]] = load [trivial] %0 : $*ConformingStruct
|
||||
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]([[VALUE]])
|
||||
// CHECK-NEXT: return [[RESULT]]
|
||||
|
||||
sil hidden [ossa] @test_specialize_default_witness_method : $@convention(thin) (@in_guaranteed ConformingStruct) -> () {
|
||||
bb0(%0 : $*ConformingStruct):
|
||||
%fn = function_ref @defaultA : $@convention(witness_method: ResilientProtocol) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
|
||||
%result = apply %fn<ConformingStruct>(%0) : $@convention(witness_method: ResilientProtocol) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
|
||||
return %result : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden [ossa] @test_specialize_default_witness_method_nontrivial
|
||||
// CHECK: bb0(%0 : $*ConformingNonTrivialStruct):
|
||||
// CHECK: [[FN:%.*]] = function_ref @$s8defaultA4main26ConformingNonTrivialStructV_Tg5
|
||||
// CHECK-NEXT: [[VALUE:%.*]] = load_borrow %0 : $*ConformingNonTrivialStruct
|
||||
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]([[VALUE]])
|
||||
// CHECK: } // end sil function 'test_specialize_default_witness_method_nontrivial'
|
||||
|
||||
sil hidden [ossa] @test_specialize_default_witness_method_nontrivial : $@convention(thin) (@in_guaranteed ConformingNonTrivialStruct) -> () {
|
||||
bb0(%0 : $*ConformingNonTrivialStruct):
|
||||
%fn = function_ref @defaultA : $@convention(witness_method: ResilientProtocol) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
|
||||
%result = apply %fn<ConformingNonTrivialStruct>(%0) : $@convention(witness_method: ResilientProtocol) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
|
||||
return %result : $()
|
||||
}
|
||||
71
test/SILOptimizer/specialize_inherited_ossa.sil
Normal file
71
test/SILOptimizer/specialize_inherited_ossa.sil
Normal file
@@ -0,0 +1,71 @@
|
||||
// RUN: %target-sil-opt -enable-sil-verify-all -generic-specializer -module-name inherit -sil-generic-specializer-enable-ownership %s | %FileCheck %s
|
||||
|
||||
import Builtin
|
||||
import Swift
|
||||
|
||||
class MMFont {
|
||||
}
|
||||
|
||||
class Klass {}
|
||||
|
||||
struct MMStorage<Key : Hashable, Value> {
|
||||
var k: Klass = Klass()
|
||||
}
|
||||
|
||||
func ==(lhs: MMObject, rhs: MMObject) -> Bool
|
||||
|
||||
class MMObject : Hashable {
|
||||
func hash(into hasher: inout Hasher)
|
||||
}
|
||||
|
||||
class MMString : MMObject {
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @caller : $@convention(thin) (Int, Int, @owned MMString) -> @owned MMStorage<MMString, Optional<MMFont>> {
|
||||
sil [ossa] @caller : $@convention(thin) (Int, Int, @owned MMString) -> @owned MMStorage<MMString, Optional<MMFont>> {
|
||||
bb0(%0 : $Int, %1 : $Int, %2 : @owned $MMString):
|
||||
%3 = metatype $@thin MMStorage<MMString, Optional<MMFont>>.Type
|
||||
%13 = function_ref @ext_fn1 : $@convention(thin) (Int, @thin MMStorage<MMString, Optional<MMFont>>.Type) -> @owned MMStorage<MMString, Optional<MMFont>>
|
||||
%14 = apply %13(%0, %3) : $@convention(thin) (Int, @thin MMStorage<MMString, Optional<MMFont>>.Type) -> @owned MMStorage<MMString, Optional<MMFont>>
|
||||
%15 = copy_value %14 : $MMStorage<MMString, Optional<MMFont>>
|
||||
|
||||
// CHECK: [[STACK:%[0-9]+]] = alloc_stack $MMString
|
||||
%37 = alloc_stack $MMString
|
||||
store %2 to [init] %37 : $*MMString
|
||||
// CHECK: [[ID:%[0-9]+]] = function_ref @$s6callee7inherit8MMStringC_AB6MMFontCSgTg5 : $@convention(method) (@owned MMString, Int, @owned MMStorage<MMString, Optional<MMFont>>) -> Bool
|
||||
%34 = function_ref @callee : $@convention(method) <τ_0_0, τ_0_1 where τ_0_0 : Hashable> (@in τ_0_0, Int, @owned MMStorage<τ_0_0, τ_0_1>) -> Bool
|
||||
// CHECK: [[LOAD:%[0-9]+]] = load [take] [[STACK]]
|
||||
// CHECK: apply [[ID]]([[LOAD]], %1, %{{[0-9]+}}) : $@convention(method) (@owned MMString, Int, @owned MMStorage<MMString, Optional<MMFont>>) -> Bool
|
||||
%45 = apply %34<MMString, MMFont?>(%37, %1, %14) : $@convention(method) <τ_0_0, τ_0_1 where τ_0_0 : Hashable> (@in τ_0_0, Int, @owned MMStorage<τ_0_0, τ_0_1>) -> Bool
|
||||
dealloc_stack %37 : $*MMString
|
||||
|
||||
return %15 : $MMStorage<MMString, Optional<MMFont>>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @$s6callee7inherit8MMStringC_AB6MMFontCSgTg5 : $@convention(method) (@owned MMString, Int, @owned MMStorage<MMString, Optional<MMFont>>) -> Bool {
|
||||
// CHECK: [[META:%[0-9]+]] = metatype $@thick MMString.Type
|
||||
// CHECK: [[ID3:%[0-9]+]] = witness_method $MMString, #Equatable."==" :
|
||||
// CHECK: [[STACK2:%[0-9]+]] = alloc_stack $MMString
|
||||
// CHECK: [[STACK3:%[0-9]+]] = alloc_stack $MMString
|
||||
// CHECK: apply [[ID3]]<MMString>([[STACK2]], [[STACK3]], [[META]]) : $@convention(witness_method: Equatable) <τ_0_0 where τ_0_0 : Equatable> (@in τ_0_0, @in τ_0_0, @thick τ_0_0.Type) -> Bool
|
||||
|
||||
// CHECK-LABEL: @callee : $@convention(method) <Key, Value where Key : Hashable> (@in Key, Int, @owned MMStorage<Key, Value>) -> Bool {
|
||||
sil [noinline] [ossa] @callee : $@convention(method) <Key, Value where Key : Hashable> (@in Key, Int, @owned MMStorage<Key, Value>) -> Bool {
|
||||
bb0(%0 : $*Key, %1 : $Int, %2 : @owned $MMStorage<Key, Value>):
|
||||
%25 = metatype $@thick Key.Type
|
||||
// CHECK: [[ID2:%[0-9]+]] = witness_method $Key, #Equatable."==" :
|
||||
%26 = witness_method $Key, #Equatable."==" : $@convention(witness_method: Equatable) <τ_0_0 where τ_0_0 : Equatable> (@in τ_0_0, @in τ_0_0, @thick τ_0_0.Type) -> Bool
|
||||
%27 = alloc_stack $Key
|
||||
%33 = alloc_stack $Key
|
||||
copy_addr %0 to [initialization] %27 : $*Key
|
||||
copy_addr %0 to [initialization] %33 : $*Key
|
||||
// CHECK: apply [[ID2]]<Key>
|
||||
%35 = apply %26<Key>(%27, %33, %25) : $@convention(witness_method: Equatable) <τ_0_0 where τ_0_0 : Equatable> (@in τ_0_0, @in τ_0_0, @thick τ_0_0.Type) -> Bool
|
||||
dealloc_stack %33 : $*Key
|
||||
dealloc_stack %27 : $*Key
|
||||
destroy_value %2 : $MMStorage<Key, Value>
|
||||
destroy_addr %0 : $*Key
|
||||
return %35 : $Bool
|
||||
}
|
||||
|
||||
sil @ext_fn1 : $@convention(thin) (Int, @thin MMStorage<MMString, Optional<MMFont>>.Type) -> @owned MMStorage<MMString, Optional<MMFont>>
|
||||
45
test/SILOptimizer/specialize_opaque_ossa.sil
Normal file
45
test/SILOptimizer/specialize_opaque_ossa.sil
Normal file
@@ -0,0 +1,45 @@
|
||||
// RUN: %target-sil-opt -enable-sil-opaque-values -enable-sil-verify-all -generic-specializer -sil-generic-specializer-enable-ownership %s | %FileCheck %s
|
||||
|
||||
sil_stage canonical
|
||||
|
||||
import Builtin
|
||||
|
||||
// Test that foo is specialized on Builtin.Int64 and the copy_values and destroy_values are dropped.
|
||||
//
|
||||
// CHECK-LABEL: sil shared [ossa] @$s3fooBi64__Tg5 : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> () {
|
||||
// CHECK: bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64):
|
||||
// CHECK: [[F:%.*]] = function_ref @$s3fooBi64__Tg5 : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> ()
|
||||
// CHECK: %{{.*}} = apply [[F]](%0, %1) : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> ()
|
||||
// CHECK-NOT: copy_value
|
||||
// CHECK-NOT: destroy_value
|
||||
// CHECK: return %{{.*}} : $()
|
||||
// CHECK: } // end sil function '$s3fooBi64__Tg5'
|
||||
|
||||
// Test that foo when specialized on Builtin.NativeObject, keeps copy_value/etc.
|
||||
// CHECK-LABEL: sil shared [ossa] @$s3fooBo_Tg5 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () {
|
||||
// CHECK: copy_value
|
||||
// CHECK: apply
|
||||
// CHECK: destroy_value
|
||||
// CHECK: } // end sil function '$s3fooBo_Tg5'
|
||||
|
||||
sil hidden [ossa] @foo : $@convention(thin) <T> (@in T, @in T) -> () {
|
||||
bb0(%0 : @owned $T, %1 : @owned $T):
|
||||
%f = function_ref @foo : $@convention(thin) <τ_0_0> (@in τ_0_0, @in τ_0_0) -> ()
|
||||
%cp0 = copy_value %0 : $T
|
||||
%cp1 = copy_value %1 : $T
|
||||
%call = apply %f<T>(%cp0, %cp1) : $@convention(thin) <τ_0_0> (@in τ_0_0, @in τ_0_0) -> ()
|
||||
destroy_value %1 : $T
|
||||
destroy_value %0 : $T
|
||||
%10 = tuple ()
|
||||
return %10 : $()
|
||||
}
|
||||
|
||||
sil [ossa] @testSpecialize : $@convention(thin) (Builtin.Int64, @owned Builtin.NativeObject) -> () {
|
||||
bb0(%0 : $Builtin.Int64, %1 : @owned $Builtin.NativeObject):
|
||||
%f = function_ref @foo : $@convention(thin) <T> (@in T, @in T) -> ()
|
||||
%call = apply %f<Builtin.Int64>(%0, %0) : $@convention(thin) <τ_0_0> (@in τ_0_0, @in τ_0_0) -> ()
|
||||
%1a = copy_value %1 : $Builtin.NativeObject
|
||||
%call2 = apply %f<Builtin.NativeObject>(%1, %1a) : $@convention(thin) <τ_0_0> (@in τ_0_0, @in τ_0_0) -> ()
|
||||
%999 = tuple ()
|
||||
return %999 : $()
|
||||
}
|
||||
74
test/SILOptimizer/specialize_opaque_result_types_ossa.sil
Normal file
74
test/SILOptimizer/specialize_opaque_result_types_ossa.sil
Normal file
@@ -0,0 +1,74 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %target-swift-frontend -disable-availability-checking %S/Inputs/opaque_result_types.swift -module-name External -emit-module -emit-module-path %t/External.swiftmodule
|
||||
// RUN: %target-sil-opt -I %t -enable-sil-verify-all %s -generic-specializer -sil-generic-specializer-enable-ownership | %FileCheck %s
|
||||
|
||||
// REQUIRES: CPU=x86_64
|
||||
|
||||
import Builtin
|
||||
import Swift
|
||||
import SwiftShims
|
||||
import External
|
||||
|
||||
sil_stage canonical
|
||||
|
||||
sil @project : $@convention(thin) (@in Test) -> @out IndexingIterator<Test>
|
||||
|
||||
sil shared [ossa] @test : $@convention(thin) (@owned Test) -> () {
|
||||
bb0(%0 : @owned $Test):
|
||||
%3 = alloc_stack $Test
|
||||
store %0 to [init] %3 : $*Test
|
||||
%5 = alloc_stack $IndexingIterator<Test>
|
||||
%6 = function_ref @project : $@convention(thin) (@in Test) -> @out IndexingIterator<Test>
|
||||
%7 = apply %6(%5, %3) : $@convention(thin) (@in Test) -> @out IndexingIterator<Test>
|
||||
%44 = alloc_stack $Optional<Int64>
|
||||
// function_ref protocol witness for IteratorProtocol.next() in conformance IndexingIterator<A>
|
||||
%45 = function_ref @next : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : Collection> (@inout IndexingIterator<τ_0_0>) -> @out Optional<τ_0_0.Element>
|
||||
%46 = apply %45<Test>(%44, %5) : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : Collection> (@inout IndexingIterator<τ_0_0>) -> @out Optional<τ_0_0.Element>
|
||||
destroy_addr %44: $*Optional<Int64>
|
||||
dealloc_stack %44 : $*Optional<Int64>
|
||||
destroy_addr %5: $*IndexingIterator<Test>
|
||||
dealloc_stack %5 : $*IndexingIterator<Test>
|
||||
dealloc_stack %3 : $*Test
|
||||
%41 = tuple ()
|
||||
return %41 : $()
|
||||
}
|
||||
// CHECK-LABEL: sil shared [ossa] @$s4next8External4TestV_Tg5 : $@convention(witness_method: IteratorProtocol) (@inout IndexingIterator<Test>) -> Optional<Int64>
|
||||
// CHECK: bb0(%0 : $*IndexingIterator<Test>):
|
||||
// CHECK: alloc_stack $Optional<Int64>
|
||||
// CHECK: ([[RES:%.*]], %{{.*}}) = begin_apply {{.*}}<Test>({{.*}}) : $@yield_once @convention(witness_method: Collection) <τ_0_0 where τ_0_0 : Collection> (@in_guaranteed τ_0_0.Index, @in_guaranteed τ_0_0) -> @yields @in_guaranteed τ_0_0.Element
|
||||
// CHECK: [[DEST:%.*]] = init_enum_data_addr %1 : $*Optional<Int64>, #Optional.some!enumelt
|
||||
// CHECK: copy_addr [[RES]] to [initialization] {{.*}} : $*Int64
|
||||
// CHECK: } // end sil function '$s4next8External4TestV_Tg5'
|
||||
sil [ossa] @next : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : Collection> (@inout IndexingIterator<τ_0_0>) -> @out Optional<τ_0_0.Element> {
|
||||
bb0(%0 : $*Optional<τ_0_0.Element>, %1 : $*IndexingIterator<τ_0_0>):
|
||||
%2 = metatype $@thick τ_0_0.Index.Type
|
||||
%3 = struct_element_addr %1 : $*IndexingIterator<τ_0_0>, #IndexingIterator._position
|
||||
%4 = alloc_stack $τ_0_0.Index
|
||||
copy_addr %3 to [initialization] %4 : $*τ_0_0.Index
|
||||
%6 = struct_element_addr %1 : $*IndexingIterator<τ_0_0>, #IndexingIterator._elements
|
||||
%20 = alloc_stack $τ_0_0
|
||||
copy_addr %6 to [initialization] %20 : $*τ_0_0
|
||||
%22 = alloc_stack $τ_0_0.Index
|
||||
copy_addr %3 to [initialization] %22 : $*τ_0_0.Index
|
||||
%24 = alloc_stack $τ_0_0
|
||||
copy_addr [take] %20 to [initialization] %24 : $*τ_0_0
|
||||
%26 = witness_method $τ_0_0, #Collection.subscript!read : <Self where Self : Collection> (Self) -> (Self.Index) -> () : $@yield_once @convention(witness_method: Collection) <τ_0_0 where τ_0_0 : Collection> (@in_guaranteed τ_0_0.Index, @in_guaranteed τ_0_0) -> @yields @in_guaranteed τ_0_0.Element
|
||||
|
||||
// The specialized begin apply %26<Test> has a result type of t_0_0.Element
|
||||
// which works out to be an opaque result type whose underlying type is Int64.
|
||||
// Make sure that the specialized code handles this correctly.
|
||||
(%27, %28) = begin_apply %26<τ_0_0>(%22, %24) : $@yield_once @convention(witness_method: Collection) <τ_0_0 where τ_0_0 : Collection> (@in_guaranteed τ_0_0.Index, @in_guaranteed τ_0_0) -> @yields @in_guaranteed τ_0_0.Element
|
||||
|
||||
%29 = init_enum_data_addr %0 : $*Optional<τ_0_0.Element>, #Optional.some!enumelt
|
||||
copy_addr %27 to [initialization] %29 : $*τ_0_0.Element
|
||||
end_apply %28
|
||||
destroy_addr %22 : $*τ_0_0.Index
|
||||
destroy_addr %24 : $*τ_0_0
|
||||
dealloc_stack %24 : $*τ_0_0
|
||||
dealloc_stack %22 : $*τ_0_0.Index
|
||||
dealloc_stack %20 : $*τ_0_0
|
||||
destroy_addr %4 : $*τ_0_0.Index
|
||||
dealloc_stack %4 : $*τ_0_0.Index
|
||||
%41 = tuple ()
|
||||
return %41 : $()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
132
test/SILOptimizer/specialize_reabstraction_ossa.sil
Normal file
132
test/SILOptimizer/specialize_reabstraction_ossa.sil
Normal file
@@ -0,0 +1,132 @@
|
||||
// RUN: %target-sil-opt -enable-sil-verify-all -generic-specializer -sil-generic-specializer-enable-ownership %s | %FileCheck %s
|
||||
|
||||
sil_stage canonical
|
||||
|
||||
import Builtin
|
||||
import Swift
|
||||
|
||||
public protocol RefProto {
|
||||
associatedtype T
|
||||
var me: Ref<Self.T> { get }
|
||||
}
|
||||
|
||||
public final class Ref<T> : RefProto {
|
||||
public final var me: Ref<T> { get }
|
||||
deinit
|
||||
init()
|
||||
}
|
||||
|
||||
extension RefProto {
|
||||
public func merge<U>(other: Ref<U>) -> Ref<(Self.T, U)>
|
||||
}
|
||||
|
||||
public protocol ValProto {
|
||||
associatedtype T
|
||||
var me: Val<Self.T> { get }
|
||||
}
|
||||
|
||||
extension ValProto {
|
||||
public func merge<U>(other: Val<U>) -> Val<(Self.T, U)>
|
||||
}
|
||||
|
||||
public struct Val<T> : ValProto {
|
||||
public var me: Val<T> { get }
|
||||
init()
|
||||
}
|
||||
|
||||
sil @coerce : $@convention(thin) <T, U, V> (@owned @callee_owned (@owned Ref<T>) -> @owned @callee_owned (@owned Ref<U>) -> @owned Ref<V>) -> @owned @callee_owned (Val<U>) -> Val<V>
|
||||
|
||||
sil [ossa] @merge : $@convention(method) <Self where Self : RefProto><U> (@owned Ref<U>, @in_guaranteed Self) -> @owned Ref<(Self.T, U)> {
|
||||
bb0(%0 : @owned $Ref<U>, %1 : $*Self):
|
||||
%2 = alloc_ref $Ref<(Self.T, U)>
|
||||
destroy_value %0 : $Ref<U>
|
||||
return %2 : $Ref<(Self.T, U)>
|
||||
}
|
||||
|
||||
sil [ossa] @merge_curried : $@convention(thin) <Self where Self : RefProto><U> (@in Self) -> @owned @callee_owned (@owned Ref<U>) -> @owned Ref<(Self.T, U)> {
|
||||
bb0(%0 : $*Self):
|
||||
%1 = function_ref @merge : $@convention(method) <τ_0_0 where τ_0_0 : RefProto><τ_1_0> (@owned Ref<τ_1_0>, @in_guaranteed τ_0_0) -> @owned Ref<(τ_0_0.T, τ_1_0)>
|
||||
%2 = partial_apply %1<Self, U>(%0) : $@convention(method) <τ_0_0 where τ_0_0 : RefProto><τ_1_0> (@owned Ref<τ_1_0>, @in_guaranteed τ_0_0) -> @owned Ref<(τ_0_0.T, τ_1_0)>
|
||||
return %2 : $@callee_owned (@owned Ref<U>) -> @owned Ref<(Self.T, U)>
|
||||
}
|
||||
|
||||
sil [ossa] [reabstraction_thunk] @reabstract : $@convention(thin) <Self where Self : ValProto><U> (@owned Ref<Self.T>, @owned @callee_owned (@in Ref<Self.T>) -> @owned @callee_owned (@owned Ref<U>) -> @owned Ref<(Self.T, U)>) -> @owned @callee_owned (@owned Ref<U>) -> @owned Ref<(Self.T, U)> {
|
||||
bb0(%0 : @owned $Ref<Self.T>, %1 : @owned $@callee_owned (@in Ref<Self.T>) -> @owned @callee_owned (@owned Ref<U>) -> @owned Ref<(Self.T, U)>):
|
||||
%2 = alloc_stack $Ref<Self.T>
|
||||
store %0 to [init] %2 : $*Ref<Self.T>
|
||||
%4 = apply %1(%2) : $@callee_owned (@in Ref<Self.T>) -> @owned @callee_owned (@owned Ref<U>) -> @owned Ref<(Self.T, U)>
|
||||
dealloc_stack %2 : $*Ref<Self.T>
|
||||
return %4 : $@callee_owned (@owned Ref<U>) -> @owned Ref<(Self.T, U)>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @test
|
||||
sil [ossa] @test : $@convention(thin) (Val<Bool>, Val<Int>) -> Val<(Bool, Int)> {
|
||||
// CHECK: bb0
|
||||
bb0(%0 : $Val<Bool>, %1 : $Val<Int>):
|
||||
// CHECK: [[COERCE:%.*]] = function_ref @coerce
|
||||
%2 = function_ref @coerce : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2> (@owned @callee_owned (@owned Ref<τ_0_0>) -> @owned @callee_owned (@owned Ref<τ_0_1>) -> @owned Ref<τ_0_2>) -> @owned @callee_owned (Val<τ_0_1>) -> Val<τ_0_2>
|
||||
// CHECK: [[MERGE:%.*]] = function_ref @$s13merge_curried4main3RefCySbG_SiTg5
|
||||
%3 = function_ref @merge_curried : $@convention(thin) <τ_0_0 where τ_0_0 : RefProto><τ_1_0> (@in τ_0_0) -> @owned @callee_owned (@owned Ref<τ_1_0>) -> @owned Ref<(τ_0_0.T, τ_1_0)>
|
||||
// CHECK: [[PARTIAL:%.*]] = partial_apply [[MERGE]]()
|
||||
%4 = partial_apply %3<Ref<Bool>, Int>() : $@convention(thin) <τ_0_0 where τ_0_0 : RefProto><τ_1_0> (@in τ_0_0) -> @owned @callee_owned (@owned Ref<τ_1_0>) -> @owned Ref<(τ_0_0.T, τ_1_0)>
|
||||
// CHECK-NOT: function_ref @reabstract
|
||||
%5 = function_ref @reabstract : $@convention(thin) <τ_0_0 where τ_0_0 : ValProto><τ_1_0> (@owned Ref<τ_0_0.T>, @owned @callee_owned (@in Ref<τ_0_0.T>) -> @owned @callee_owned (@owned Ref<τ_1_0>) -> @owned Ref<(τ_0_0.T, τ_1_0)>) -> @owned @callee_owned (@owned Ref<τ_1_0>) -> @owned Ref<(τ_0_0.T, τ_1_0)>
|
||||
// CHECK-NOT: partial_apply
|
||||
%6 = partial_apply %5<Val<Bool>, Int>(%4) : $@convention(thin) <τ_0_0 where τ_0_0 : ValProto><τ_1_0> (@owned Ref<τ_0_0.T>, @owned @callee_owned (@in Ref<τ_0_0.T>) -> @owned @callee_owned (@owned Ref<τ_1_0>) -> @owned Ref<(τ_0_0.T, τ_1_0)>) -> @owned @callee_owned (@owned Ref<τ_1_0>) -> @owned Ref<(τ_0_0.T, τ_1_0)>
|
||||
// CHECK: apply [[COERCE]]<Bool, Int, (Bool, Int)>([[PARTIAL]])
|
||||
%7 = apply %2<Bool, Int, (Bool, Int)>(%6) : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2> (@owned @callee_owned (@owned Ref<τ_0_0>) -> @owned @callee_owned (@owned Ref<τ_0_1>) -> @owned Ref<τ_0_2>) -> @owned @callee_owned (Val<τ_0_1>) -> Val<τ_0_2>
|
||||
%8 = apply %7(%1) : $@callee_owned (Val<Int>) -> Val<(Bool, Int)>
|
||||
return %8 : $Val<(Bool, Int)>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil shared [ossa] @$s9coroutineSb_Tg5 : $@yield_once @convention(thin) (Bool) -> @yields @inout Bool {
|
||||
// CHECK: bb0(%0 : $Bool):
|
||||
// CHECK-NEXT: [[TEMP:%.*]] = alloc_stack $Bool
|
||||
// CHECK-NEXT: store %0 to [trivial] [[TEMP]] : $*Bool
|
||||
// CHECK-NEXT: yield [[TEMP]] : $*Bool, resume bb1, unwind bb2
|
||||
// CHECK: bb1:
|
||||
// CHECK-NEXT: destroy_addr [[TEMP]] : $*Bool
|
||||
// CHECK-NEXT: [[RV:%.*]] = tuple ()
|
||||
// CHECK-NEXT: dealloc_stack [[TEMP]] : $*Bool
|
||||
// CHECK-NEXT: return [[RV]] : $()
|
||||
// CHECK: bb2:
|
||||
// CHECK-NEXT: destroy_addr [[TEMP]] : $*Bool
|
||||
// CHECK-NEXT: dealloc_stack [[TEMP]] : $*Bool
|
||||
// CHECK-NEXT: unwind
|
||||
// CHECK-NEXT: }
|
||||
sil [ossa] @coroutine : $@yield_once @convention(thin) <T> (@in T) -> @yields @inout T {
|
||||
bb0(%0 : $*T):
|
||||
yield %0 : $*T, resume bb1, unwind bb2
|
||||
bb1:
|
||||
destroy_addr %0 : $*T
|
||||
%rv = tuple ()
|
||||
return %rv : $()
|
||||
bb2:
|
||||
destroy_addr %0 : $*T
|
||||
unwind
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @test_coroutine : $@convention(thin) (Bool) -> () {
|
||||
// CHECK: bb0(%0 : $Bool):
|
||||
// CHECK-NEXT: [[TEMP:%.*]] = alloc_stack $Bool
|
||||
// CHECK-NEXT: store %0 to [trivial] [[TEMP]] : $*Bool
|
||||
// CHECK-NEXT: // function_ref
|
||||
// CHECK-NEXT: [[CORO:%.*]] = function_ref @$s9coroutineSb_Tg5 : $@yield_once @convention(thin) (Bool) -> @yields @inout Bool
|
||||
// CHECK-NEXT: [[LOAD:%.*]] = load [trivial] [[TEMP]] : $*Bool
|
||||
// CHECK-NEXT: ([[ADDR:%.*]], [[TOKEN:%.*]]) = begin_apply [[CORO]]([[LOAD]])
|
||||
// CHECK-NEXT: end_apply [[TOKEN]]
|
||||
// CHECK-NEXT: dealloc_stack [[TEMP]] : $*Bool
|
||||
// CHECK-NEXT: [[RV:%.*]] = tuple ()
|
||||
// CHECK-NEXT: return [[RV]] : $()
|
||||
// CHECK-NEXT: }
|
||||
sil [ossa] @test_coroutine : $@convention(thin) (Bool) -> () {
|
||||
bb0(%0 : $Bool):
|
||||
%coro = function_ref @coroutine : $@yield_once @convention(thin) <T> (@in T) -> @yields @inout T
|
||||
%temp = alloc_stack $Bool
|
||||
store %0 to [trivial] %temp : $*Bool
|
||||
(%addr, %token) = begin_apply %coro<Bool>(%temp) : $@yield_once @convention(thin) <T> (@in T) -> @yields @inout T
|
||||
end_apply %token
|
||||
dealloc_stack %temp : $*Bool
|
||||
%rv = tuple ()
|
||||
return %rv : $()
|
||||
}
|
||||
150
test/SILOptimizer/specialize_recursive_generics_ossa.sil
Normal file
150
test/SILOptimizer/specialize_recursive_generics_ossa.sil
Normal file
@@ -0,0 +1,150 @@
|
||||
// RUN: %target-sil-opt -enable-sil-verify-all %s -generic-specializer -cse -sil-generic-specializer-enable-ownership | %FileCheck %s
|
||||
|
||||
// Check that SIL cloner can correctly handle specialization of recursive
|
||||
// functions with generic arguments.
|
||||
|
||||
sil_stage canonical
|
||||
|
||||
import Builtin
|
||||
import Swift
|
||||
|
||||
// Check that this recursive function is specialized only for Int32.
|
||||
// CHECK-LABEL: sil shared [noinline] [ossa] @$s62_TF29specialize_recursive_generics18recursive_genericsU__FQ_T_s5Int32V_Tg5
|
||||
// CHECK: function_ref @$s62_TF29specialize_recursive_generics18recursive_genericsU__FQ_T_s5Int32V_Tg5
|
||||
// CHECK: return
|
||||
sil [noinline] [ossa] @_TF29specialize_recursive_generics18recursive_genericsU__FQ_T_ : $@convention(thin) <T> (@in T) -> () {
|
||||
bb0(%0 : $*T):
|
||||
debug_value_addr %0 : $*T, let, name "t" // id: %1
|
||||
// function_ref specialize_recursive_generics.recursive_generics <A>(A) -> ()
|
||||
%2 = function_ref @_TF29specialize_recursive_generics18recursive_genericsU__FQ_T_ : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () // user: %5
|
||||
%3 = alloc_stack $T // users: %4, %5, %6
|
||||
copy_addr %0 to [initialization] %3 : $*T // id: %4
|
||||
%5 = apply %2<T>(%3) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
|
||||
dealloc_stack %3 : $*T // id: %6
|
||||
destroy_addr %0 : $*T // id: %7
|
||||
%8 = tuple () // user: %9
|
||||
return %8 : $() // id: %9
|
||||
}
|
||||
|
||||
// Check that this recursive function is specialized twice: for (Int, Double) and for (Double, Int).
|
||||
|
||||
// CHECK-LABEL: sil shared [noinline] [ossa] @$s97_TF29specialize_recursive_generics47recursive_generics_with_different_substitutionsU___FTQ_Q0__T_Sd_s5Int32VTg5
|
||||
// CHECK: function_ref @$s97_TF29specialize_recursive_generics47recursive_generics_with_different_substitutionsU___FTQ_Q0__T_s5Int32V_SdTg5
|
||||
// CHECK: return
|
||||
|
||||
// CHECK-LABEL: sil shared [noinline] [ossa] @$s97_TF29specialize_recursive_generics47recursive_generics_with_different_substitutionsU___FTQ_Q0__T_s5Int32V_SdTg5
|
||||
// CHECK: function_ref @$s97_TF29specialize_recursive_generics47recursive_generics_with_different_substitutionsU___FTQ_Q0__T_Sd_s5Int32VTg5
|
||||
// CHECK: return
|
||||
|
||||
|
||||
sil [noinline] [ossa] @_TF29specialize_recursive_generics47recursive_generics_with_different_substitutionsU___FTQ_Q0__T_ : $@convention(thin) <T, U> (@in T, @in U) -> () {
|
||||
bb0(%0 : $*T, %1 : $*U):
|
||||
debug_value_addr %0 : $*T, let, name "t" // id: %2
|
||||
debug_value_addr %1 : $*U, let, name "u" // id: %3
|
||||
// function_ref specialize_recursive_generics.recursive_generics_with_different_substitutions <A, B>(A, B) -> ()
|
||||
%4 = function_ref @_TF29specialize_recursive_generics47recursive_generics_with_different_substitutionsU___FTQ_Q0__T_ : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> () // user: %9
|
||||
%5 = alloc_stack $U // users: %6, %9, %11
|
||||
copy_addr %1 to [initialization] %5 : $*U // id: %6
|
||||
%7 = alloc_stack $T // users: %8, %9, %10
|
||||
copy_addr %0 to [initialization] %7 : $*T // id: %8
|
||||
%9 = apply %4<U, T>(%5, %7) : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
|
||||
dealloc_stack %7 : $*T // id: %10
|
||||
dealloc_stack %5 : $*U // id: %11
|
||||
destroy_addr %1 : $*U // id: %12
|
||||
destroy_addr %0 : $*T // id: %13
|
||||
%14 = tuple () // user: %15
|
||||
return %14 : $() // id: %15
|
||||
}
|
||||
|
||||
sil [ossa] @$s29specialize_recursive_generics05test_b1_C0yyF : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
// function_ref specialize_recursive_generics.recursive_generics <A>(A) -> ()
|
||||
%0 = function_ref @_TF29specialize_recursive_generics18recursive_genericsU__FQ_T_ : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () // user: %5
|
||||
%1 = integer_literal $Builtin.Int32, 3 // user: %2
|
||||
%2 = struct $Int32 (%1 : $Builtin.Int32) // user: %4
|
||||
%3 = alloc_stack $Int32 // users: %4, %5, %6
|
||||
store %2 to [trivial] %3 : $*Int32 // id: %4
|
||||
%5 = apply %0<Int32>(%3) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
|
||||
dealloc_stack %3 : $*Int32 // id: %6
|
||||
%7 = tuple () // user: %8
|
||||
return %7 : $() // id: %8
|
||||
}
|
||||
|
||||
sil [ossa] @$s29specialize_recursive_generics05test_b1_C29_with_different_substitutionsyyF : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
// function_ref specialize_recursive_generics.recursive_generics_with_different_substitutions <A, B>(A, B) -> ()
|
||||
%0 = function_ref @_TF29specialize_recursive_generics47recursive_generics_with_different_substitutionsU___FTQ_Q0__T_ : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> () // user: %10
|
||||
%1 = float_literal $Builtin.FPIEEE80, 0x3FFF999999999999999A // 1.20000000000000000004 // user: %2
|
||||
%2 = builtin "fptrunc_FPIEEE80_FPIEEE64"(%1 : $Builtin.FPIEEE80) : $Builtin.FPIEEE64 // user: %3
|
||||
%3 = struct $Double (%2 : $Builtin.FPIEEE64) // user: %5
|
||||
%4 = alloc_stack $Double // users: %5, %10, %12
|
||||
store %3 to [trivial] %4 : $*Double // id: %5
|
||||
%6 = integer_literal $Builtin.Int32, 1 // user: %7
|
||||
%7 = struct $Int32 (%6 : $Builtin.Int32) // user: %9
|
||||
%8 = alloc_stack $Int32 // users: %9, %10, %11
|
||||
store %7 to [trivial] %8 : $*Int32 // id: %9
|
||||
%10 = apply %0<Double, Int32>(%4, %8) : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
|
||||
dealloc_stack %8 : $*Int32 // id: %11
|
||||
dealloc_stack %4 : $*Double // id: %12
|
||||
%13 = tuple () // user: %14
|
||||
return %13 : $() // id: %14
|
||||
}
|
||||
|
||||
|
||||
public class C : P {}
|
||||
|
||||
public protocol P {}
|
||||
|
||||
sil hidden [ossa] [noinline] @helper : $@convention(thin) <T> (@in T, @in P) -> @owned Optional<C> {
|
||||
bb0(%0 : $*T, %1 : $*P):
|
||||
%4 = alloc_stack $P
|
||||
copy_addr %1 to [initialization] %4 : $*P
|
||||
%6 = alloc_stack $C
|
||||
checked_cast_addr_br take_always P in %4 : $*P to C in %6 : $*C, bb1, bb2
|
||||
bb1:
|
||||
%8 = load [take] %6 : $*C
|
||||
%9 = enum $Optional<C>, #Optional.some!enumelt, %8 : $C
|
||||
dealloc_stack %6 : $*C
|
||||
br bb3(%9 : $Optional<C>)
|
||||
|
||||
bb2:
|
||||
%12 = enum $Optional<C>, #Optional.none!enumelt
|
||||
dealloc_stack %6 : $*C
|
||||
br bb3(%12 : $Optional<C>)
|
||||
|
||||
bb3(%15 : @owned $Optional<C>):
|
||||
dealloc_stack %4 : $*P
|
||||
destroy_addr %1 : $*P
|
||||
destroy_addr %0 : $*T
|
||||
return %15 : $Optional<C>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil shared [ossa] @$s6lookup4main1CC_Tg5
|
||||
sil [ossa] @lookup : $@convention(method) <Self where Self : P> (@owned C, @in_guaranteed Self) -> @owned Optional<C> {
|
||||
bb0(%0 : @owned $C, %1 : $*Self):
|
||||
// CHECK: [[HELPER:%.*]] = function_ref @$s6helpers5Int32V_Tg5
|
||||
%4 = function_ref @helper : $@convention(thin) <τ_0_0> (@in τ_0_0, @in P) -> @owned Optional<C>
|
||||
%5 = integer_literal $Builtin.Int32, 1
|
||||
%6 = struct $Int32 (%5 : $Builtin.Int32)
|
||||
%7 = alloc_stack $Int32
|
||||
store %6 to [trivial] %7 : $*Int32
|
||||
%9 = alloc_stack $P
|
||||
%10 = init_existential_addr %9 : $*P, $Self
|
||||
copy_addr %1 to [initialization] %10 : $*Self
|
||||
// CHECK: apply [[HELPER]]
|
||||
// CHECK-NOT: apply [[HELPER]]
|
||||
%12 = apply %4<Int32>(%7, %9) : $@convention(thin) <τ_0_0> (@in τ_0_0, @in P) -> @owned Optional<C>
|
||||
destroy_value %12 : $Optional<C>
|
||||
dealloc_stack %9 : $*P
|
||||
dealloc_stack %7 : $*Int32
|
||||
// CHECK: [[LOOKUP:%.*]] = function_ref @$s6lookup4main1CC_Tg5
|
||||
%16 = function_ref @lookup : $@convention(method) <τ_0_0 where τ_0_0 : P> (@owned C, @in_guaranteed τ_0_0) -> @owned Optional<C>
|
||||
%17 = alloc_stack $C
|
||||
%0c = copy_value %0 : $C
|
||||
store %0c to [init] %17 : $*C
|
||||
// CHECK: apply [[LOOKUP]]
|
||||
%20 = apply %16<C>(%0, %17) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@owned C, @in_guaranteed τ_0_0) -> @owned Optional<C>
|
||||
destroy_addr %17 : $*C
|
||||
dealloc_stack %17 : $*C
|
||||
return %20 : $Optional<C>
|
||||
}
|
||||
Reference in New Issue
Block a user