diff --git a/include/swift/AST/SILOptions.h b/include/swift/AST/SILOptions.h index 0b087b5a352a1..b49cf57ce62ba 100644 --- a/include/swift/AST/SILOptions.h +++ b/include/swift/AST/SILOptions.h @@ -50,6 +50,9 @@ class SILOptions { /// Remove all runtime assertions during optimizations. bool RemoveRuntimeAsserts = false; + /// Enable existential specializer optimization. + bool ExistentialSpecializer = false; + /// Controls whether the SIL ARC optimizations are run. bool EnableARCOptimizations = true; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index eb5d8162a93d9..79b665d8ead0a 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -424,6 +424,9 @@ def sil_unroll_threshold : Separate<["-"], "sil-unroll-threshold">, MetaVarName<"<250>">, HelpText<"Controls the aggressiveness of loop unrolling">; +def sil_existential_specializer : Flag<["-"], "sil-existential-specializer">, + HelpText<"Enable SIL existential specializer optimization">; + def sil_merge_partial_modules : Flag<["-"], "sil-merge-partial-modules">, HelpText<"Merge SIL from all partial swiftmodules into the final module">; diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index bb39e23bd3400..8f86eb04b21e8 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -160,6 +160,8 @@ PASS(DeadStoreElimination, "dead-store-elim", "Dead Store Elimination") PASS(GenericSpecializer, "generic-specializer", "Generic Function Specialization on Static Types") +PASS(ExistentialSpecializer, "existential-specializer", + "Existential Specializer") PASS(GlobalOpt, "global-opt", "SIL Global Optimization") PASS(GlobalPropertyOpt, "global-property-opt", diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 1094b58e86835..55aefdbc9f89f 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -608,6 +608,9 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args, return true; } } + if (Args.hasArg(OPT_sil_existential_specializer)) { + Opts.ExistentialSpecializer = true; + } if (const Arg *A = Args.getLastArg(OPT_num_threads)) { if (StringRef(A->getValue()).getAsInteger(10, Opts.NumThreads)) { Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/CMakeLists.txt b/lib/SILOptimizer/FunctionSignatureTransforms/CMakeLists.txt index 720da7401a09f..56ea0c3542ace 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/CMakeLists.txt +++ b/lib/SILOptimizer/FunctionSignatureTransforms/CMakeLists.txt @@ -3,4 +3,6 @@ silopt_register_sources( DeadArgumentTransform.cpp ArgumentExplosionTransform.cpp OwnedToGuaranteedTransform.cpp + ExistentialSpecializer.cpp + ExistentialTransform.cpp ) diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp new file mode 100644 index 0000000000000..4b2781add5b25 --- /dev/null +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp @@ -0,0 +1,358 @@ +//===--- ExistentialSpecializer.cpp - Specialization of functions -----===// +//===--- with existential arguments -----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Specialize functions with existential parameters to generic ones. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "sil-existential-specializer" +#include "ExistentialTransform.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/Existential.h" +#include "swift/SILOptimizer/Utils/Local.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" + +using namespace swift; + +STATISTIC(NumFunctionsWithExistentialArgsSpecialized, + "Number of functions with existential args specialized"); + +namespace { + +/// ExistentialSpecializer class. +class ExistentialSpecializer : public SILFunctionTransform { + + /// Determine if the current function is a target for existential + /// specialization of args. + bool canSpecializeExistentialArgsInFunction( + ApplySite &Apply, + llvm::SmallDenseMap + &ExistentialArgDescriptor); + + /// Can Callee be specialized? + bool canSpecializeCalleeFunction(ApplySite &Apply); + + /// Specialize existential args in function F. + void specializeExistentialArgsInAppliesWithinFunction(SILFunction &F); + + /// CallerAnalysis information. + CallerAnalysis *CA; + +public: + void run() override { + + auto *F = getFunction(); + + /// Don't optimize functions that should not be optimized. + if (!F->shouldOptimize() || !F->getModule().getOptions().ExistentialSpecializer) { + return; + } + + /// Get CallerAnalysis information handy. + CA = PM->getAnalysis(); + + /// Perform specialization. + specializeExistentialArgsInAppliesWithinFunction(*F); + } +}; +} // namespace + +/// Find concrete type from init_existential_refs/addrs. +static bool findConcreteTypeFromInitExistential(SILValue Arg, + CanType &ConcreteType) { + if (auto *IER = dyn_cast(Arg)) { + ConcreteType = IER->getFormalConcreteType(); + return true; + } else if (auto *IE = dyn_cast(Arg)) { + ConcreteType = IE->getFormalConcreteType(); + return true; + } + return false; +} + +/// Find the concrete type of the existential argument. Wrapper +/// for findInitExistential in Local.h/cpp. In future, this code +/// can move to Local.h/cpp. +static bool findConcreteType(ApplySite AI, int ArgIdx, CanType &ConcreteType) { + bool isCopied = false; + auto Arg = AI.getArgument(ArgIdx); + + /// Ignore any unconditional cast instructions. Is it Safe? Do we need to + /// also add UnconditionalCheckedCastAddrInst? TODO. + if (auto *Instance = dyn_cast(Arg)) { + Arg = Instance->getOperand(); + } + + /// Return init_existential if the Arg is global_addr. + if (auto *GAI = dyn_cast(Arg)) { + SILValue InitExistential = + findInitExistentialFromGlobalAddrAndApply(GAI, AI, ArgIdx); + /// If the Arg is already init_existential, return the concrete type. + if (findConcreteTypeFromInitExistential(InitExistential, ConcreteType)) { + return true; + } + } + + /// Handle AllocStack instruction separately. + if (auto *Instance = dyn_cast(Arg)) { + if (SILValue Src = + getAddressOfStackInit(Instance, AI.getInstruction(), isCopied)) { + Arg = Src; + } + } + + /// If the Arg is already init_existential after getAddressofStackInit + /// call, return the concrete type. + if (findConcreteTypeFromInitExistential(Arg, ConcreteType)) { + return true; + } + + /// Call findInitExistential and determine the init_existential. + ArchetypeType *OpenedArchetype = nullptr; + SILValue OpenedArchetypeDef; + auto FAS = FullApplySite::isa(AI.getInstruction()); + if (!FAS) + return false; + SILInstruction *InitExistential = + findInitExistential(FAS.getArgumentOperands()[ArgIdx], OpenedArchetype, + OpenedArchetypeDef, isCopied); + if (!InitExistential) { + LLVM_DEBUG(llvm::dbgs() << "ExistentialSpecializer Pass: Bail! Due to " + "findInitExistential\n";); + return false; + } + + /// Return the concrete type from init_existential returned from + /// findInitExistential. + if (auto *SingleVal = InitExistential->castToSingleValueInstruction()) { + if (findConcreteTypeFromInitExistential(SingleVal, ConcreteType)) { + return true; + } + } + return false; +} + +/// Check if the argument Arg is used in a destroy_use instruction. +static void +findIfCalleeUsesArgInDestroyUse(SILValue Arg, + ExistentialTransformArgumentDescriptor &ETAD) { + for (Operand *ArgUse : Arg->getUses()) { + auto *ArgUser = ArgUse->getUser(); + if (isa(ArgUser)) { + ETAD.DestroyAddrUse = true; + break; + } + } +} + +/// Check if any apply argument meets the criteria for existential +/// specialization. +bool ExistentialSpecializer::canSpecializeExistentialArgsInFunction( + ApplySite &Apply, + llvm::SmallDenseMap + &ExistentialArgDescriptor) { + auto *F = Apply.getReferencedFunction(); + auto Args = F->begin()->getFunctionArguments(); + bool returnFlag = false; + + /// Analyze the argument for protocol conformance. + for (unsigned Idx = 0, Num = Args.size(); Idx < Num; ++Idx) { + auto Arg = Args[Idx]; + auto ArgType = Arg->getType(); + auto SwiftArgType = ArgType.getASTType(); + + /// Checking for AnyObject and Any is added to ensure that we do not blow up + /// the code size by specializing to every type that conforms to Any or + /// AnyObject. In future, we may want to lift these two restrictions in a + /// controlled way. + if (!ArgType.isExistentialType() || ArgType.isAnyObject() || + SwiftArgType->isAny()) + continue; + + auto ExistentialRepr = + Arg->getType().getPreferredExistentialRepresentation(F->getModule()); + if (ExistentialRepr != ExistentialRepresentation::Opaque && + ExistentialRepr != ExistentialRepresentation::Class) + continue; + + /// Find the concrete type. + CanType ConcreteType; + if (!findConcreteType(Apply, Idx, ConcreteType)) { + LLVM_DEBUG( + llvm::dbgs() + << "ExistentialSpecializer Pass: Bail! Due to findConcreteType " + "for callee:" + << F->getName() << " in caller:" + << Apply.getInstruction()->getParent()->getParent()->getName() + << "\n";); + continue; + } + + /// Determine attributes of the existential addr arguments such as + /// destroy_use, immutable_access. + ExistentialTransformArgumentDescriptor ETAD; + ETAD.AccessType = + Apply.getOrigCalleeType()->getParameters()[Idx].isIndirectMutating() || + Apply.getOrigCalleeType()->getParameters()[Idx].isConsumed() + ? OpenedExistentialAccess::Mutable + : OpenedExistentialAccess::Immutable; + ETAD.DestroyAddrUse = false; + if ((Args[Idx]->getType().getPreferredExistentialRepresentation( + F->getModule())) != ExistentialRepresentation::Class) + findIfCalleeUsesArgInDestroyUse(Arg, ETAD); + + /// Save the attributes + ExistentialArgDescriptor[Idx] = ETAD; + LLVM_DEBUG(llvm::dbgs() + << "ExistentialSpecializer Pass:Function: " << F->getName() + << " Arg:" << Idx << "has a concrete type.\n"); + returnFlag |= true; + } + return returnFlag; +} + +/// Determine if this callee function can be specialized or not. +bool ExistentialSpecializer::canSpecializeCalleeFunction(ApplySite &Apply) { + + /// Do not handle partial applies. + if (isa(Apply.getInstruction())) { + return false; + } + + /// Determine the caller of the apply. + auto *Callee = Apply.getReferencedFunction(); + if (!Callee) + return false; + + /// Callee should be optimizable. + if (!Callee->shouldOptimize()) + return false; + + /// External function definitions. + if (!Callee->isDefinition()) + return false; + + /// Ignore functions with indirect results. + if (Callee->getConventions().hasIndirectSILResults()) + return false; + + /// Ignore error returning functions. + if (Callee->getLoweredFunctionType()->hasErrorResult()) + return false; + + /// Do not optimize always_inlinable functions. + if (Callee->getInlineStrategy() == Inline_t::AlwaysInline) + return false; + + /// Only choose a select few function representations for specialization. + switch (Callee->getRepresentation()) { + case SILFunctionTypeRepresentation::ObjCMethod: + case SILFunctionTypeRepresentation::Block: + return false; + default: break; + } + return true; +} + +/// Specialize existential args passed as arguments to callees. Iterate over all +/// call sites of the caller F and check for legality to apply existential +/// specialization. +void ExistentialSpecializer::specializeExistentialArgsInAppliesWithinFunction( + SILFunction &F) { + bool Changed = false; + for (auto &BB : F) { + for (auto It = BB.begin(), End = BB.end(); It != End; ++It) { + auto *I = &*It; + + /// Is it an apply site? + ApplySite Apply = ApplySite::isa(I); + if (!Apply) + continue; + + /// Can the callee be specialized? + if (!canSpecializeCalleeFunction(Apply)) { + LLVM_DEBUG(llvm::dbgs() << "ExistentialSpecializer Pass: Bail! Due to " + "canSpecializeCalleeFunction.\n"; + I->dump();); + continue; + } + + auto *Callee = Apply.getReferencedFunction(); + + /// Determine the arguments that can be specialized. + llvm::SmallDenseMap + ExistentialArgDescriptor; + if (!canSpecializeExistentialArgsInFunction(Apply, + ExistentialArgDescriptor)) { + LLVM_DEBUG(llvm::dbgs() + << "ExistentialSpecializer Pass: Bail! Due to " + "canSpecializeExistentialArgsInFunction in function: " + << Callee->getName() << " -> abort\n"); + continue; + } + + LLVM_DEBUG(llvm::dbgs() + << "ExistentialSpecializer Pass: Function::" + << Callee->getName() + << " has an existential argument and can be optimized " + "via ExistentialSpecializer\n"); + + /// Name Mangler for naming the protocol constrained generic method. + auto P = Demangle::SpecializationPass::FunctionSignatureOpts; + Mangle::FunctionSignatureSpecializationMangler Mangler( + P, Callee->isSerialized(), Callee); + + /// Save the arguments in a descriptor. + llvm::SpecificBumpPtrAllocator Allocator; + llvm::SmallVector ArgumentDescList; + auto Args = Callee->begin()->getFunctionArguments(); + for (unsigned i : indices(Args)) { + ArgumentDescList.emplace_back(Args[i], Allocator); + } + + /// This is the function to optimize for existential specilizer. + LLVM_DEBUG(llvm::dbgs() + << "*** Running ExistentialSpecializer Pass on function: " + << Callee->getName() << " ***\n"); + + /// Instantiate the ExistentialSpecializerTransform pass. + SILOptFunctionBuilder FuncBuilder(*this); + ExistentialTransform ET(FuncBuilder, Callee, Mangler, ArgumentDescList, + ExistentialArgDescriptor); + + /// Run the existential specializer pass. + Changed = ET.run(); + + if (Changed) { + /// Update statistics on the number of functions specialized. + ++NumFunctionsWithExistentialArgsSpecialized; + + /// Make sure the PM knows about the new specialized inner function. + addFunctionToPassManagerWorklist(ET.getExistentialSpecializedFunction(), + Callee); + + /// Invalidate analysis results of Callee. + PM->invalidateAnalysis(Callee, + SILAnalysis::InvalidationKind::Everything); + } + } + } + return; +} + +SILTransform *swift::createExistentialSpecializer() { + return new ExistentialSpecializer(); +} diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp new file mode 100644 index 0000000000000..6b3d91d43451f --- /dev/null +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp @@ -0,0 +1,559 @@ +//===------- ExistentialTransform.cpp - Transform Existential Args -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Transform existential parameters to generic ones. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "sil-existential-transform" +#include "ExistentialTransform.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericSignatureBuilder.h" +#include "swift/SIL/OptimizationRemark.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/TypeSubstCloner.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/CFG.h" +#include "swift/SILOptimizer/Utils/Existential.h" +#include "swift/SILOptimizer/Utils/Generics.h" +#include "swift/SILOptimizer/Utils/Local.h" +#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h" +#include "swift/SILOptimizer/Utils/SpecializationMangler.h" +#include "llvm/ADT/SmallVector.h" + +using namespace swift; + +/// Create a SILCloner for Existential Specilizer. +namespace { +class ExistentialSpecializerCloner + : public TypeSubstCloner { + using SuperTy = + TypeSubstCloner; + friend class SILInstructionVisitor; + friend class SILCloner; + + SILFunction *OrigF; + llvm::SmallVector &ArgumentDescList; + llvm::SmallDenseMap &ArgToGenericTypeMap; + llvm::SmallDenseMap + &ExistentialArgDescriptor; + +protected: + void postProcess(SILInstruction *Orig, SILInstruction *Cloned) { + SILClonerWithScopes::postProcess(Orig, + Cloned); + } + +public: + ExistentialSpecializerCloner( + SILFunction *OrigF, SILFunction *NewF, SubstitutionMap Subs, + llvm::SmallVector &ArgumentDescList, + llvm::SmallDenseMap &ArgToGenericTypeMap, + llvm::SmallDenseMap + &ExistentialArgDescriptor) + : SuperTy(*NewF, *OrigF, Subs), OrigF(OrigF), + ArgumentDescList(ArgumentDescList), + ArgToGenericTypeMap(ArgToGenericTypeMap), + ExistentialArgDescriptor(ExistentialArgDescriptor) {} + void cloneAndPopulateFunction(); +}; +} // end anonymous namespace + +/// This function will create the generic version. +void ExistentialSpecializerCloner::cloneAndPopulateFunction() { + // Get the Builder and the NewF + SILBuilder &NewFBuilder = getBuilder(); + SILFunction &NewF = NewFBuilder.getFunction(); + + auto NewFTy = NewF.getLoweredFunctionType(); + SmallVector params; + params.append(NewFTy->getParameters().begin(), NewFTy->getParameters().end()); + + /// Builder will have a ScopeClone with a debugscope that is inherited from + /// the F. + ScopeCloner SC(NewF); + auto DebugScope = SC.getOrCreateClonedScope(OrigF->getDebugScope()); + NewFBuilder.setCurrentDebugScope(DebugScope); + SILOpenedArchetypesTracker OpenedArchetypesTrackerNewF(&NewF); + NewFBuilder.setOpenedArchetypesTracker(&OpenedArchetypesTrackerNewF); + + // Create the entry basic block with the function arguments. + SILBasicBlock *OrigEntryBB = &*OrigF->begin(); + SILBasicBlock *ClonedEntryBB = NewF.createBasicBlock(); + SILModule &M = OrigF->getModule(); + auto &Ctx = M.getASTContext(); + llvm::SmallDenseMap ArgToAllocStackMap; + bool MissingDestroyUse = false; + + BBMap.insert(std::make_pair(OrigEntryBB, ClonedEntryBB)); + NewFBuilder.setInsertionPoint(ClonedEntryBB); + + /// Determine the location for the new init_existential_ref. + auto InsertLoc = OrigF->begin()->begin()->getLoc(); + for (auto &ArgDesc : ArgumentDescList) { + SILArgument *OrigArg = OrigEntryBB->getArgument(ArgDesc.Index); + auto iter = ArgToGenericTypeMap.find(ArgDesc.Index); + SILArgument *NewArg = nullptr; + /// For all Generic Arguments. + if (iter != ArgToGenericTypeMap.end()) { + auto GenericParam = iter->second; + SILType GenericSILType = + M.Types.getLoweredType(NewF.mapTypeIntoContext(GenericParam)); + NewArg = ClonedEntryBB->createFunctionArgument(GenericSILType); + NewArg->setOwnershipKind(ValueOwnershipKind( + M, GenericSILType, ArgDesc.Arg->getArgumentConvention())); + /// Determine the Conformances. + SmallVector NewConformances; + auto ContextTy = NewF.mapTypeIntoContext(GenericParam); + auto OpenedArchetype = ContextTy->castTo(); + for (auto proto : OpenedArchetype->getConformsTo()) { + NewConformances.push_back(ProtocolConformanceRef(proto)); + } + ArrayRef Conformances = + Ctx.AllocateCopy(NewConformances); + auto ExistentialRepr = + ArgDesc.Arg->getType().getPreferredExistentialRepresentation(M); + switch (ExistentialRepr) { + case ExistentialRepresentation::Opaque: { + /// Create this sequence for init_existential_addr.: + /// bb0(%0 : $*T): + /// %3 = alloc_stack $P + /// %4 = init_existential_addr %3 : $*P, $T + /// copy_addr %0 to [initialization] %4 : $*T + /// destroy_addr %0 : $*T + /// %7 = open_existential_addr immutable_access %3 : $*P to + /// $*@opened P + auto *ASI = + NewFBuilder.createAllocStack(InsertLoc, ArgDesc.Arg->getType()); + ArgToAllocStackMap.insert( + std::pair(ArgDesc.Index, ASI)); + + auto *EAI = NewFBuilder.createInitExistentialAddr( + InsertLoc, ASI, NewArg->getType().getASTType(), NewArg->getType(), + Conformances); + /// If DestroyAddr is already there, then do not use [take]. + NewFBuilder.createCopyAddr( + InsertLoc, NewArg, EAI, + ExistentialArgDescriptor[ArgDesc.Index].AccessType == + OpenedExistentialAccess::Immutable + ? IsTake_t::IsNotTake + : IsTake_t::IsTake, + IsInitialization_t::IsInitialization); + if (ExistentialArgDescriptor[ArgDesc.Index].DestroyAddrUse) { + NewFBuilder.createDestroyAddr(InsertLoc, NewArg); + } else { + MissingDestroyUse = true; + } + ValueMap.insert(std::make_pair(OrigArg, SILValue(ASI))); + assert(ValueMap.find(OrigArg) != ValueMap.end()); + break; + } + case ExistentialRepresentation::Class: { + /// Simple case: Create an init_existential. + /// %5 = init_existential_ref %0 : $T : $T, $P + auto *InitRef = NewFBuilder.createInitExistentialRef( + InsertLoc, ArgDesc.Arg->getType(), NewArg->getType().getASTType(), + NewArg, Conformances); + ValueMap.insert(std::make_pair(OrigArg, SILValue(InitRef))); + assert(ValueMap.find(OrigArg) != ValueMap.end()); + break; + } + default: { + llvm_unreachable("Unhandled existential type in ExistentialTransform!"); + break; + } + }; + } else { + /// Arguments that are not rewritten. + auto Ty = params[ArgDesc.Index].getType(); + auto MappedTy = M.Types.getLoweredType(NewF.mapTypeIntoContext(Ty)); + NewArg = ClonedEntryBB->createFunctionArgument(MappedTy, ArgDesc.Decl); + NewArg->setOwnershipKind(ValueOwnershipKind( + M, MappedTy, ArgDesc.Arg->getArgumentConvention())); + ValueMap.insert(std::make_pair(OrigArg, SILValue(NewArg))); + assert(ValueMap.find(OrigArg) != ValueMap.end()); + } + } + + // Recursively visit original BBs in depth-first preorder, starting with the + // entry block, cloning all instructions other than terminators. + visitSILBasicBlock(OrigEntryBB); + + // Now iterate over the BBs and fix up the terminators. + for (auto BI = BBMap.begin(), BE = BBMap.end(); BI != BE; ++BI) { + NewFBuilder.setInsertionPoint(BI->second); + visit(BI->first->getTerminator()); + } + + /// If there is an argument with no DestroyUse, insert DeallocStack + /// before return Instruction. + llvm::SmallPtrSet ReturnInsts; + if (MissingDestroyUse) { + /// Find the set of return instructions in a function. + for (auto &BB : NewF) { + TermInst *TI = BB.getTerminator(); + if (auto *RI = dyn_cast(TI)) { + ReturnInsts.insert(RI); + } + } + } + + /// Walk over destroy_addr instructions and perform fixups. + for (auto &ArgDesc : reversed(ArgumentDescList)) { + int ArgIndex = ArgDesc.Index; + auto iter = ArgToAllocStackMap.find(ArgIndex); + if (iter != ArgToAllocStackMap.end()) { + auto it = ExistentialArgDescriptor.find(ArgIndex); + if (it != ExistentialArgDescriptor.end() && it->second.DestroyAddrUse) { + for (Operand *ASIUse : iter->second->getUses()) { + auto *ASIUser = ASIUse->getUser(); + if (auto *DAI = dyn_cast(ASIUser)) { + SILBuilder Builder(ASIUser); + Builder.setInsertionPoint(&*std::next(ASIUser->getIterator())); + Builder.createDeallocStack(DAI->getLoc(), iter->second); + } + } + } else { // Need to insert DeallocStack before return. + for (auto *I : ReturnInsts) { + SILBuilder Builder(I->getParent()); + Builder.setInsertionPoint(I); + Builder.createDeallocStack(iter->second->getLoc(), iter->second); + } + } + } + } +} + +/// Create a new function name for the newly generated protocol constrained +/// generic function. +std::string ExistentialTransform::createExistentialSpecializedFunctionName() { + for (auto const &IdxIt : ExistentialArgDescriptor) { + int Idx = IdxIt.first; + Mangler.setArgumentExistentialToGeneric(Idx); + } + auto MangledName = Mangler.mangle(); + assert(!F->getModule().hasFunction(MangledName)); + return MangledName; +} + +/// Convert all existential argument types to generic argument type. +void ExistentialTransform::convertExistentialArgTypesToGenericArgTypes( + GenericSignatureBuilder &Builder) { + + SILModule &M = F->getModule(); + auto *Mod = M.getSwiftModule(); + auto &Ctx = M.getASTContext(); + auto FTy = F->getLoweredFunctionType(); + + /// If the original function is generic, then maintain the same. + auto OrigGenericSig = FTy->getGenericSignature(); + + /// Original list of parameters + SmallVector params; + params.append(FTy->getParameters().begin(), FTy->getParameters().end()); + + /// Determine the existing generic parameter depth. + int Depth = 0; + if (OrigGenericSig != nullptr) { + Depth = OrigGenericSig->getGenericParams().back()->getDepth() + 1; + } + + /// Index of the Generic Parameter. + int GPIdx = 0; + + /// Convert the protocol arguments of F to generic ones. + for (auto const &IdxIt : ExistentialArgDescriptor) { + int Idx = IdxIt.first; + auto ¶m = params[Idx]; + auto PType = param.getType(); + assert(PType.isExistentialType()); + /// Generate new generic parameter. + auto *NewGenericParam = GenericTypeParamType::get(Depth, GPIdx++, Ctx); + Builder.addGenericParameter(NewGenericParam); + Requirement NewRequirement(RequirementKind::Conformance, NewGenericParam, + PType); + auto Source = + GenericSignatureBuilder::FloatingRequirementSource::forAbstract(); + Builder.addRequirement(NewRequirement, Source, Mod); + ArgToGenericTypeMap.insert( + std::pair(Idx, NewGenericParam)); + assert(ArgToGenericTypeMap.find(Idx) != ArgToGenericTypeMap.end()); + } +} + +/// Create the signature for the newly generated protocol constrained generic +/// function. +CanSILFunctionType +ExistentialTransform::createExistentialSpecializedFunctionType() { + auto FTy = F->getLoweredFunctionType(); + SILModule &M = F->getModule(); + auto &Ctx = M.getASTContext(); + GenericSignature *NewGenericSig; + GenericEnvironment *NewGenericEnv; + + // Form a new generic signature based on the old one. + GenericSignatureBuilder Builder(Ctx); + + /// If the original function is generic, then maintain the same. + auto OrigGenericSig = FTy->getGenericSignature(); + /// First, add the old generic signature. + Builder.addGenericSignature(OrigGenericSig); + + /// Convert existential argument types to generic argument types. + convertExistentialArgTypesToGenericArgTypes(Builder); + + /// Compute the updated generic signature. + NewGenericSig = std::move(Builder).computeGenericSignature( + SourceLoc(), /*allowConcreteGenericParams=*/true); + NewGenericEnv = NewGenericSig->createGenericEnvironment(); + + /// Create a lambda for GenericParams. + auto getCanonicalType = [&](Type t) -> CanType { + return t->getCanonicalType(NewGenericSig); + }; + + /// Original list of parameters + SmallVector params; + params.append(FTy->getParameters().begin(), FTy->getParameters().end()); + + /// Create the complete list of parameters. + int Idx = 0; + llvm::SmallVector InterfaceParams; + InterfaceParams.reserve(params.size()); + for (auto ¶m : params) { + auto iter = ArgToGenericTypeMap.find(Idx); + if (iter != ArgToGenericTypeMap.end()) { + auto GenericParam = iter->second; + InterfaceParams.push_back(SILParameterInfo(getCanonicalType(GenericParam), + param.getConvention())); + } else { + InterfaceParams.push_back(param); + } + Idx++; + } + + // Add error results. + Optional InterfaceErrorResult; + if (FTy->hasErrorResult()) { + InterfaceErrorResult = FTy->getErrorResult(); + } + + /// Finally the ExtInfo. + auto ExtInfo = FTy->getExtInfo(); + ExtInfo = ExtInfo.withRepresentation(SILFunctionTypeRepresentation::Thin); + auto witnessMethodConformance = FTy->getWitnessMethodConformanceOrNone(); + + /// Return the new signature. + return SILFunctionType::get( + NewGenericSig, ExtInfo, FTy->getCoroutineKind(), + FTy->getCalleeConvention(), InterfaceParams, FTy->getYields(), + FTy->getResults(), InterfaceErrorResult, Ctx, witnessMethodConformance); +} + +/// Create the Thunk Body with always_inline attribute. +void ExistentialTransform::populateThunkBody() { + + SILModule &M = F->getModule(); + + F->setThunk(IsSignatureOptimizedThunk); + F->setInlineStrategy(AlwaysInline); + + /// Remove original body of F. + for (auto It = F->begin(), End = F->end(); It != End;) { + auto *BB = &*It++; + removeDeadBlock(BB); + } + + /// Create a basic block and the function arguments. + auto *ThunkBody = F->createBasicBlock(); + for (auto &ArgDesc : ArgumentDescList) { + ThunkBody->createFunctionArgument(ArgDesc.Arg->getType(), ArgDesc.Decl); + } + + /// Builder to add new instructions in the Thunk. + SILBuilder Builder(ThunkBody); + SILOpenedArchetypesTracker OpenedArchetypesTracker(F); + Builder.setOpenedArchetypesTracker(&OpenedArchetypesTracker); + Builder.setCurrentDebugScope(ThunkBody->getParent()->getDebugScope()); + + /// Location to insert new instructions. + auto Loc = ThunkBody->getParent()->getLocation(); + + /// Create the function_ref instruction to the NewF. + auto *FRI = Builder.createFunctionRef(Loc, NewF); + + auto GenCalleeType = NewF->getLoweredFunctionType(); + auto CalleeGenericSig = GenCalleeType->getGenericSignature(); + auto OrigGenCalleeType = F->getLoweredFunctionType(); + auto OrigCalleeGenericSig = OrigGenCalleeType->getGenericSignature(); + + /// Determine arguments to Apply. + /// Generate opened existentials for generics. + llvm::SmallVector ApplyArgs; + llvm::SmallDenseMap GenericToOpenedTypeMap; + for (auto &ArgDesc : ArgumentDescList) { + auto iter = ArgToGenericTypeMap.find(ArgDesc.Index); + auto it = ExistentialArgDescriptor.find(ArgDesc.Index); + if (iter != ArgToGenericTypeMap.end() && + it != ExistentialArgDescriptor.end()) { + ArchetypeType *Opened; + auto OrigOperand = ThunkBody->getArgument(ArgDesc.Index); + auto SwiftType = ArgDesc.Arg->getType().getASTType(); + auto OpenedType = + SwiftType->openAnyExistentialType(Opened)->getCanonicalType(); + auto OpenedSILType = NewF->getModule().Types.getLoweredType(OpenedType); + SILValue archetypeValue; + auto ExistentialRepr = + ArgDesc.Arg->getType().getPreferredExistentialRepresentation(M); + switch (ExistentialRepr) { + case ExistentialRepresentation::Opaque: { + archetypeValue = Builder.createOpenExistentialAddr( + Loc, OrigOperand, OpenedSILType, it->second.AccessType); + ApplyArgs.push_back(archetypeValue); + break; + } + case ExistentialRepresentation::Class: { + archetypeValue = + Builder.createOpenExistentialRef(Loc, OrigOperand, OpenedSILType); + ApplyArgs.push_back(archetypeValue); + break; + } + default: { + llvm_unreachable("Unhandled existential type in ExistentialTransform!"); + break; + } + }; + GenericToOpenedTypeMap.insert( + std::pair(iter->second, OpenedType)); + assert(GenericToOpenedTypeMap.find(iter->second) != + GenericToOpenedTypeMap.end()); + } else { + ApplyArgs.push_back(ThunkBody->getArgument(ArgDesc.Index)); + } + } + + unsigned int OrigDepth = 0; + if (F->getLoweredFunctionType()->isPolymorphic()) { + OrigDepth = OrigCalleeGenericSig->getGenericParams().back()->getDepth() + 1; + } + SubstitutionMap OrigSubMap = F->getForwardingSubstitutionMap(); + + /// Create substitutions for Apply instructions. + auto SubMap = SubstitutionMap::get( + CalleeGenericSig, + [&](SubstitutableType *type) -> Type { + if (auto *GP = dyn_cast(type)) { + if (GP->getDepth() < OrigDepth) { + return Type(GP).subst(OrigSubMap); + } else { + auto iter = GenericToOpenedTypeMap.find(GP); + assert(iter != GenericToOpenedTypeMap.end()); + return iter->second; + } + } else { + return type; + } + }, + MakeAbstractConformanceForGenericType()); + + /// Perform the substitutions. + auto SubstCalleeType = GenCalleeType->substGenericArgs(M, SubMap); + + /// Obtain the Result Type. + SILValue ReturnValue; + auto FunctionTy = NewF->getLoweredFunctionType(); + SILFunctionConventions Conv(SubstCalleeType, M); + SILType ResultType = Conv.getSILResultType(); + + /// If the original function has error results, we need to generate a + /// try_apply to call a function with an error result. + if (FunctionTy->hasErrorResult()) { + SILFunction *Thunk = ThunkBody->getParent(); + SILBasicBlock *NormalBlock = Thunk->createBasicBlock(); + ReturnValue = + NormalBlock->createPHIArgument(ResultType, ValueOwnershipKind::Owned); + SILBasicBlock *ErrorBlock = Thunk->createBasicBlock(); + + SILType Error = Conv.getSILType(FunctionTy->getErrorResult()); + auto *ErrorArg = + ErrorBlock->createPHIArgument(Error, ValueOwnershipKind::Owned); + Builder.createTryApply(Loc, FRI, SubMap, ApplyArgs, NormalBlock, + ErrorBlock); + + Builder.setInsertionPoint(ErrorBlock); + Builder.createThrow(Loc, ErrorArg); + Builder.setInsertionPoint(NormalBlock); + } else { + /// Create the Apply with substitutions + ReturnValue = Builder.createApply(Loc, FRI, SubMap, ApplyArgs, false); + } + + /// Set up the return results. + if (NewF->isNoReturnFunction()) { + Builder.createUnreachable(Loc); + } else { + Builder.createReturn(Loc, ReturnValue); + } +} + +/// Strategy to specialize existential arguments: +/// (1) Create a protocol constrained generic function from the old function; +/// (2) Create a thunk for the original function that invokes (1) including +/// setting +/// its inline strategy as always inline. +void ExistentialTransform::createExistentialSpecializedFunction() { + std::string Name = createExistentialSpecializedFunctionName(); + SILLinkage linkage = getSpecializedLinkage(F, F->getLinkage()); + + /// Create devirtualized function type. + auto NewFTy = createExistentialSpecializedFunctionType(); + + auto NewFGenericSig = NewFTy->getGenericSignature(); + auto NewFGenericEnv = NewFGenericSig->createGenericEnvironment(); + + /// Step 1: Create the new protocol constrained generic function. + NewF = FunctionBuilder.createFunction( + linkage, Name, NewFTy, NewFGenericEnv, F->getLocation(), F->isBare(), + F->isTransparent(), F->isSerialized(), F->getEntryCount(), F->isThunk(), + F->getClassSubclassScope(), F->getInlineStrategy(), F->getEffectsKind(), + nullptr, F->getDebugScope()); + /// Set the semantics attributes for the new function. + for (auto &Attr : F->getSemanticsAttrs()) + NewF->addSemanticsAttr(Attr); + + /// Set Unqualified ownership, if any. + if (!F->hasQualifiedOwnership()) { + NewF->setUnqualifiedOwnership(); + } + + /// Step 1a: Populate the body of NewF. + SubstitutionMap Subs = SubstitutionMap::get( + NewFGenericSig, + [&](SubstitutableType *type) -> Type { + return NewFGenericEnv->mapTypeIntoContext(type); + }, + LookUpConformanceInModule(F->getModule().getSwiftModule())); + ExistentialSpecializerCloner cloner(F, NewF, Subs, ArgumentDescList, + ArgToGenericTypeMap, + ExistentialArgDescriptor); + cloner.cloneAndPopulateFunction(); + + /// Step 2: Create the thunk with always_inline and populate its body. + populateThunkBody(); + + assert(F->getDebugScope()->Parent != NewF->getDebugScope()->Parent); + + LLVM_DEBUG(llvm::dbgs() << "After ExistentialSpecializer Pass\n"; F->dump(); + NewF->dump();); +} diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.h b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.h new file mode 100644 index 0000000000000..f6c9066d89b9f --- /dev/null +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.h @@ -0,0 +1,103 @@ +//===---- ExistentialSpecializerTransform.h - Existential Specializer -----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This contains utilities for transforming existential args to generics. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_EXISTENTIALTRANSFORM_H +#define SWIFT_SIL_EXISTENTIALTRANSFORM_H +#include "FunctionSignatureOpts.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SILOptimizer/Utils/Existential.h" +#include "swift/SILOptimizer/Utils/Local.h" +#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h" +#include "swift/SILOptimizer/Utils/SpecializationMangler.h" +#include "llvm/ADT/SmallBitVector.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" + +namespace swift { + +/// A descriptor to carry information from ExistentialTransform analysis +/// to transformation. +struct ExistentialTransformArgumentDescriptor { + OpenedExistentialAccess AccessType; + bool DestroyAddrUse; +}; + +/// ExistentialTransform creates a protocol constrained generic and a thunk. +class ExistentialTransform { + /// Function Builder to create a new thunk. + SILOptFunctionBuilder &FunctionBuilder; + + /// The original function to analyze and transform. + SILFunction *F; + + /// The newly created inner function. + SILFunction *NewF; + + /// The function signature mangler we are using. + Mangle::FunctionSignatureSpecializationMangler &Mangler; + + /// List of arguments and their descriptors to specialize + llvm::SmallDenseMap + &ExistentialArgDescriptor; + + /// Argument to Generic Type Map for NewF. + llvm::SmallDenseMap ArgToGenericTypeMap; + + /// Allocate the argument descriptors. + llvm::SmallVector &ArgumentDescList; + + /// Create the Devirtualized Inner Function. + void createExistentialSpecializedFunction(); + + /// Create new generic arguments from existential arguments. + void + convertExistentialArgTypesToGenericArgTypes(GenericSignatureBuilder &Builder); + + /// Create a name for the inner function. + std::string createExistentialSpecializedFunctionName(); + + /// Create the new devirtualized protocol function signature. + CanSILFunctionType createExistentialSpecializedFunctionType(); + + /// Create the thunk. + void populateThunkBody(); + +public: + /// Constructor. + ExistentialTransform( + SILOptFunctionBuilder &FunctionBuilder, SILFunction *F, + Mangle::FunctionSignatureSpecializationMangler &Mangler, + llvm::SmallVector &ADL, + llvm::SmallDenseMap + &ExistentialArgDescriptor) + : FunctionBuilder(FunctionBuilder), F(F), NewF(nullptr), Mangler(Mangler), + ExistentialArgDescriptor(ExistentialArgDescriptor), + ArgumentDescList(ADL) {} + + /// Return the optimized iner function. + SILFunction *getExistentialSpecializedFunction() { return NewF; } + + /// External function for the optimization. + bool run() { + createExistentialSpecializedFunction(); + return true; + } +}; + +} // end namespace swift + +#endif diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 32075ebc36c62..7e27287c93520 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -246,6 +246,9 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { // Promote stack allocations to values. P.addMem2Reg(); + // Run the existential specializer Pass. + P.addExistentialSpecializer(); + // Cleanup, which is important if the inliner has restarted the pass pipeline. P.addPerformanceConstantPropagation(); P.addSimplifyCFG(); diff --git a/test/SILOptimizer/existential_transform.swift b/test/SILOptimizer/existential_transform.swift new file mode 100644 index 0000000000000..37392ac5c38f3 --- /dev/null +++ b/test/SILOptimizer/existential_transform.swift @@ -0,0 +1,346 @@ +// RUN: %target-swift-frontend -O -sil-existential-specializer -Xllvm -sil-disable-pass=GenericSpecializer -Xllvm -sil-disable-pass=FunctionSignatureOpts -Xllvm -sil-disable-pass=SILCombine -emit-sil -sil-verify-all %s | %FileCheck %s + +internal protocol SomeProtocol : class { + func foo() -> Int +} +internal class SomeClass: SomeProtocol { + init() { + } + @inline(never) func foo() -> Int { + return 10 + } +} + +@inline(never) internal func wrap_foo_cp(a:SomeProtocol ) -> Int{ + return a.foo() +} + +// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform2cpyyF : $@convention(thin) () -> () { +// CHECK: bb0: +// CHECK: alloc_ref +// CHECK: debug_value +// CHECK: init_existential_ref +// CHECK: debug_value +// CHECK: function_ref @$s21existential_transform11wrap_foo_cp1aSiAA12SomeProtocol_p_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : SomeProtocol> (@guaranteed τ_0_0) -> Int +// CHECK: open_existential_ref +// CHECK: apply +// CHECK: set_deallocating +// CHECK: debug_value +// CHECK: debug_value +// CHECK: dealloc_ref +// CHECK: dealloc_ref +// CHECK: tuple +// CHECK: return +// CHECK-LABEL: } // end sil function '$s21existential_transform2cpyyF' +@inline(never) func cp() { +let magic1:SomeProtocol = SomeClass() +let _ = wrap_foo_cp(a:magic1) +} + +/// Non Class Protocol +internal protocol SomeNoClassProtocol { + func foo() -> Int +} +internal class SomeNoClass: SomeNoClassProtocol { + init() { + } + @inline(never) func foo() -> Int { + return 10 + } +} + +@inline(never) internal func wrap_foo_ncp(a:SomeNoClassProtocol) -> Int{ + return a.foo() +} + +// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform3ncpyyF : $@convention(thin) () -> () { +// CHECK: bb0: +// CHECK: alloc_stack +// CHECK: alloc_ref $SomeNoClass +// CHECK: debug_value +// CHECK: init_existential_addr +// CHECK: store +// CHECK: function_ref @$s21existential_transform12wrap_foo_ncp1aSiAA19SomeNoClassProtocol_p_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : SomeNoClassProtocol> (@in_guaranteed τ_0_0) -> Int +// CHECK: open_existential_addr +// CHECK: apply +// CHECK: destroy_addr +// CHECK: dealloc_stack +// CHECK: tuple +// CHECK: return +// CHECK-LABEL: } // end sil function '$s21existential_transform3ncpyyF' +@inline(never) func ncp() { +let magic2:SomeNoClassProtocol = SomeNoClass() +let _ = wrap_foo_ncp(a:magic2) +} + + +/// Class Protocol Composition +internal protocol SomeClassProtocolComp : class { + func foo() -> Int +} +internal protocol SomeOtherClassProtocolComp : class { + func bar() -> Int +} +internal class SomeClassComp: SomeClassProtocolComp, SomeOtherClassProtocolComp { + init() { + } + @inline(never) func foo() -> Int { + return 10 + } + @inline(never) func bar() -> Int { + return 20 + } +} +@inline(never) internal func wrap_foo_bar_cpc(a:SomeClassProtocolComp & SomeOtherClassProtocolComp) -> Int{ + return a.foo() + a.bar() +} + +// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform3cpcyyF : $@convention(thin) () -> () { +// CHECK: bb0: +// CHECK: alloc_ref +// CHECK: debug_value +// CHECK: init_existential_ref +// CHECK: debug_value +// CHECK: function_ref @$s21existential_transform16wrap_foo_bar_cpc1aSiAA21SomeClassProtocolComp_AA0g5OtherhiJ0p_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : SomeClassProtocolComp, τ_0_0 : SomeOtherClassProtocolComp> (@guaranteed τ_0_0) -> Int +// CHECK: open_existential_ref +// CHECK: apply +// CHECK: set_deallocating +// CHECK: debug_value +// CHECK: debug_value +// CHECK: dealloc_ref +// CHECK: dealloc_ref +// CHECK: tuple +// CHECK: return +// CHECK-LABEL: } // end sil function '$s21existential_transform3cpcyyF' +@inline(never) func cpc() { +let magic3:SomeClassProtocolComp & SomeOtherClassProtocolComp = SomeClassComp() +let _ = wrap_foo_bar_cpc(a:magic3) +} + +/// Non Class Protocol Comp +internal protocol SomeNoClassProtocolComp { + func foo() -> Int +} +internal protocol SomeOtherNoClassProtocolComp { + func bar() -> Int +} +internal class SomeNoClassComp: SomeNoClassProtocolComp, SomeOtherNoClassProtocolComp { + init() { + } + @inline(never) func foo() -> Int { + return 10 + } + @inline(never) func bar() -> Int { + return 20 + } +} +@inline(never) internal func wrap_no_foo_bar_comp_ncpc(a:SomeNoClassProtocolComp & SomeOtherNoClassProtocolComp) -> Int{ + return a.foo() + a.bar() +} + +// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform4ncpcyyF : $@convention(thin) () -> () { +// CHECK: bb0: +// CHECK: alloc_stack +// CHECK: alloc_ref +// CHECK: debug_value +// CHECK: init_existential_addr +// CHECK: store +// CHECK: function_ref @$s21existential_transform25wrap_no_foo_bar_comp_ncpc1aSiAA23SomeNoClassProtocolComp_AA0i5OtherjklM0p_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : SomeNoClassProtocolComp, τ_0_0 : SomeOtherNoClassProtocolComp> (@in_guaranteed τ_0_0) -> Int +// CHECK: open_existential_addr +// CHECK: apply +// CHECK: destroy_addr +// CHECK: dealloc_stack +// CHECK: tuple +// CHECK: return +// CHECK-LABEL: } // end sil function '$s21existential_transform4ncpcyyF' +@inline(never) func ncpc() { +let magic4:SomeNoClassProtocolComp & SomeOtherNoClassProtocolComp = SomeNoClassComp() +let _ = wrap_no_foo_bar_comp_ncpc(a:magic4) +} + +internal protocol P : class { + func foo() -> Int +} +internal class K : P { + init() { + } + @inline(never) func foo() -> Int { + return 10 + } +} + +// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform18do_not_optimize_cp1aSiAA1P_p_tF : $@convention(thin) (@guaranteed P) -> Int { +// CHECK: bb0(%0 : $P): +// CHECK: debug_value +// CHECK: [[O1:%.*]] = open_existential_ref +// CHECK: witness_method $@opened("{{.*}}") P, #P.foo!1 : (Self) -> () -> Int, [[O1]] : $@opened("{{.*}}") P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@guaranteed τ_0_0) -> Int +// CHECK: apply +// CHECK: return +// CHECK-LABEL: } // end sil function '$s21existential_transform18do_not_optimize_cp1aSiAA1P_p_tF' +@inline(never) internal func do_not_optimize_cp(a:P ) -> Int{ + return a.foo() +} + +internal protocol PP : class { + func foo() -> Int +} +internal class KK : PP { + init() { + } + @inline(never) func foo() -> Int { + return 10 + } +} + +// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform13wrap_inout_cp1aSiAA2PP_pz_tF : $@convention(thin) (@inout PP) -> Int { +// CHECK: bb0(%0 : $*PP): +// CHECK: debug_value_addr +// CHECK: load +// CHECK: [[O1:%.*]] = open_existential_ref +// CHECK: witness_method $@opened("{{.*}}") PP, #PP.foo!1 : (Self) -> () -> Int, %3 : $@opened("{{.*}}PP : $@convention(witness_method: PP) <τ_0_0 where τ_0_0 : PP> (@guaranteed τ_0_0) -> Int +// CHECK: apply +// CHECK: return +// CHECK-LABEL: } // end sil function '$s21existential_transform13wrap_inout_cp1aSiAA2PP_pz_tF' +@inline(never) internal func wrap_inout_cp(a: inout PP ) -> Int{ + return a.foo() +} + +// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform24do_not_optimize_inout_cpyyF : $@convention(thin) () -> () { +// CHECK: bb0: +// CHECK: alloc_stack +// CHECK: alloc_ref +// CHECK: debug_value +// CHECK: init_existential_ref +// CHECK: store +// CHECK: function_ref @$s21existential_transform13wrap_inout_cp1aSiAA2PP_pz_tF : $@convention(thin) (@inout PP) -> Int +// CHECK: apply +// CHECK: set_deallocating +// CHECK: debug_value +// CHECK: debug_value +// CHECK: dealloc_ref +// CHECK: dealloc_ref +// CHECK: dealloc_stack +// CHECK: tuple +// CHECK: return +// CHECK-LABEL: } // end sil function '$s21existential_transform24do_not_optimize_inout_cpyyF' +@inline(never) func do_not_optimize_inout_cp() { +var magic5:PP = KK() +let _ = wrap_inout_cp(a: &magic5) +} + +internal protocol PPP { + func foo() -> Int +} +internal class KKKClass : PPP { + init() { + } + @inline(never) func foo() -> Int { + return 10 + } +} + +@inline(never) internal func wrap_inout_ncp(a: inout PPP ) -> Int{ + return a.foo() +} + +// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform9inout_ncpyyF : $@convention(thin) () -> () { +// CHECK: bb0: +// CHECK: alloc_stack +// CHECK: alloc_ref +// CHECK: debug_value +// CHECK: init_existential_addr +// CHECK: store +// CHECK: function_ref @$s21existential_transform14wrap_inout_ncp1aSiAA3PPP_pz_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : PPP> (@inout τ_0_0) -> Int +// CHECK: open_existential_addr +// CHECK: apply +// CHECK: destroy_addr +// CHECK: dealloc_stack +// CHECK: tuple +// CHECK: return +// CHECK-LABEL: } // end sil function '$s21existential_transform9inout_ncpyyF' +@inline(never) func inout_ncp() { +var magic6:PPP = KKKClass() +let _ = wrap_inout_ncp(a: &magic6) +} + +internal protocol PPPP { + func foo() -> Int +} +internal struct SSSS : PPPP { + init() { + } + @inline(never) func foo() -> Int { + return 10 + } +} +@inline(never) internal func wrap_struct_inout_ncp(a: inout PPPP ) -> Int{ + return a.foo() +} + +// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform16struct_inout_ncpyyF : $@convention(thin) () -> () { +// CHECK: bb0: +// CHECK: alloc_stack +// CHECK: struct +// CHECK: init_existential_addr +// CHECK: store +// CHECK: function_ref @$s21existential_transform21wrap_struct_inout_ncp1aSiAA4PPPP_pz_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : PPPP> (@inout τ_0_0) -> Int +// CHECK: open_existential_addr +// CHECK: apply +// CHECK: destroy_addr +// CHECK: dealloc_stack +// CHECK: tuple +// CHECK: return +// CHECK-LABEL: } // end sil function '$s21existential_transform16struct_inout_ncpyyF' +@inline(never) func struct_inout_ncp() { +var magic7:PPPP = SSSS() +let _ = wrap_struct_inout_ncp(a: &magic7) +} + +protocol GP { +func foo() -> Int +} + +class GC: GP { + @inline(never) func foo() ->Int { + return 10 + } +} +func wrap_gcp(_ a:T,_ b:GP) -> Int { + return a.foo() + b.foo() +} +// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform3gcpySixAA2GPRzlF : $@convention(thin) (@in_guaranteed T) -> Int { +// CHECK: bb0(%0 : $*T): +// CHECK: debug_value_addr +// CHECK: alloc_ref +// CHECK: debug_value +// CHECK: debug_value +// CHECK: alloc_stack +// CHECK: init_existential_addr +// CHECK: store +// CHECK: function_ref @$s21existential_transform8wrap_gcpySix_AA2GP_ptAaCRzlFTf4ne_n : $@convention(thin) <τ_0_0 where τ_0_0 : GP><τ_1_0 where τ_1_0 : GP> (@in_guaranteed τ_0_0, @in_guaranteed τ_1_0) -> Int +// CHECK: open_existential_addr +// CHECK: strong_retain +// CHECK: apply +// CHECK: destroy_addr +// CHECK: dealloc_stack +// CHECK: strong_release +// CHECK: return +// CHECK: } // end sil function '$s21existential_transform3gcpySixAA2GPRzlF' +@inline(never) func gcp(_ a:T) -> Int { + let k:GC = GC() + return wrap_gcp(a, k) +} + +@_optimize(none) public func foo() -> Int { +cp() +ncp() +cpc() +ncpc() +let p:P = K() +let x:Int = do_not_optimize_cp(a:p) +do_not_optimize_inout_cp() +inout_ncp() +struct_inout_ncp() +let y:Int = gcp(GC()) +return x + y +}