blob: 5dabe344bf7633b2d4c3bc75abcc8b073328f564 [file] [log] [blame]
//===--- Generics.cpp ---- Utilities for transforming generics ------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "generic-specializer"
#include "swift/SILOptimizer/Utils/Generics.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsSIL.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/SemanticAttrs.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/TypeMatcher.h"
#include "swift/Basic/Statistic.h"
#include "swift/Demangling/ManglingMacros.h"
#include "swift/SIL/DebugUtils.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/OptimizationRemark.h"
#include "swift/SIL/PrettyStackTrace.h"
#include "swift/SILOptimizer/Utils/GenericCloner.h"
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
#include "swift/SILOptimizer/Utils/SpecializationMangler.h"
#include "swift/Serialization/SerializedSILLoader.h"
#include "swift/Strings.h"
using namespace swift;
STATISTIC(NumPreventedGenericSpecializationLoops,
"# of prevented infinite generic specializations loops");
STATISTIC(NumPreventedTooComplexGenericSpecializations,
"# of prevented generic specializations with too complex "
"generic type parameters");
/// Set to true to enable the support for partial specialization.
llvm::cl::opt<bool> EnablePartialSpecialization(
"sil-partial-specialization", llvm::cl::init(false),
llvm::cl::desc("Enable partial specialization of generics"));
/// If set, then generic specialization tries to specialize using
/// all substitutions, even if they the replacement types are generic.
llvm::cl::opt<bool> SupportGenericSubstitutions(
"sil-partial-specialization-with-generic-substitutions",
llvm::cl::init(false),
llvm::cl::desc("Enable partial specialization with generic substitutions"));
/// Set to true to print detected infinite generic specialization loops that
/// were prevented.
llvm::cl::opt<bool> PrintGenericSpecializationLoops(
"sil-print-generic-specialization-loops", llvm::cl::init(false),
llvm::cl::desc("Print detected infinite generic specialization loops that "
"were prevented"));
llvm::cl::opt<bool> VerifyFunctionsAfterSpecialization(
"sil-generic-verify-after-specialization", llvm::cl::init(false),
llvm::cl::desc(
"Verify functions after they are specialized "
"'PrettyStackTraceFunction'-ing the original function if we fail."));
static bool OptimizeGenericSubstitutions = false;
/// Max depth of a type which can be processed by the generic
/// specializer.
/// E.g. the depth of Array<Array<Array<T>>> is 3.
/// No specializations will be produced, if any of generic parameters contains
/// a bound generic type with the depth higher than this threshold
static const unsigned TypeDepthThreshold = 50;
/// Set the width threshold rather high, because some projects uses very wide
/// tuples to model fixed size arrays.
static const unsigned TypeWidthThreshold = 2000;
/// Compute the width and the depth of a type.
/// We compute both, because some pathological test-cases result in very
/// wide types and some others result in very deep types. It is important
/// to bail as soon as we hit the threshold on any of both dimensions to
/// prevent compiler hangs and crashes.
static std::pair<unsigned, unsigned> getTypeDepthAndWidth(Type t) {
unsigned Depth = 0;
unsigned Width = 0;
if (auto *BGT = t->getAs<BoundGenericType>()) {
auto *NTD = BGT->getNominalOrBoundGenericNominal();
if (NTD) {
auto StoredProperties = NTD->getStoredProperties();
Width += StoredProperties.size();
}
++Depth;
unsigned MaxTypeDepth = 0;
auto GenericArgs = BGT->getGenericArgs();
for (auto Ty : GenericArgs) {
unsigned TypeWidth;
unsigned TypeDepth;
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(Ty);
if (TypeDepth > MaxTypeDepth)
MaxTypeDepth = TypeDepth;
Width += TypeWidth;
}
Depth += MaxTypeDepth;
return std::make_pair(Depth, Width);
}
if (auto *TupleTy = t->getAs<TupleType>()) {
Width += TupleTy->getNumElements();
++Depth;
unsigned MaxTypeDepth = 0;
auto ElementTypes = TupleTy->getElementTypes();
for (auto Ty : ElementTypes) {
unsigned TypeWidth;
unsigned TypeDepth;
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(Ty);
if (TypeDepth > MaxTypeDepth)
MaxTypeDepth = TypeDepth;
Width += TypeWidth;
}
Depth += MaxTypeDepth;
return std::make_pair(Depth, Width);
}
if (auto *FnTy = t->getAs<SILFunctionType>()) {
++Depth;
unsigned MaxTypeDepth = 0;
auto Params = FnTy->getParameters();
Width += Params.size();
for (auto Param : Params) {
unsigned TypeWidth;
unsigned TypeDepth;
std::tie(TypeDepth, TypeWidth) =
getTypeDepthAndWidth(Param.getInterfaceType());
if (TypeDepth > MaxTypeDepth)
MaxTypeDepth = TypeDepth;
Width += TypeWidth;
}
auto Results = FnTy->getResults();
Width += Results.size();
for (auto Result : Results) {
unsigned TypeWidth;
unsigned TypeDepth;
std::tie(TypeDepth, TypeWidth) =
getTypeDepthAndWidth(Result.getInterfaceType());
if (TypeDepth > MaxTypeDepth)
MaxTypeDepth = TypeDepth;
Width += TypeWidth;
}
if (FnTy->hasErrorResult()) {
Width += 1;
unsigned TypeWidth;
unsigned TypeDepth;
std::tie(TypeDepth, TypeWidth) =
getTypeDepthAndWidth(FnTy->getErrorResult().getInterfaceType());
if (TypeDepth > MaxTypeDepth)
MaxTypeDepth = TypeDepth;
Width += TypeWidth;
}
Depth += MaxTypeDepth;
return std::make_pair(Depth, Width);
}
if (auto *FnTy = t->getAs<FunctionType>()) {
++Depth;
unsigned MaxTypeDepth = 0;
auto Params = FnTy->getParams();
Width += Params.size();
for (auto &Param : Params) {
unsigned TypeWidth;
unsigned TypeDepth;
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(Param.getParameterType());
if (TypeDepth > MaxTypeDepth)
MaxTypeDepth = TypeDepth;
Width += TypeWidth;
}
unsigned TypeWidth;
unsigned TypeDepth;
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(FnTy->getResult());
if (TypeDepth > MaxTypeDepth)
MaxTypeDepth = TypeDepth;
Width += TypeWidth;
Depth += MaxTypeDepth;
return std::make_pair(Depth, Width);
}
if (auto *MT = t->getAs<MetatypeType>()) {
Depth += 1;
unsigned TypeWidth;
unsigned TypeDepth;
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(MT->getInstanceType());
Width += TypeWidth;
Depth += TypeDepth;
return std::make_pair(Depth, Width);
}
return std::make_pair(Depth, Width);
}
static bool isTypeTooComplex(Type t) {
unsigned TypeWidth;
unsigned TypeDepth;
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(t);
return TypeWidth >= TypeWidthThreshold || TypeDepth >= TypeDepthThreshold;
}
namespace {
/// A helper class used to check whether one type is structurally contained
/// the other type either completely or partially.
class TypeComparator : public TypeMatcher<TypeComparator> {
bool IsContained = false;
public:
bool isEqual(CanType T1, CanType T2) { return T1 == T2; }
/// Check whether the type T1 is different from T2 and contained in the type
/// T2.
bool isStrictlyContainedIn(CanType T1, CanType T2) {
if (isEqual(T1, T2))
return false;
return T2.findIf([&T1, this](Type T) -> bool {
return isEqual(T->getCanonicalType(), T1);
});
}
/// Check whether the type T1 is strictly or partially contained in the type
/// T2.
/// Partially contained means that if you drop the common structural "prefix"
/// of T1 and T2 and get T1' and T2' then T1' is strictly contained in T2'.
bool isPartiallyContainedIn(CanType T1, CanType T2) {
if (isStrictlyContainedIn(T1, T2))
return true;
match(T1, T2);
return IsContained;
}
/// This method is invoked aftre skipping a common prefix of two types,
/// when a structural difference is found.
bool mismatch(TypeBase *firstType, TypeBase *secondType,
Type sugaredFirstType) {
auto firstCanType = firstType->getCanonicalType();
auto secondCanType = secondType->getCanonicalType();
if (isEqual(firstCanType, secondCanType))
return false;
if (isStrictlyContainedIn(firstCanType, secondCanType)) {
IsContained = true;
return false;
}
return false;
}
};
} // anonymous namespace
/// Checks if a second substitution map is an expanded version of
/// the first substitution map.
/// This is the case if at least one of the substitution type in Subs2 is
/// "bigger" than the corresponding substitution type in Subs1.
/// Type T2 is "smaller" than type T1 if T2 is structurally contained in T1.
static bool growingSubstitutions(SubstitutionMap Subs1,
SubstitutionMap Subs2) {
auto Replacements1 = Subs1.getReplacementTypes();
auto Replacements2 = Subs2.getReplacementTypes();
assert(Replacements1.size() == Replacements2.size());
TypeComparator TypeCmp;
// Perform component-wise comparisions for substitutions.
for (unsigned idx : indices(Replacements1)) {
auto Type1 = Replacements1[idx]->getCanonicalType();
auto Type2 = Replacements2[idx]->getCanonicalType();
// If types are the same, the substitution type does not grow.
if (TypeCmp.isEqual(Type2, Type1))
continue;
// If the new substitution type is getting smaller, the
// substitution type does not grow.
if (TypeCmp.isPartiallyContainedIn(Type2, Type1))
continue;
if (TypeCmp.isPartiallyContainedIn(Type1, Type2)) {
LLVM_DEBUG(llvm::dbgs() << "Type:\n"; Type1.dump(llvm::dbgs());
llvm::dbgs() << "is (partially) contained in type:\n";
Type2.dump(llvm::dbgs());
llvm::dbgs() << "Replacements[" << idx
<< "] has got bigger since last time.\n");
return true;
}
// None of the types is contained in the other type.
// They are not comparable in this sense.
}
// The substitition list is not growing.
return false;
}
/// Checks whether specializing a given generic apply would create an infinite
/// cycle in the generic specializations graph. This can be the case if there is
/// a loop in the specialization graph and generic parameters at each iteration
/// of such a loop are getting bigger and bigger.
/// The specialization graph is represented by means of SpecializationInformation.
/// We use this meta-information about specializations to detect cycles in this
/// graph.
static bool createsInfiniteSpecializationLoop(ApplySite Apply) {
if (!Apply)
return false;
auto *Callee = Apply.getCalleeFunction();
SILFunction *Caller = nullptr;
Caller = Apply.getFunction();
int numAcceptedCycles = 1;
// Name of the function to be specialized.
auto GenericFunc = Callee;
LLVM_DEBUG(llvm::dbgs() << "\n\n\nChecking for a specialization cycle:\n"
<< "Caller: " << Caller->getName() << "\n"
<< "Callee: " << Callee->getName() << "\n";
llvm::dbgs() << "Substitutions:\n";
Apply.getSubstitutionMap().dump(llvm::dbgs()));
auto *CurSpecializationInfo = Apply.getSpecializationInfo();
if (CurSpecializationInfo) {
LLVM_DEBUG(llvm::dbgs() << "Scan call-site's history\n");
} else if (Caller->isSpecialization()) {
CurSpecializationInfo = Caller->getSpecializationInfo();
LLVM_DEBUG(llvm::dbgs() << "Scan caller's specialization history\n");
}
while (CurSpecializationInfo) {
LLVM_DEBUG(llvm::dbgs() << "Current caller is a specialization:\n"
<< "Caller: "
<< CurSpecializationInfo->getCaller()->getName()
<< "\n"
<< "Parent: "
<< CurSpecializationInfo->getParent()->getName()
<< "\n";
llvm::dbgs() << "Substitutions:\n";
for (auto Replacement :
CurSpecializationInfo->getSubstitutions()
.getReplacementTypes()) {
Replacement->dump(llvm::dbgs());
});
if (CurSpecializationInfo->getParent() == GenericFunc) {
LLVM_DEBUG(llvm::dbgs() << "Found a call graph loop, checking "
"substitutions\n");
// Consider if components of the substitution list gets bigger compared to
// the previously seen specialization of the same generic function.
if (growingSubstitutions(CurSpecializationInfo->getSubstitutions(),
Apply.getSubstitutionMap())) {
LLVM_DEBUG(llvm::dbgs() << "Found a generic specialization loop!\n");
// Accept a cycles up to a limit. This is necessary to generate
// efficient code for some library functions, like compactMap, which
// contain small specialization cycles.
if (numAcceptedCycles == 0)
return true;
--numAcceptedCycles;
}
}
// Get the next element of the specialization history.
auto *CurCaller = CurSpecializationInfo->getCaller();
CurSpecializationInfo = nullptr;
if (!CurCaller)
break;
LLVM_DEBUG(llvm::dbgs() << "\nCurrent caller is: " << CurCaller->getName()
<< "\n");
if (!CurCaller->isSpecialization())
break;
CurSpecializationInfo = CurCaller->getSpecializationInfo();
}
assert(!CurSpecializationInfo);
LLVM_DEBUG(llvm::dbgs() << "Stop the scan: Current caller is not a "
"specialization\n");
return false;
}
// =============================================================================
// ReabstractionInfo
// =============================================================================
static bool shouldNotSpecialize(SILFunction *Callee, SILFunction *Caller,
SubstitutionMap Subs = {}) {
if (Callee->hasSemanticsAttr(semantics::OPTIMIZE_SIL_SPECIALIZE_GENERIC_NEVER))
return true;
if (Caller &&
Caller->getEffectiveOptimizationMode() == OptimizationMode::ForSize &&
Callee->hasSemanticsAttr(semantics::OPTIMIZE_SIL_SPECIALIZE_GENERIC_SIZE_NEVER)) {
return true;
}
if (Subs.hasAnySubstitutableParams() &&
Callee->hasSemanticsAttr(semantics::OPTIMIZE_SIL_SPECIALIZE_GENERIC_PARTIAL_NEVER))
return true;
return false;
}
/// Prepares the ReabstractionInfo object for further processing and checks
/// if the current function can be specialized at all.
/// Returns false, if the current function cannot be specialized.
/// Returns true otherwise.
bool ReabstractionInfo::prepareAndCheck(ApplySite Apply, SILFunction *Callee,
SubstitutionMap ParamSubs,
OptRemark::Emitter *ORE) {
assert(ParamSubs.hasAnySubstitutableParams());
if (shouldNotSpecialize(Callee, Apply ? Apply.getFunction() : nullptr))
return false;
SpecializedGenericEnv = nullptr;
SpecializedGenericSig = nullptr;
auto CalleeGenericSig = Callee->getLoweredFunctionType()
->getInvocationGenericSignature();
auto CalleeGenericEnv = Callee->getGenericEnvironment();
this->Callee = Callee;
this->Apply = Apply;
// Get the original substitution map.
CalleeParamSubMap = ParamSubs;
using namespace OptRemark;
// We do not support partial specialization.
if (!EnablePartialSpecialization && CalleeParamSubMap.hasArchetypes()) {
LLVM_DEBUG(llvm::dbgs() <<" Partial specialization is not supported.\n");
LLVM_DEBUG(ParamSubs.dump(llvm::dbgs()));
return false;
}
// Perform some checks to see if we need to bail.
if (CalleeParamSubMap.hasDynamicSelf()) {
REMARK_OR_DEBUG(ORE, [&]() {
return RemarkMissed("DynamicSelf", *Apply.getInstruction())
<< IndentDebug(4) << "Cannot specialize with dynamic self";
});
return false;
}
// Check if the substitution contains any generic types that are too deep.
// If this is the case, bail to avoid the explosion in the number of
// generated specializations.
for (auto Replacement : ParamSubs.getReplacementTypes()) {
if (isTypeTooComplex(Replacement)) {
REMARK_OR_DEBUG(ORE, [&]() {
return RemarkMissed("TypeTooDeep", *Apply.getInstruction())
<< IndentDebug(4)
<< "Cannot specialize because the generic type is too deep";
});
++NumPreventedTooComplexGenericSpecializations;
return false;
}
}
// Check if we have substitutions which replace generic type parameters with
// concrete types or unbound generic types.
bool HasConcreteGenericParams = false;
bool HasNonArchetypeGenericParams = false;
HasUnboundGenericParams = false;
CalleeGenericSig->forEachParam([&](GenericTypeParamType *GP, bool Canonical) {
if (!Canonical)
return;
// Check only the substitutions for the generic parameters.
// Ignore any dependent types, etc.
auto Replacement = Type(GP).subst(CalleeParamSubMap);
if (!Replacement->is<ArchetypeType>())
HasNonArchetypeGenericParams = true;
if (Replacement->hasArchetype()) {
HasUnboundGenericParams = true;
// Check if the replacement is an archetype which is more specific
// than the corresponding archetype in the original generic signature.
// If this is the case, then specialization makes sense, because
// it would produce something more specific.
if (CalleeGenericEnv) {
if (auto Archetype = Replacement->getAs<ArchetypeType>()) {
auto OrigArchetype =
CalleeGenericEnv->mapTypeIntoContext(GP)->castTo<ArchetypeType>();
if (Archetype->requiresClass() && !OrigArchetype->requiresClass())
HasNonArchetypeGenericParams = true;
if (Archetype->getLayoutConstraint() &&
!OrigArchetype->getLayoutConstraint())
HasNonArchetypeGenericParams = true;
}
}
} else {
HasConcreteGenericParams = true;
}
});
if (HasUnboundGenericParams) {
// Bail if we cannot specialize generic substitutions, but all substitutions
// were generic.
if (!HasConcreteGenericParams && !SupportGenericSubstitutions) {
LLVM_DEBUG(llvm::dbgs() << " Partial specialization is not supported "
"if all substitutions are generic.\n");
LLVM_DEBUG(ParamSubs.dump(llvm::dbgs()));
return false;
}
if (!HasNonArchetypeGenericParams && !HasConcreteGenericParams) {
LLVM_DEBUG(llvm::dbgs() << " Partial specialization is not supported "
"if all substitutions are archetypes.\n");
LLVM_DEBUG(ParamSubs.dump(llvm::dbgs()));
return false;
}
// We need a generic environment for the partial specialization.
if (!CalleeGenericEnv)
return false;
// Bail if the callee should not be partially specialized.
if (shouldNotSpecialize(Callee, Apply.getFunction(), ParamSubs))
return false;
}
// Check if specializing this call site would create in an infinite generic
// specialization loop.
if (createsInfiniteSpecializationLoop(Apply)) {
REMARK_OR_DEBUG(ORE, [&]() {
return RemarkMissed("SpecializationLoop", *Apply.getInstruction())
<< IndentDebug(4)
<< "Generic specialization is not supported if it would result in "
"a generic specialization of infinite depth. Callee "
<< NV("Callee", Callee)
<< " occurs multiple times on the call chain";
});
if (PrintGenericSpecializationLoops)
llvm::errs() << "Detected and prevented an infinite "
"generic specialization loop for callee: "
<< Callee->getName() << '\n';
++NumPreventedGenericSpecializationLoops;
return false;
}
return true;
}
bool ReabstractionInfo::canBeSpecialized(ApplySite Apply, SILFunction *Callee,
SubstitutionMap ParamSubs) {
ReabstractionInfo ReInfo;
return ReInfo.prepareAndCheck(Apply, Callee, ParamSubs);
}
ReabstractionInfo::ReabstractionInfo(
ModuleDecl *targetModule, bool isWholeModule, ApplySite Apply,
SILFunction *Callee, SubstitutionMap ParamSubs, IsSerialized_t Serialized,
bool ConvertIndirectToDirect, OptRemark::Emitter *ORE)
: ConvertIndirectToDirect(ConvertIndirectToDirect),
TargetModule(targetModule), isWholeModule(isWholeModule),
Serialized(Serialized) {
if (!prepareAndCheck(Apply, Callee, ParamSubs, ORE))
return;
SILFunction *Caller = nullptr;
if (Apply)
Caller = Apply.getFunction();
if (!EnablePartialSpecialization || !HasUnboundGenericParams) {
// Fast path for full specializations.
performFullSpecializationPreparation(Callee, ParamSubs);
} else {
performPartialSpecializationPreparation(Caller, Callee, ParamSubs);
}
verify();
if (SpecializedGenericSig) {
LLVM_DEBUG(llvm::dbgs() << "\n\nPartially specialized types for function: "
<< Callee->getName() << "\n\n";
llvm::dbgs() << "Original generic function type:\n"
<< Callee->getLoweredFunctionType() << "\n"
<< "Partially specialized generic function type:\n"
<< SpecializedType << "\n\n");
}
// Some correctness checks.
auto SpecializedFnTy = getSpecializedType();
auto SpecializedSubstFnTy = SpecializedFnTy;
if (SpecializedFnTy->isPolymorphic() &&
!getCallerParamSubstitutionMap().empty()) {
auto CalleeFnTy = Callee->getLoweredFunctionType();
assert(CalleeFnTy->isPolymorphic());
auto CalleeSubstFnTy = CalleeFnTy->substGenericArgs(
Callee->getModule(), getCalleeParamSubstitutionMap(),
getResilienceExpansion());
assert(!CalleeSubstFnTy->isPolymorphic() &&
"Substituted callee type should not be polymorphic");
assert(!CalleeSubstFnTy->hasTypeParameter() &&
"Substituted callee type should not have type parameters");
SpecializedSubstFnTy = SpecializedFnTy->substGenericArgs(
Callee->getModule(), getCallerParamSubstitutionMap(),
getResilienceExpansion());
assert(!SpecializedSubstFnTy->isPolymorphic() &&
"Substituted callee type should not be polymorphic");
assert(!SpecializedSubstFnTy->hasTypeParameter() &&
"Substituted callee type should not have type parameters");
auto SpecializedCalleeSubstFnTy =
createSpecializedType(CalleeSubstFnTy, Callee->getModule());
if (SpecializedSubstFnTy != SpecializedCalleeSubstFnTy) {
llvm::dbgs() << "SpecializedFnTy:\n" << SpecializedFnTy << "\n";
llvm::dbgs() << "SpecializedSubstFnTy:\n" << SpecializedSubstFnTy << "\n";
getCallerParamSubstitutionMap().getCanonical().dump(llvm::dbgs());
llvm::dbgs() << "\n\n";
llvm::dbgs() << "CalleeFnTy:\n" << CalleeFnTy << "\n";
llvm::dbgs() << "SpecializedCalleeSubstFnTy:\n" << SpecializedCalleeSubstFnTy << "\n";
ParamSubs.getCanonical().dump(llvm::dbgs());
llvm::dbgs() << "\n\n";
assert(SpecializedSubstFnTy == SpecializedCalleeSubstFnTy &&
"Substituted function types should be the same");
}
}
// If the new type is the same, there is nothing to do and
// no specialization should be performed.
if (getSubstitutedType() == Callee->getLoweredFunctionType()) {
LLVM_DEBUG(llvm::dbgs() << "The new specialized type is the same as "
"the original type. Don't specialize!\n";
llvm::dbgs() << "The type is: " << getSubstitutedType() << "\n");
SpecializedType = CanSILFunctionType();
SubstitutedType = CanSILFunctionType();
SpecializedGenericSig = nullptr;
SpecializedGenericEnv = nullptr;
return;
}
if (SpecializedGenericSig) {
// It is a partial specialization.
LLVM_DEBUG(llvm::dbgs() << "Specializing the call:\n";
Apply.getInstruction()->dumpInContext();
llvm::dbgs() << "\n\nPartially specialized types for function: "
<< Callee->getName() << "\n\n";
llvm::dbgs() << "Callee generic function type:\n"
<< Callee->getLoweredFunctionType() << "\n\n";
llvm::dbgs() << "Callee's call substitution:\n";
getCalleeParamSubstitutionMap().getCanonical().dump(llvm::dbgs());
llvm::dbgs() << "Partially specialized generic function type:\n"
<< getSpecializedType() << "\n\n";
llvm::dbgs() << "\nSpecialization call substitution:\n";
getCallerParamSubstitutionMap().getCanonical().dump(llvm::dbgs());
);
}
}
bool ReabstractionInfo::canBeSpecialized() const {
return getSpecializedType();
}
bool ReabstractionInfo::isFullSpecialization() const {
return !getCalleeParamSubstitutionMap().hasArchetypes();
}
bool ReabstractionInfo::isPartialSpecialization() const {
return getCalleeParamSubstitutionMap().hasArchetypes();
}
void ReabstractionInfo::createSubstitutedAndSpecializedTypes() {
auto &M = Callee->getModule();
// Find out how the function type looks like after applying the provided
// substitutions.
if (!SubstitutedType) {
SubstitutedType = createSubstitutedType(Callee, CallerInterfaceSubs,
HasUnboundGenericParams);
}
assert(!SubstitutedType->hasArchetype() &&
"Substituted function type should not contain archetypes");
// Check which parameters and results can be converted from
// indirect to direct ones.
NumFormalIndirectResults = SubstitutedType->getNumIndirectFormalResults();
unsigned NumArgs = NumFormalIndirectResults +
SubstitutedType->getParameters().size();
Conversions.resize(NumArgs);
TrivialArgs.resize(NumArgs);
SILFunctionConventions substConv(SubstitutedType, M);
TypeExpansionContext resilienceExp = getResilienceExpansion();
TypeExpansionContext minimalExp(ResilienceExpansion::Minimal,
TargetModule, isWholeModule);
if (SubstitutedType->getNumDirectFormalResults() == 0) {
// The original function has no direct result yet. Try to convert the first
// indirect result to a direct result.
// TODO: We could also convert multiple indirect results by returning a
// tuple type and created tuple_extract instructions at the call site.
unsigned IdxForResult = 0;
for (SILResultInfo RI : SubstitutedType->getIndirectFormalResults()) {
assert(RI.isFormalIndirect());
TypeCategory tc = getReturnTypeCategory(RI, substConv, resilienceExp);
if (tc != NotLoadable) {
Conversions.set(IdxForResult);
if (tc == LoadableAndTrivial)
TrivialArgs.set(IdxForResult);
if (resilienceExp != minimalExp &&
getReturnTypeCategory(RI, substConv, minimalExp) == NotLoadable) {
hasConvertedResilientParams = true;
}
break;
}
++IdxForResult;
}
}
// Try to convert indirect incoming parameters to direct parameters.
unsigned IdxForParam = NumFormalIndirectResults;
for (SILParameterInfo PI : SubstitutedType->getParameters()) {
auto IdxToInsert = IdxForParam;
++IdxForParam;
TypeCategory tc = getParamTypeCategory(PI, substConv, resilienceExp);
if (tc == NotLoadable)
continue;
switch (PI.getConvention()) {
case ParameterConvention::Indirect_In:
case ParameterConvention::Indirect_In_Guaranteed:
Conversions.set(IdxToInsert);
if (tc == LoadableAndTrivial)
TrivialArgs.set(IdxToInsert);
if (resilienceExp != minimalExp &&
getParamTypeCategory(PI, substConv, minimalExp) == NotLoadable) {
hasConvertedResilientParams = true;
}
break;
case ParameterConvention::Indirect_In_Constant:
case ParameterConvention::Indirect_Inout:
case ParameterConvention::Indirect_InoutAliasable:
case ParameterConvention::Direct_Owned:
case ParameterConvention::Direct_Unowned:
case ParameterConvention::Direct_Guaranteed:
break;
}
}
// Produce a specialized type, which is the substituted type with
// the parameters/results passing conventions adjusted according
// to the conversions selected above.
SpecializedType = createSpecializedType(SubstitutedType, M);
}
ReabstractionInfo::TypeCategory ReabstractionInfo::
getReturnTypeCategory(const SILResultInfo &RI,
const SILFunctionConventions &substConv,
TypeExpansionContext typeExpansion) {
auto &M = Callee->getModule();
auto ResultTy = substConv.getSILType(RI, typeExpansion);
ResultTy = Callee->mapTypeIntoContext(ResultTy);
auto &TL = M.Types.getTypeLowering(ResultTy, typeExpansion);
if (!TL.isLoadable())
return NotLoadable;
if (RI.getReturnValueType(M, SubstitutedType, typeExpansion)
->isVoid())
return NotLoadable;
if (!shouldExpand(M, ResultTy))
return NotLoadable;
return TL.isTrivial() ? LoadableAndTrivial : Loadable;
}
ReabstractionInfo::TypeCategory ReabstractionInfo::
getParamTypeCategory(const SILParameterInfo &PI,
const SILFunctionConventions &substConv,
TypeExpansionContext typeExpansion) {
auto &M = Callee->getModule();
auto ParamTy = substConv.getSILType(PI, typeExpansion);
ParamTy = Callee->mapTypeIntoContext(ParamTy);
auto &TL = M.Types.getTypeLowering(ParamTy, typeExpansion);
if (!TL.isLoadable())
return NotLoadable;
return TL.isTrivial() ? LoadableAndTrivial : Loadable;
}
/// Create a new substituted type with the updated signature.
CanSILFunctionType
ReabstractionInfo::createSubstitutedType(SILFunction *OrigF,
SubstitutionMap SubstMap,
bool HasUnboundGenericParams) {
auto &M = OrigF->getModule();
if ((SpecializedGenericSig &&
SpecializedGenericSig->areAllParamsConcrete()) ||
!HasUnboundGenericParams) {
SpecializedGenericSig = nullptr;
SpecializedGenericEnv = nullptr;
}
auto CanSpecializedGenericSig = SpecializedGenericSig.getCanonicalSignature();
// First substitute concrete types into the existing function type.
CanSILFunctionType FnTy;
{
FnTy = OrigF->getLoweredFunctionType()
->substGenericArgs(M, SubstMap, getResilienceExpansion())
->getUnsubstitutedType(M);
// FIXME: Some of the added new requirements may not have been taken into
// account by the substGenericArgs. So, canonicalize in the context of the
// specialized signature.
if (CanSpecializedGenericSig)
FnTy = cast<SILFunctionType>(
CanSpecializedGenericSig->getCanonicalTypeInContext(FnTy));
else {
FnTy = cast<SILFunctionType>(FnTy->getCanonicalType());
assert(!FnTy->hasTypeParameter() && "Type parameters outside generic context?");
}
}
assert(FnTy);
// Use the new specialized generic signature.
auto NewFnTy = SILFunctionType::get(
CanSpecializedGenericSig, FnTy->getExtInfo(), FnTy->getCoroutineKind(),
FnTy->getCalleeConvention(), FnTy->getParameters(), FnTy->getYields(),
FnTy->getResults(), FnTy->getOptionalErrorResult(),
FnTy->getPatternSubstitutions(), SubstitutionMap(), M.getASTContext(),
FnTy->getWitnessMethodConformanceOrInvalid());
// This is an interface type. It should not have any archetypes.
assert(!NewFnTy->hasArchetype());
return NewFnTy;
}
/// Convert the substituted function type into a specialized function type based
/// on the ReabstractionInfo.
CanSILFunctionType ReabstractionInfo::
createSpecializedType(CanSILFunctionType SubstFTy, SILModule &M) const {
SmallVector<SILResultInfo, 8> SpecializedResults;
SmallVector<SILYieldInfo, 8> SpecializedYields;
SmallVector<SILParameterInfo, 8> SpecializedParams;
auto context = getResilienceExpansion();
unsigned IndirectResultIdx = 0;
for (SILResultInfo RI : SubstFTy->getResults()) {
RI = RI.getUnsubstituted(M, SubstFTy, context);
if (RI.isFormalIndirect()) {
bool isTrivial = TrivialArgs.test(IndirectResultIdx);
if (isFormalResultConverted(IndirectResultIdx++)) {
// Convert the indirect result to a direct result.
// Indirect results are passed as owned, so we also need to pass the
// direct result as owned (except it's a trivial type).
auto C = (isTrivial
? ResultConvention::Unowned
: ResultConvention::Owned);
SpecializedResults.push_back(RI.getWithConvention(C));
continue;
}
}
// No conversion: re-use the original, substituted result info.
SpecializedResults.push_back(RI);
}
unsigned ParamIdx = 0;
for (SILParameterInfo PI : SubstFTy->getParameters()) {
PI = PI.getUnsubstituted(M, SubstFTy, context);
bool isTrivial = TrivialArgs.test(param2ArgIndex(ParamIdx));
if (!isParamConverted(ParamIdx++)) {
// No conversion: re-use the original, substituted parameter info.
SpecializedParams.push_back(PI);
continue;
}
// Convert the indirect parameter to a direct parameter.
// Indirect parameters are passed as owned/guaranteed, so we also
// need to pass the direct/guaranteed parameter as
// owned/guaranteed (except it's a trivial type).
auto C = ParameterConvention::Direct_Unowned;
if (!isTrivial) {
if (PI.isGuaranteed()) {
C = ParameterConvention::Direct_Guaranteed;
} else {
C = ParameterConvention::Direct_Owned;
}
}
SpecializedParams.push_back(PI.getWithConvention(C));
}
for (SILYieldInfo YI : SubstFTy->getYields()) {
// For now, always re-use the original, substituted yield info.
SpecializedYields.push_back(YI.getUnsubstituted(M, SubstFTy, context));
}
auto Signature = SubstFTy->isPolymorphic()
? SubstFTy->getInvocationGenericSignature()
: CanGenericSignature();
return SILFunctionType::get(
Signature, SubstFTy->getExtInfo(),
SubstFTy->getCoroutineKind(), SubstFTy->getCalleeConvention(),
SpecializedParams, SpecializedYields, SpecializedResults,
SubstFTy->getOptionalErrorResult(), SubstitutionMap(), SubstitutionMap(),
M.getASTContext(), SubstFTy->getWitnessMethodConformanceOrInvalid());
}
/// Create a new generic signature from an existing one by adding
/// additional requirements.
static std::pair<GenericEnvironment *, GenericSignature>
getGenericEnvironmentAndSignatureWithRequirements(
GenericSignature OrigGenSig, GenericEnvironment *OrigGenericEnv,
ArrayRef<Requirement> Requirements, SILModule &M) {
SmallVector<Requirement, 2> RequirementsCopy(Requirements.begin(),
Requirements.end());
auto NewGenSig = evaluateOrDefault(
M.getASTContext().evaluator,
AbstractGenericSignatureRequest{
OrigGenSig.getPointer(), { }, std::move(RequirementsCopy)},
GenericSignature());
auto NewGenEnv = NewGenSig->getGenericEnvironment();
return { NewGenEnv, NewGenSig };
}
/// This is a fast path for full specializations.
/// There is no need to form a new generic signature in such cases,
/// because the specialized function will be non-generic.
void ReabstractionInfo::performFullSpecializationPreparation(
SILFunction *Callee, SubstitutionMap ParamSubs) {
assert((!EnablePartialSpecialization || !HasUnboundGenericParams) &&
"Only full specializations are handled here");
SILModule &M = Callee->getModule();
this->Callee = Callee;
// Get the original substitution map.
ClonerParamSubMap = ParamSubs;
SubstitutedType = Callee->getLoweredFunctionType()->substGenericArgs(
M, ClonerParamSubMap, getResilienceExpansion());
CallerParamSubMap = {};
createSubstitutedAndSpecializedTypes();
}
/// If the archetype (or any of its dependent types) has requirements
/// depending on other archetypes, return true.
/// Otherwise return false.
static bool hasNonSelfContainedRequirements(ArchetypeType *Archetype,
GenericSignature Sig,
GenericEnvironment *Env) {
auto Reqs = Sig->getRequirements();
auto CurrentGP = Archetype->getInterfaceType()
->getCanonicalType()
->getRootGenericParam();
for (auto Req : Reqs) {
switch(Req.getKind()) {
case RequirementKind::Conformance:
case RequirementKind::Superclass:
case RequirementKind::Layout:
// FIXME: Second type of a superclass requirement may contain
// generic parameters.
continue;
case RequirementKind::SameType: {
// Check if this requirement contains more than one generic param.
// If this is the case, then these archetypes are interdependent and
// we should return true.
auto First = Req.getFirstType()->getCanonicalType();
auto Second = Req.getSecondType()->getCanonicalType();
SmallSetVector<TypeBase *, 2> UsedGenericParams;
First.visit([&](Type Ty) {
if (auto *GP = Ty->getAs<GenericTypeParamType>()) {
UsedGenericParams.insert(GP);
}
});
Second.visit([&](Type Ty) {
if (auto *GP = Ty->getAs<GenericTypeParamType>()) {
UsedGenericParams.insert(GP);
}
});
if (UsedGenericParams.count(CurrentGP) && UsedGenericParams.size() > 1)
return true;
}
}
}
return false;
}
/// Collect all requirements for a generic parameter corresponding to a given
/// archetype.
static void collectRequirements(ArchetypeType *Archetype, GenericSignature Sig,
GenericEnvironment *Env,
SmallVectorImpl<Requirement> &CollectedReqs) {
auto Reqs = Sig->getRequirements();
auto CurrentGP = Archetype->getInterfaceType()
->getCanonicalType()
->getRootGenericParam();
CollectedReqs.clear();
for (auto Req : Reqs) {
switch(Req.getKind()) {
case RequirementKind::Conformance:
case RequirementKind::Superclass:
case RequirementKind::Layout:
// If it is a generic param or something derived from it, add this
// requirement.
// FIXME: Second type of a superclass requirement may contain
// generic parameters.
if (Req.getFirstType()->getCanonicalType()->getRootGenericParam() ==
CurrentGP)
CollectedReqs.push_back(Req);
continue;
case RequirementKind::SameType: {
// Check if this requirement contains more than one generic param.
// If this is the case, then these archetypes are interdependent and
// we should return true.
auto First = Req.getFirstType()->getCanonicalType();
auto Second = Req.getSecondType()->getCanonicalType();
SmallSetVector<GenericTypeParamType *, 2> UsedGenericParams;
First.visit([&](Type Ty) {
if (auto *GP = Ty->getAs<GenericTypeParamType>()) {
UsedGenericParams.insert(GP);
}
});
Second.visit([&](Type Ty) {
if (auto *GP = Ty->getAs<GenericTypeParamType>()) {
UsedGenericParams.insert(GP);
}
});
if (!UsedGenericParams.count(CurrentGP))
continue;
if (UsedGenericParams.size() != 1) {
llvm::dbgs() << "Strange requirement for "
<< CurrentGP->getCanonicalType() << "\n";
Req.dump(llvm::dbgs());
}
assert(UsedGenericParams.size() == 1);
CollectedReqs.push_back(Req);
continue;
}
}
}
}
/// Returns true if a given substitution should participate in the
/// partial specialization.
///
/// TODO:
/// If a replacement is an archetype or a dependent type
/// of an archetype, then it does not make sense to substitute
/// it into the signature of the specialized function, because
/// it does not provide any benefits at runtime and may actually
/// lead to performance degradations.
///
/// If a replacement is a loadable type, it is most likely
/// rather beneficial to specialize using this substitution, because
/// it would allow for more efficient codegen for this type.
///
/// If a substitution simply replaces a generic parameter in the callee
/// by a generic parameter in the caller and this generic parameter
/// in the caller does have more "specific" conformances or requirements,
/// then it does name make any sense to perform this substitutions.
/// In particular, if the generic parameter in the callee is unconstrained
/// (i.e. just T), then providing a more specific generic parameter with some
/// conformances does not help, because the body of the callee does not invoke
/// any methods from any of these new conformances, unless these conformances
/// or requirements influence the layout of the generic type, e.g. "class",
/// "Trivial of size N", "HeapAllocationObject", etc.
/// (NOTE: It could be that additional conformances can still be used due
/// to conditional conformances or something like that, if the caller
/// has an invocation like: "G<T>().method(...)". In this case, G<T>().method()
/// and G<T:P>().method() may be resolved differently).
///
/// We may need to analyze the uses of the generic type inside
/// the function body (recursively). It is ever loaded/stored?
/// Do we create objects of this type? Which conformances are
/// really used?
static bool
shouldBePartiallySpecialized(Type Replacement,
GenericSignature Sig, GenericEnvironment *Env) {
// If replacement is a concrete type, this substitution
// should participate.
if (!Replacement->hasArchetype())
return true;
// We cannot handle opened existentials yet.
if (Replacement->hasOpenedExistential())
return false;
if (!SupportGenericSubstitutions) {
// Don't partially specialize if the replacement contains an archetype.
if (Replacement->hasArchetype())
return false;
}
// If the archetype used (or any of its dependent types) has requirements
// depending on other caller's archetypes, then we don't want to specialize
// on it as it may require introducing more generic parameters, which
// is not beneficial.
// Collect the archetypes used by the replacement type.
llvm::SmallSetVector<ArchetypeType *, 2> UsedArchetypes;
Replacement.visit([&](Type Ty) {
if (auto Archetype = Ty->getAs<ArchetypeType>()) {
if (auto Primary = dyn_cast<PrimaryArchetypeType>(Archetype->getRoot())) {
UsedArchetypes.insert(Primary);
}
}
});
// Check if any of the used archetypes are non-self contained when
// it comes to requirements.
for (auto *UsedArchetype : UsedArchetypes) {
if (hasNonSelfContainedRequirements(UsedArchetype, Sig, Env)) {
LLVM_DEBUG(llvm::dbgs() << "Requirements of the archetype depend on "
"other caller's generic parameters! "
"It cannot be partially specialized:\n";
UsedArchetype->dump(llvm::dbgs());
llvm::dbgs() << "This archetype is used in the substitution: "
<< Replacement << "\n");
return false;
}
}
if (OptimizeGenericSubstitutions) {
// Is it an unconstrained generic parameter?
if (auto Archetype = Replacement->getAs<ArchetypeType>()) {
if (Archetype->getConformsTo().empty()) {
// TODO: If Replacement add a new layout constraint, then
// it may be still useful to perform the partial specialization.
return false;
}
}
}
return true;
}
namespace swift {
/// A helper class for creating partially specialized function signatures.
///
/// The following naming convention is used to describe the members and
/// functions:
/// Caller - the function which invokes the callee.
/// Callee - the callee to be specialized.
/// Specialized - the specialized callee which is being created.
class FunctionSignaturePartialSpecializer {
/// Maps caller's generic parameters to generic parameters of the specialized
/// function.
llvm::DenseMap<SubstitutableType *, Type>
CallerInterfaceToSpecializedInterfaceMapping;
/// Maps callee's generic parameters to generic parameters of the specialized
/// function.
llvm::DenseMap<SubstitutableType *, Type>
CalleeInterfaceToSpecializedInterfaceMapping;
/// Maps the generic parameters of the specialized function to the caller's
/// contextual types.
llvm::DenseMap<SubstitutableType *, Type>
SpecializedInterfaceToCallerArchetypeMapping;
/// A SubstitutionMap for re-mapping caller's interface types
/// to interface types of the specialized function.
SubstitutionMap CallerInterfaceToSpecializedInterfaceMap;
/// Maps callee's interface types to caller's contextual types.
/// It is computed from the original substitutions.
SubstitutionMap CalleeInterfaceToCallerArchetypeMap;
/// Maps callee's interface types to specialized functions interface types.
SubstitutionMap CalleeInterfaceToSpecializedInterfaceMap;
/// Maps the generic parameters of the specialized function to the caller's
/// contextual types.
SubstitutionMap SpecializedInterfaceToCallerArchetypeMap;
/// Generic signatures and environments for the caller, callee and
/// the specialized function.
GenericSignature CallerGenericSig;
GenericEnvironment *CallerGenericEnv;
GenericSignature CalleeGenericSig;
GenericEnvironment *CalleeGenericEnv;
GenericSignature SpecializedGenericSig;
GenericEnvironment *SpecializedGenericEnv;
SILModule &M;
ModuleDecl *SM;
ASTContext &Ctx;
/// Set of newly created generic type parameters.
SmallVector<GenericTypeParamType*, 2> AllGenericParams;
/// Set of newly created requirements.
SmallVector<Requirement, 2> AllRequirements;
/// Archetypes used in the substitutions of an apply instructions.
/// These are the contextual archetypes of the caller function, which
/// invokes a generic function that is being specialized.
llvm::SmallSetVector<ArchetypeType *, 2> UsedCallerArchetypes;
/// Number of created generic parameters so far.
unsigned GPIdx = 0;
void createGenericParamsForUsedCallerArchetypes();
void createGenericParamsForCalleeGenericParams();
void addRequirements(ArrayRef<Requirement> Reqs, SubstitutionMap &SubsMap);
void addCallerRequirements();
void addCalleeRequirements();
std::pair<GenericEnvironment *, GenericSignature>
getSpecializedGenericEnvironmentAndSignature();
void computeCallerInterfaceToSpecializedInterfaceMap();
void computeCalleeInterfaceToSpecializedInterfaceMap();
void computeSpecializedInterfaceToCallerArchetypeMap();
/// Collect all used archetypes from all the substitutions.
/// Take into account only those archetypes that occur in the
/// substitutions of generic parameters which will be partially
/// specialized. Ignore all others.
void collectUsedCallerArchetypes(SubstitutionMap ParamSubs);
/// Create a new generic parameter.
GenericTypeParamType *createGenericParam();
public:
FunctionSignaturePartialSpecializer(SILModule &M,
GenericSignature CallerGenericSig,
GenericEnvironment *CallerGenericEnv,
GenericSignature CalleeGenericSig,
GenericEnvironment *CalleeGenericEnv,
SubstitutionMap ParamSubs)
: CallerGenericSig(CallerGenericSig), CallerGenericEnv(CallerGenericEnv),
CalleeGenericSig(CalleeGenericSig), CalleeGenericEnv(CalleeGenericEnv),
M(M), SM(M.getSwiftModule()), Ctx(M.getASTContext()) {
SpecializedGenericSig = nullptr;
SpecializedGenericEnv = nullptr;
CalleeInterfaceToCallerArchetypeMap = ParamSubs;
}
/// This constructor is used by when processing @_specialize.
/// In this case, the caller and the callee are the same function.
FunctionSignaturePartialSpecializer(SILModule &M,
GenericSignature CalleeGenericSig,
GenericEnvironment *CalleeGenericEnv,
GenericSignature SpecializedSig)
: CallerGenericSig(CalleeGenericSig), CallerGenericEnv(CalleeGenericEnv),
CalleeGenericSig(CalleeGenericSig), CalleeGenericEnv(CalleeGenericEnv),
SpecializedGenericSig(SpecializedSig),
M(M), SM(M.getSwiftModule()), Ctx(M.getASTContext()) {
// Create the new generic signature using provided requirements.
SpecializedGenericEnv = SpecializedGenericSig->getGenericEnvironment();
// Compute SubstitutionMaps required for re-mapping.
// Callee's generic signature and specialized generic signature
// use the same set of generic parameters, i.e. each generic
// parameter should be mapped to itself.
for (auto GP : CalleeGenericSig->getGenericParams()) {
CalleeInterfaceToSpecializedInterfaceMapping[GP] = Type(GP);
}
computeCalleeInterfaceToSpecializedInterfaceMap();
// Each generic parameter of the callee is mapped to its own
// archetype.
SpecializedInterfaceToCallerArchetypeMap =
SubstitutionMap::get(
SpecializedGenericSig,
[&](SubstitutableType *type) -> Type {
return CalleeGenericEnv->mapTypeIntoContext(type);
},
LookUpConformanceInSignature(SpecializedGenericSig.getPointer()));
}
GenericSignature getSpecializedGenericSignature() {
return SpecializedGenericSig;
}
GenericEnvironment *getSpecializedGenericEnvironment() {
return SpecializedGenericEnv;
}
void createSpecializedGenericSignature(SubstitutionMap ParamSubs);
void createSpecializedGenericSignatureWithNonGenericSubs();
SubstitutionMap computeClonerParamSubs();
SubstitutionMap getCallerParamSubs();
void computeCallerInterfaceSubs(SubstitutionMap &CallerInterfaceSubs);
};
} // end of namespace
GenericTypeParamType *
FunctionSignaturePartialSpecializer::createGenericParam() {
auto GP = GenericTypeParamType::get(0, GPIdx++, Ctx);
AllGenericParams.push_back(GP);
return GP;
}
/// Collect all used caller's archetypes from all the substitutions.
void FunctionSignaturePartialSpecializer::collectUsedCallerArchetypes(
SubstitutionMap ParamSubs) {
for (auto Replacement : ParamSubs.getReplacementTypes()) {
if (!Replacement->hasArchetype())
continue;
// If the substitution will not be performed in the specialized
// function, there is no need to check for any archetypes inside
// the replacement.
if (!shouldBePartiallySpecialized(Replacement,
CallerGenericSig, CallerGenericEnv))
continue;
// Add used generic parameters/archetypes.
Replacement.visit([&](Type Ty) {
if (auto Archetype = Ty->getAs<ArchetypeType>()) {
if (auto Primary = dyn_cast<PrimaryArchetypeType>(Archetype->getRoot())) {
UsedCallerArchetypes.insert(Primary);
}
}
});
}
}
void FunctionSignaturePartialSpecializer::
computeCallerInterfaceToSpecializedInterfaceMap() {
if (!CallerGenericSig)
return;
CallerInterfaceToSpecializedInterfaceMap =
SubstitutionMap::get(
CallerGenericSig,
[&](SubstitutableType *type) -> Type {
return CallerInterfaceToSpecializedInterfaceMapping.lookup(type);
},
LookUpConformanceInSignature(CallerGenericSig.getPointer()));
LLVM_DEBUG(llvm::dbgs() << "\n\nCallerInterfaceToSpecializedInterfaceMap "
"map:\n";
CallerInterfaceToSpecializedInterfaceMap.dump(llvm::dbgs()));
}
void FunctionSignaturePartialSpecializer::
computeSpecializedInterfaceToCallerArchetypeMap() {
// Define a substitution map for re-mapping interface types of
// the specialized function to contextual types of the caller.
SpecializedInterfaceToCallerArchetypeMap =
SubstitutionMap::get(
SpecializedGenericSig,
[&](SubstitutableType *type) -> Type {
LLVM_DEBUG(llvm::dbgs() << "Mapping specialized interface type to "
"caller archetype:\n";
llvm::dbgs() << "Interface type: "; type->dump(llvm::dbgs());
llvm::dbgs() << "Archetype: ";
auto Archetype =
SpecializedInterfaceToCallerArchetypeMapping.lookup(type);
if (Archetype) Archetype->dump(llvm::dbgs());
else llvm::dbgs() << "Not found!\n";);
return SpecializedInterfaceToCallerArchetypeMapping.lookup(type);
},
LookUpConformanceInSignature(SpecializedGenericSig.getPointer()));
LLVM_DEBUG(llvm::dbgs() << "\n\nSpecializedInterfaceToCallerArchetypeMap "
"map:\n";
SpecializedInterfaceToCallerArchetypeMap.dump(llvm::dbgs()));
}
void FunctionSignaturePartialSpecializer::
computeCalleeInterfaceToSpecializedInterfaceMap() {
CalleeInterfaceToSpecializedInterfaceMap =
SubstitutionMap::get(
CalleeGenericSig,
[&](SubstitutableType *type) -> Type {
return CalleeInterfaceToSpecializedInterfaceMapping.lookup(type);
},
LookUpConformanceInSignature(CalleeGenericSig.getPointer()));
LLVM_DEBUG(llvm::dbgs() << "\n\nCalleeInterfaceToSpecializedInterfaceMap:\n";
CalleeInterfaceToSpecializedInterfaceMap.dump(llvm::dbgs()));
}
/// Generate a new generic type parameter for each used archetype from
/// the caller.
void FunctionSignaturePartialSpecializer::
createGenericParamsForUsedCallerArchetypes() {
for (auto CallerArchetype : UsedCallerArchetypes) {
auto CallerGenericParam = CallerArchetype->getInterfaceType();
assert(CallerGenericParam->is<GenericTypeParamType>());
LLVM_DEBUG(llvm::dbgs() << "\n\nChecking used caller archetype:\n";
CallerArchetype->dump(llvm::dbgs());
llvm::dbgs() << "It corresponds to the caller generic "
"parameter:\n";
CallerGenericParam->dump(llvm::dbgs()));
// Create an equivalent generic parameter.
auto SubstGenericParam = createGenericParam();
auto SubstGenericParamCanTy = SubstGenericParam->getCanonicalType();
(void)SubstGenericParamCanTy;
CallerInterfaceToSpecializedInterfaceMapping
[CallerGenericParam->getCanonicalType()
->castTo<GenericTypeParamType>()] = SubstGenericParam;
SpecializedInterfaceToCallerArchetypeMapping[SubstGenericParam] =
CallerArchetype;
LLVM_DEBUG(llvm::dbgs() << "\nCreated a new specialized generic "
"parameter:\n";
SubstGenericParam->dump(llvm::dbgs());
llvm::dbgs() << "Created a mapping "
"(caller interface -> specialize interface):\n"
<< CallerGenericParam << " -> "
<< SubstGenericParamCanTy << "\n";
llvm::dbgs() << "Created a mapping"
"(specialized interface -> caller archetype):\n"
<< SubstGenericParamCanTy << " -> "
<< CallerArchetype->getCanonicalType() << "\n");
}
}
/// Create a new generic parameter for each of the callee's generic parameters
/// which requires a substitution.
void FunctionSignaturePartialSpecializer::
createGenericParamsForCalleeGenericParams() {
for (auto GP : CalleeGenericSig->getGenericParams()) {
auto CanTy = GP->getCanonicalType();
auto CanTyInContext =
CalleeGenericSig->getCanonicalTypeInContext(CanTy);
auto Replacement = CanTyInContext.subst(CalleeInterfaceToCallerArchetypeMap);
LLVM_DEBUG(llvm::dbgs() << "\n\nChecking callee generic parameter:\n";
CanTy->dump(llvm::dbgs()));
if (!Replacement) {
LLVM_DEBUG(llvm::dbgs() << "No replacement found. Skipping.\n");
continue;
}
LLVM_DEBUG(llvm::dbgs() << "Replacement found:\n";
Replacement->dump(llvm::dbgs()));
bool ShouldSpecializeGP = shouldBePartiallySpecialized(
Replacement, CallerGenericSig, CallerGenericEnv);
if (ShouldSpecializeGP) {
LLVM_DEBUG(llvm::dbgs() << "Should be partially specialized.\n");
} else {
LLVM_DEBUG(llvm::dbgs() << "Should not be partially specialized.\n");
}
// Create an equivalent generic parameter in the specialized
// generic environment.
auto SubstGenericParam = createGenericParam();
auto SubstGenericParamCanTy = SubstGenericParam->getCanonicalType();
// Remember which specialized generic parameter correspond's to callee's
// generic parameter.
CalleeInterfaceToSpecializedInterfaceMapping[GP] = SubstGenericParam;
LLVM_DEBUG(llvm::dbgs() << "\nCreated a new specialized generic "
"parameter:\n";
SubstGenericParam->dump(llvm::dbgs());
llvm::dbgs() << "Created a mapping "
"(callee interface -> specialized interface):\n"
<< CanTy << " -> "
<< SubstGenericParamCanTy << "\n");
if (!ShouldSpecializeGP) {
// Remember the original substitution from the apply instruction.
SpecializedInterfaceToCallerArchetypeMapping[SubstGenericParam] =
Replacement;
LLVM_DEBUG(llvm::dbgs() << "Created a mapping (specialized interface -> "
"caller archetype):\n"
<< Type(SubstGenericParam) << " -> "
<< Replacement << "\n");
continue;
}
// Add a same type requirement based on the provided generic parameter
// substitutions.
auto ReplacementCallerInterfaceTy = Replacement->mapTypeOutOfContext();
auto SpecializedReplacementCallerInterfaceTy =
ReplacementCallerInterfaceTy.subst(
CallerInterfaceToSpecializedInterfaceMap);
assert(!SpecializedReplacementCallerInterfaceTy->hasError());
Requirement Req(RequirementKind::SameType, SubstGenericParamCanTy,
SpecializedReplacementCallerInterfaceTy);
AllRequirements.push_back(Req);
LLVM_DEBUG(llvm::dbgs() << "Added a requirement:\n";
Req.dump(llvm::dbgs()));
if (ReplacementCallerInterfaceTy->is<GenericTypeParamType>()) {
// Remember that the new generic parameter corresponds
// to the same caller archetype, which corresponds to
// the ReplacementCallerInterfaceTy.
SpecializedInterfaceToCallerArchetypeMapping[SubstGenericParam] =
SpecializedInterfaceToCallerArchetypeMapping.lookup(
ReplacementCallerInterfaceTy
.subst(CallerInterfaceToSpecializedInterfaceMap)
->castTo<SubstitutableType>());
LLVM_DEBUG(llvm::dbgs()
<< "Created a mapping (specialized interface -> "
"caller archetype):\n"
<< Type(SubstGenericParam) << " -> "
<< SpecializedInterfaceToCallerArchetypeMapping[SubstGenericParam]
->getCanonicalType()
<< "\n");
continue;
}
SpecializedInterfaceToCallerArchetypeMapping[SubstGenericParam] =
Replacement;
LLVM_DEBUG(llvm::dbgs()
<< "Created a mapping (specialized interface -> "
"caller archetype):\n"
<< Type(SubstGenericParam) << " -> "
<< SpecializedInterfaceToCallerArchetypeMapping[SubstGenericParam]
->getCanonicalType()
<< "\n");
}
}
/// Add requirements from a given list of requirements re-mapping them using
/// the provided SubstitutionMap.
void FunctionSignaturePartialSpecializer::addRequirements(
ArrayRef<Requirement> Reqs, SubstitutionMap &SubsMap) {
for (auto &reqReq : Reqs) {
LLVM_DEBUG(llvm::dbgs() << "\n\nRe-mapping the requirement:\n";
reqReq.dump(llvm::dbgs()));
AllRequirements.push_back(*reqReq.subst(SubsMap));
}
}
/// Add requirements from the caller's signature.
void FunctionSignaturePartialSpecializer::addCallerRequirements() {
for (auto CallerArchetype : UsedCallerArchetypes) {
// Add requirements for this caller generic parameter and its dependent
// types.
SmallVector<Requirement, 4> CollectedReqs;
collectRequirements(CallerArchetype, CallerGenericSig, CallerGenericEnv,
CollectedReqs);
if (!CollectedReqs.empty()) {
LLVM_DEBUG(llvm::dbgs() << "Adding caller archetype requirements:\n";
for (auto Req : CollectedReqs) {
Req.dump(llvm::dbgs());
}
CallerInterfaceToSpecializedInterfaceMap.dump(llvm::dbgs());
);
addRequirements(CollectedReqs, CallerInterfaceToSpecializedInterfaceMap);
}
}
}
/// Add requirements from the callee's signature.
void FunctionSignaturePartialSpecializer::addCalleeRequirements() {
if (CalleeGenericSig)
addRequirements(CalleeGenericSig->getRequirements(),
CalleeInterfaceToSpecializedInterfaceMap);
}
std::pair<GenericEnvironment *, GenericSignature>
FunctionSignaturePartialSpecializer::
getSpecializedGenericEnvironmentAndSignature() {
if (AllGenericParams.empty())
return { nullptr, nullptr };
// Finalize the archetype builder.
auto GenSig = evaluateOrDefault(
Ctx.evaluator,
AbstractGenericSignatureRequest{nullptr, AllGenericParams, AllRequirements},
GenericSignature());
auto GenEnv = GenSig ? GenSig->getGenericEnvironment() : nullptr;
return { GenEnv, GenSig };
}
SubstitutionMap FunctionSignaturePartialSpecializer::computeClonerParamSubs() {
return SubstitutionMap::get(
CalleeGenericSig,
[&](SubstitutableType *type) -> Type {
LLVM_DEBUG(llvm::dbgs() << "\ngetSubstitution for ClonerParamSubs:\n"
<< Type(type) << "\n"
<< "in generic signature:\n";
CalleeGenericSig->print(llvm::dbgs()));
auto SpecializedInterfaceTy =
Type(type).subst(CalleeInterfaceToSpecializedInterfaceMap);
return SpecializedGenericEnv->mapTypeIntoContext(
SpecializedInterfaceTy);
},
LookUpConformanceInSignature(SpecializedGenericSig.getPointer()));
}
SubstitutionMap FunctionSignaturePartialSpecializer::getCallerParamSubs() {
return SpecializedInterfaceToCallerArchetypeMap;
}
void FunctionSignaturePartialSpecializer::computeCallerInterfaceSubs(
SubstitutionMap &CallerInterfaceSubs) {
CallerInterfaceSubs = SubstitutionMap::get(
CalleeGenericSig,
[&](SubstitutableType *type) -> Type {
// First, map callee's interface type to specialized interface type.
auto Ty = Type(type).subst(CalleeInterfaceToSpecializedInterfaceMap);
Type SpecializedInterfaceTy =
SpecializedGenericEnv->mapTypeIntoContext(Ty)
->mapTypeOutOfContext();
assert(!SpecializedInterfaceTy->hasError());
return SpecializedInterfaceTy;
},
LookUpConformanceInSignature(CalleeGenericSig.getPointer()));
LLVM_DEBUG(llvm::dbgs() << "\n\nCallerInterfaceSubs map:\n";
CallerInterfaceSubs.dump(llvm::dbgs()));
}
/// Fast-path for the case when generic substitutions are not supported.
void FunctionSignaturePartialSpecializer::
createSpecializedGenericSignatureWithNonGenericSubs() {
// Simply create a set of same-type requirements based on concrete
// substitutions.
SmallVector<Requirement, 4> Requirements;
CalleeGenericSig->forEachParam([&](GenericTypeParamType *GP, bool Canonical) {
if (!Canonical)
return;
auto Replacement = Type(GP).subst(CalleeInterfaceToCallerArchetypeMap);
if (Replacement->hasArchetype())
return;
// Replacement is concrete. Add a same type requirement.
Requirement Req(RequirementKind::SameType, GP, Replacement);
Requirements.push_back(Req);
});
// Create a new generic signature by taking the existing one
// and adding new requirements to it. No need to introduce
// any new generic parameters.
auto GenPair = getGenericEnvironmentAndSignatureWithRequirements(
CalleeGenericSig, CalleeGenericEnv, Requirements, M);
if (GenPair.second) {
SpecializedGenericSig = GenPair.second.getCanonicalSignature();
SpecializedGenericEnv = GenPair.first;
}
for (auto GP : CalleeGenericSig->getGenericParams()) {
CalleeInterfaceToSpecializedInterfaceMapping[GP] = Type(GP);
}
computeCalleeInterfaceToSpecializedInterfaceMap();
SpecializedInterfaceToCallerArchetypeMap =
CalleeInterfaceToCallerArchetypeMap;
}
void FunctionSignaturePartialSpecializer::createSpecializedGenericSignature(
SubstitutionMap ParamSubs) {
// Collect all used caller's archetypes from all the substitutions.
collectUsedCallerArchetypes(ParamSubs);
// Generate a new generic type parameter for each used archetype from
// the caller.
createGenericParamsForUsedCallerArchetypes();
// Create a SubstitutionMap for re-mapping caller's interface types
// to interface types of the specialized function.
computeCallerInterfaceToSpecializedInterfaceMap();
// Add generic parameters that will come from the callee.
// Introduce a new generic parameter in the new generic signature
// for each generic parameter from the callee.
createGenericParamsForCalleeGenericParams();
computeCalleeInterfaceToSpecializedInterfaceMap();
// Add requirements from the callee's generic signature.
addCalleeRequirements();
// Add requirements from the caller's generic signature.
addCallerRequirements();
auto GenPair = getSpecializedGenericEnvironmentAndSignature();
if (GenPair.second) {
SpecializedGenericSig = GenPair.second.getCanonicalSignature();
SpecializedGenericEnv = GenPair.first;
computeSpecializedInterfaceToCallerArchetypeMap();
}
}
/// Builds a new generic and function signatures for a partial specialization.
/// Allows for partial specializations even if substitutions contain
/// type parameters.
///
/// The new generic signature has the following generic parameters:
/// - For each substitution with a concrete type CT as a replacement for a
/// generic type T, it introduces a generic parameter T' and a
/// requirement T' == CT
/// - For all other substitutions that are considered for partial specialization,
/// it collects first the archetypes used in the replacements. Then for each such
/// archetype A a new generic parameter T' introduced.
/// - If there is a substitution for type T and this substitution is excluded
/// from partial specialization (e.g. because it is impossible or would result
/// in a less efficient code), then a new generic parameter T' is introduced,
/// which does not get any additional, more specific requirements based on the
/// substitutions.
///
/// After all generic parameters are added according to the rules above,
/// the requirements of the callee's signature are re-mapped by re-formulating
/// them in terms of the newly introduced generic parameters. In case a remapped
/// requirement does not contain any generic types, it can be omitted, because
/// it is fulfilled already.
///
/// If any of the generic parameters were introduced for caller's archetypes,
/// their requirements from the caller's signature are re-mapped by
/// re-formulating them in terms of the newly introduced generic parameters.
void ReabstractionInfo::performPartialSpecializationPreparation(
SILFunction *Caller, SILFunction *Callee,
SubstitutionMap ParamSubs) {
SILModule &M = Callee->getModule();
// Caller is the SILFunction containing the apply instruction.
CanGenericSignature CallerGenericSig;
GenericEnvironment *CallerGenericEnv = nullptr;
if (Caller) {
CallerGenericSig = Caller->getLoweredFunctionType()
->getInvocationGenericSignature();
CallerGenericEnv = Caller->getGenericEnvironment();
}
// Callee is the generic function being called by the apply instruction.
auto CalleeFnTy = Callee->getLoweredFunctionType();
auto CalleeGenericSig = CalleeFnTy->getInvocationGenericSignature();
auto CalleeGenericEnv = Callee->getGenericEnvironment();
LLVM_DEBUG(llvm::dbgs() << "\n\nTrying partial specialization for: "
<< Callee->getName() << "\n";
llvm::dbgs() << "Callee generic signature is:\n";
CalleeGenericSig->print(llvm::dbgs()));
FunctionSignaturePartialSpecializer FSPS(M,
CallerGenericSig, CallerGenericEnv,
CalleeGenericSig, CalleeGenericEnv,
ParamSubs);
// Create the partially specialized generic signature and generic environment.
if (SupportGenericSubstitutions)
FSPS.createSpecializedGenericSignature(ParamSubs);
else
FSPS.createSpecializedGenericSignatureWithNonGenericSubs();
// Once the specialized signature is known, compute different
// maps and function types based on it. The specializer will need
// them for cloning and specializing the function body, rewriting
// the original apply instruction, etc.
finishPartialSpecializationPreparation(FSPS);
}
void ReabstractionInfo::finishPartialSpecializationPreparation(
FunctionSignaturePartialSpecializer &FSPS) {
SpecializedGenericSig = FSPS.getSpecializedGenericSignature();
SpecializedGenericEnv = FSPS.getSpecializedGenericEnvironment();
if (SpecializedGenericSig) {
LLVM_DEBUG(llvm::dbgs() << "\nCreated SpecializedGenericSig:\n";
SpecializedGenericSig->print(llvm::dbgs());
SpecializedGenericEnv->dump(llvm::dbgs()));
}
// Create substitution lists for the caller and cloner.
ClonerParamSubMap = FSPS.computeClonerParamSubs();
CallerParamSubMap = FSPS.getCallerParamSubs();
// Create a substitution map for the caller interface substitutions.
FSPS.computeCallerInterfaceSubs(CallerInterfaceSubs);
if (CalleeParamSubMap.empty()) {
// It can happen if there is no caller or it is an eager specialization.
CalleeParamSubMap = CallerParamSubMap;
}
HasUnboundGenericParams =
SpecializedGenericSig && !SpecializedGenericSig->areAllParamsConcrete();
createSubstitutedAndSpecializedTypes();
if (getSubstitutedType() != Callee->getLoweredFunctionType()) {
if (getSubstitutedType()->isPolymorphic())
LLVM_DEBUG(llvm::dbgs() << "Created new specialized type: "
<< SpecializedType << "\n");
}
}
/// This constructor is used when processing @_specialize.
ReabstractionInfo::ReabstractionInfo(ModuleDecl *targetModule,
bool isWholeModule, SILFunction *Callee,
GenericSignature SpecializedSig,
bool isPrespecialization)
: TargetModule(targetModule), isWholeModule(isWholeModule),
isPrespecialization(isPrespecialization) {
Serialized =
this->isPrespecialization ? IsNotSerialized : Callee->isSerialized();
if (shouldNotSpecialize(Callee, nullptr))
return;
this->Callee = Callee;
ConvertIndirectToDirect = true;
SILModule &M = Callee->getModule();
auto CalleeGenericSig =
Callee->getLoweredFunctionType()->getInvocationGenericSignature();
auto *CalleeGenericEnv = Callee->getGenericEnvironment();
FunctionSignaturePartialSpecializer FSPS(M,
CalleeGenericSig, CalleeGenericEnv,
SpecializedSig);
finishPartialSpecializationPreparation(FSPS);
}
// =============================================================================
// GenericFuncSpecializer
// =============================================================================
GenericFuncSpecializer::GenericFuncSpecializer(
SILOptFunctionBuilder &FuncBuilder, SILFunction *GenericFunc,
SubstitutionMap ParamSubs,
const ReabstractionInfo &ReInfo)
: FuncBuilder(FuncBuilder), M(GenericFunc->getModule()),
GenericFunc(GenericFunc),
ParamSubs(ParamSubs),
ReInfo(ReInfo) {
assert((GenericFunc->isDefinition() || ReInfo.isPrespecialized()) &&
"Expected definition or pre-specialized entry-point to specialize!");
auto FnTy = ReInfo.getSpecializedType();
if (ReInfo.isPartialSpecialization()) {
Mangle::PartialSpecializationMangler Mangler(
GenericFunc, FnTy, ReInfo.isSerialized(), /*isReAbstracted*/ true);
ClonedName = Mangler.mangle();
} else {
Mangle::GenericSpecializationMangler Mangler(
GenericFunc, ReInfo.isSerialized());
if (ReInfo.isPrespecialized()) {
ClonedName = Mangler.manglePrespecialized(ParamSubs);
} else {
ClonedName = Mangler.mangleReabstracted(ParamSubs,
ReInfo.needAlternativeMangling());
}
}
LLVM_DEBUG(llvm::dbgs() << " Specialized function " << ClonedName << '\n');
}
/// Return an existing specialization if one exists.
SILFunction *GenericFuncSpecializer::lookupSpecialization() {
if (SILFunction *SpecializedF = M.lookUpFunction(ClonedName)) {
if (ReInfo.getSpecializedType() != SpecializedF->getLoweredFunctionType()) {
llvm::dbgs() << "Looking for a function: " << ClonedName << "\n"
<< "Expected type: " << ReInfo.getSpecializedType() << "\n"
<< "Found type: "
<< SpecializedF->getLoweredFunctionType() << "\n";
}
assert(ReInfo.getSpecializedType()
== SpecializedF->getLoweredFunctionType() &&
"Previously specialized function does not match expected type.");
LLVM_DEBUG(llvm::dbgs() << "Found an existing specialization for: "
<< ClonedName << "\n");
return SpecializedF;
}
LLVM_DEBUG(llvm::dbgs() << "Could not find an existing specialization for: "
<< ClonedName << "\n");
return nullptr;
}
void ReabstractionInfo::verify() const {
assert((!SpecializedGenericSig && !SpecializedGenericEnv &&
!getSpecializedType()->isPolymorphic()) ||
(SpecializedGenericSig && SpecializedGenericEnv &&
getSpecializedType()->isPolymorphic()));
}
/// Create a new specialized function if possible, and cache it.
SILFunction *
GenericFuncSpecializer::tryCreateSpecialization(bool forcePrespecialization) {
// Do not create any new specializations at Onone.
if (!GenericFunc->shouldOptimize() && !forcePrespecialization)
return nullptr;
LLVM_DEBUG(llvm::dbgs() << "Creating a specialization: "
<< ClonedName << "\n");
ReInfo.verify();
// Create a new function.
SILFunction *SpecializedF = GenericCloner::cloneFunction(
FuncBuilder, GenericFunc, ReInfo,
// Use these substitutions inside the new specialized function being
// created.
ReInfo.getClonerParamSubstitutionMap(),
ClonedName);
assert((SpecializedF->getLoweredFunctionType()->isPolymorphic() &&
SpecializedF->getGenericEnvironment()) ||
(!SpecializedF->getLoweredFunctionType()->isPolymorphic() &&
!SpecializedF->getGenericEnvironment()));
// Store the meta-information about how this specialization was created.
auto *Caller = ReInfo.getApply() ? ReInfo.getApply().getFunction() : nullptr;
SubstitutionMap Subs = Caller ? ReInfo.getApply().getSubstitutionMap()
: ReInfo.getClonerParamSubstitutionMap();
SpecializedF->setClassSubclassScope(SubclassScope::NotApplicable);
SpecializedF->setSpecializationInfo(
GenericSpecializationInformation::create(Caller, GenericFunc, Subs));
if (VerifyFunctionsAfterSpecialization) {
PrettyStackTraceSILFunction SILFunctionDumper(
llvm::Twine("Generic function: ") + GenericFunc->getName() +
". Specialized Function: " + SpecializedF->getName(),
GenericFunc);
SpecializedF->verify();
}
return SpecializedF;
}
// =============================================================================
// Apply substitution
// =============================================================================
/// Fix the case where a void function returns the result of an apply, which is
/// also a call of a void-returning function.
/// We always want a void function returning a tuple _instruction_.
static void fixUsedVoidType(SILValue VoidVal, SILLocation Loc,
SILBuilder &Builder) {
assert(VoidVal->getType().isVoid());
if (VoidVal->use_empty())
return;
auto *NewVoidVal = Builder.createTuple(Loc, VoidVal->getType(), { });
VoidVal->replaceAllUsesWith(NewVoidVal);
}
/// Prepare call arguments. Perform re-abstraction if required.
///
/// \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();
unsigned ArgIdx = AI.getCalleeArgIndexOfFirstAppliedArg();
auto handleConversion = [&](SILValue InputValue) {
// Rewriting SIL arguments is only for lowered addresses.
if (!substConv.useLoweredAddresses())
return false;
if (ArgIdx < substConv.getSILArgIndexOfFirstParam()) {
// Handle result arguments.
unsigned formalIdx =
substConv.getIndirectFormalResultIndexForSILArg(ArgIdx);
if (!ReInfo.isFormalResultConverted(formalIdx)) {
return false;
}
// The result is converted from indirect to direct. We need to insert
// a store later.
assert(!StoreResultTo);
StoreResultTo = InputValue;
return true;
}
// Handle arguments for formal parameters.
unsigned paramIdx = ArgIdx - substConv.getSILArgIndexOfFirstParam();
if (!ReInfo.isParamConverted(paramIdx)) {
return false;
}
// An argument is converted from indirect to direct. Instead of the
// address we pass the loaded value.
auto argConv = substConv.getSILArgumentConvention(ArgIdx);
SILValue Val;
if (!argConv.isGuaranteedConvention() || isa<PartialApplyInst>(AI)) {
Val = Builder.emitLoadValueOperation(Loc, InputValue,
LoadOwnershipQualifier::Take);
} else {
Val = Builder.emitLoadBorrowOperation(Loc, InputValue);
if (Val.getOwnershipKind() == OwnershipKind::Guaranteed)
ArgAtIndexNeedsEndBorrow.push_back(Arguments.size());
}
Arguments.push_back(Val);
return true;
};
for (auto &Op : AI.getArgumentOperands()) {
if (!handleConversion(Op.get()))
Arguments.push_back(Op.get());
++ArgIdx;
}
}
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,
SILValue callee,
const ReabstractionInfo &reInfo) {
SILBuilderWithScope builder(applySite.getInstruction());
SILLocation loc = applySite.getLoc();
SmallVector<SILValue, 4> arguments;
SmallVector<unsigned, 4> argsNeedingEndBorrow;
SILValue 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() &&
canFnTy->isPolymorphic()) {
subs = reInfo.getCallerParamSubstitutionMap();
subs = SubstitutionMap::get(canFnTy->getSubstGenericSignature(), subs);
}
auto calleeSubstFnTy = canFnTy->substGenericArgs(
*callee->getModule(), subs, reInfo.getResilienceExpansion());
auto calleeSILSubstFnTy = SILType::getPrimitiveObjectType(calleeSubstFnTy);
SILFunctionConventions substConv(calleeSubstFnTy, builder.getModule());
switch (applySite.getKind()) {
case ApplySiteKind::TryApplyInst: {
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(
[&](SILBuilder &argBuilder) {
cleanupCallArguments(argBuilder, loc, arguments,
argsNeedingEndBorrow);
});
auto *newTAI = builder.createTryApply(loc, callee, subs, arguments,
resultBlock, tai->getErrorBB());
if (resultOut) {
assert(substConv.useLoweredAddresses());
// The original normal result of the try_apply is an empty tuple.
assert(resultBlock->getNumArguments() == 1);
builder.setInsertionPoint(resultBlock->begin());
fixUsedVoidType(resultBlock->getArgument(0), loc, builder);
SILArgument *arg = resultBlock->replacePhiArgument(
0, resultOut->getType().getObjectType(), OwnershipKind::Owned);
// Store the direct result to the original result address.
builder.emitStoreValueOperation(loc, arg, resultOut,
StoreOwnershipQualifier::Init);
}
return newTAI;
}
case ApplySiteKind::ApplyInst: {
auto *ai = cast<ApplyInst>(applySite);
FullApplySite(ai).insertAfterFullEvaluation(
[&](SILBuilder &argBuilder) {
cleanupCallArguments(argBuilder, loc, arguments,
argsNeedingEndBorrow);
});
auto *newAI =
builder.createApply(loc, callee, subs, arguments, ai->isNonThrowing());
if (resultOut) {
if (!calleeSILSubstFnTy.isNoReturnFunction(
builder.getModule(), builder.getTypeExpansionContext())) {
// Store the direct result to the original result address.
fixUsedVoidType(ai, loc, builder);
builder.emitStoreValueOperation(loc, newAI, resultOut,
StoreOwnershipQualifier::Init);
} else {
builder.createUnreachable(loc);
// unreachable should be the terminator instruction.
// So, split the current basic block right after the
// inserted unreachable instruction.
builder.getInsertionPoint()->getParent()->split(
builder.getInsertionPoint());
}
}
ai->replaceAllUsesWith(newAI);
return newAI;
}
case ApplySiteKind::BeginApplyInst: {
auto *bai = cast<BeginApplyInst>(applySite);
assert(!resultOut);
FullApplySite(bai).insertAfterFullEvaluation(
[&](SILBuilder &argBuilder) {
cleanupCallArguments(argBuilder, loc, arguments,
argsNeedingEndBorrow);
});
auto *newBAI = builder.createBeginApply(loc, callee, subs, arguments,
bai->isNonThrowing());
bai->replaceAllUsesPairwiseWith(newBAI);
return newBAI;
}
case ApplySiteKind::PartialApplyInst: {
auto *pai = cast<PartialApplyInst>(applySite);
auto *newPAI = builder.createPartialApply(
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;
}
}
llvm_unreachable("unhandled kind of apply");
}
/// Create a new apply based on an old one, but with a different
/// function being applied.
ApplySite swift::
replaceWithSpecializedFunction(ApplySite AI, SILFunction *NewF,
const ReabstractionInfo &ReInfo) {
SILBuilderWithScope Builder(AI.getInstruction());
FunctionRefInst *FRI = Builder.createFunctionRef(AI.getLoc(), NewF);
return replaceWithSpecializedCallee(AI, FRI, ReInfo);
}
namespace {
class ReabstractionThunkGenerator {
SILOptFunctionBuilder &FunctionBuilder;
SILFunction *OrigF;
SILModule &M;
SILFunction *SpecializedFunc;
const ReabstractionInfo &ReInfo;
PartialApplyInst *OrigPAI;
std::string ThunkName;
RegularLocation Loc;
SmallVector<SILValue, 4> Arguments;
public:
ReabstractionThunkGenerator(SILOptFunctionBuilder &FunctionBuilder,
const ReabstractionInfo &ReInfo,
PartialApplyInst *OrigPAI,
SILFunction *SpecializedFunc)
: FunctionBuilder(FunctionBuilder), OrigF(OrigPAI->getCalleeFunction()), M(OrigF->getModule()),
SpecializedFunc(SpecializedFunc), ReInfo(ReInfo), OrigPAI(OrigPAI),
Loc(RegularLocation::getAutoGeneratedLocation()) {
if (!ReInfo.isPartialSpecialization()) {
Mangle::GenericSpecializationMangler Mangler(OrigF, ReInfo.isSerialized());
ThunkName = Mangler.mangleNotReabstracted(
ReInfo.getCalleeParamSubstitutionMap());
} else {
Mangle::PartialSpecializationMangler Mangler(
OrigF, ReInfo.getSpecializedType(), ReInfo.isSerialized(),
/*isReAbstracted*/ false);
ThunkName = Mangler.mangle();
}
}
SILFunction *createThunk();
protected:
FullApplySite createReabstractionThunkApply(SILBuilder &Builder);
SILArgument *convertReabstractionThunkArguments(
SILBuilder &Builder, SmallVectorImpl<unsigned> &ArgsNeedingEndBorrows);
};
} // anonymous namespace
SILFunction *ReabstractionThunkGenerator::createThunk() {
SILFunction *Thunk = FunctionBuilder.getOrCreateSharedFunction(
Loc, ThunkName, ReInfo.getSubstitutedType(), IsBare, IsTransparent,
ReInfo.isSerialized(), ProfileCounter(), IsThunk, IsNotDynamic);
// Re-use an existing thunk.
if (!Thunk->empty())
return Thunk;
Thunk->setGenericEnvironment(ReInfo.getSpecializedGenericEnvironment());
SILBasicBlock *EntryBB = Thunk->createBasicBlock();
SILBuilder Builder(EntryBB);
// If the original specialized function had unqualified ownership, set the
// thunk to have unqualified ownership as well.
//
// This is a stop gap measure to allow for easy inlining. We could always make
// the Thunk qualified, but then we would need to either fix the inliner to
// inline qualified into unqualified functions /or/ have the
// OwnershipModelEliminator run as part of the normal compilation pipeline
// (which we are not doing yet).
if (!SpecializedFunc->hasOwnership()) {
Thunk->setOwnershipEliminated();
}
if (!SILModuleConventions(M).useLoweredAddresses()) {
for (auto SpecArg : SpecializedFunc->getArguments()) {
SILArgument *NewArg = EntryBB->createFunctionArgument(SpecArg->getType(),
SpecArg->getDecl());
Arguments.push_back(NewArg);
}
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.
SmallVector<unsigned, 4> ArgsThatNeedEndBorrow;
SILArgument *ReturnValueAddr =
convertReabstractionThunkArguments(Builder, ArgsThatNeedEndBorrow);
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.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([&](SILBuilder &argBuilder) {
cleanupCallArguments(argBuilder, Loc, Arguments, ArgsThatNeedEndBorrow);
});
return Thunk;
}
/// Create a call to a reabstraction thunk. Return the call's direct result.
FullApplySite ReabstractionThunkGenerator::createReabstractionThunkApply(
SILBuilder &Builder) {
SILFunction *Thunk = &Builder.getFunction();
auto *FRI = Builder.createFunctionRef(Loc, SpecializedFunc);
auto Subs = Thunk->getForwardingSubstitutionMap();
auto specConv = SpecializedFunc->getConventions();
if (!SpecializedFunc->getLoweredFunctionType()->hasErrorResult()) {
return Builder.createApply(Loc, FRI, Subs, Arguments);
}
// Create the logic for calling a throwing function.
SILBasicBlock *NormalBB = Thunk->createBasicBlock();
SILBasicBlock *ErrorBB = Thunk->createBasicBlock();
auto *TAI =
Builder.createTryApply(Loc, FRI, Subs, Arguments, NormalBB, ErrorBB);
auto *ErrorVal = ErrorBB->createPhiArgument(
SpecializedFunc->mapTypeIntoContext(
specConv.getSILErrorType(Builder.getTypeExpansionContext())),
OwnershipKind::Owned);
Builder.setInsertionPoint(ErrorBB);
Builder.createThrow(Loc, ErrorVal);
NormalBB->createPhiArgument(
SpecializedFunc->mapTypeIntoContext(
specConv.getSILResultType(Builder.getTypeExpansionContext())),
OwnershipKind::Owned);
Builder.setInsertionPoint(NormalBB);
return FullApplySite(TAI);
}
/// Create SIL arguments for a reabstraction thunk with lowered addresses. This
/// may involve replacing indirect arguments with loads and stores. Return the
/// SILArgument for the address of an indirect result, or nullptr.
///
/// FIXME: Remove this if we don't need to create reabstraction thunks after
/// address lowering.
SILArgument *ReabstractionThunkGenerator::convertReabstractionThunkArguments(
SILBuilder &Builder, SmallVectorImpl<unsigned> &ArgsThatNeedEndBorrow) {
SILFunction *Thunk = &Builder.getFunction();
CanSILFunctionType SpecType = SpecializedFunc->getLoweredFunctionType();
CanSILFunctionType SubstType = ReInfo.getSubstitutedType();
auto specConv = SpecializedFunc->getConventions();
(void)specConv;
SILFunctionConventions substConv(SubstType, M);
assert(specConv.useLoweredAddresses());
// ReInfo.NumIndirectResults corresponds to SubstTy's formal indirect
// results. SpecTy may have fewer formal indirect results.
assert(SubstType->getNumIndirectFormalResults()
>= SpecType->getNumIndirectFormalResults());
SILBasicBlock *EntryBB = Thunk->getEntryBlock();
SILArgument *ReturnValueAddr = nullptr;
auto SpecArgIter = SpecializedFunc->getArguments().begin();
auto cloneSpecializedArgument = [&]() {
// No change to the argument.
SILArgument *SpecArg = *SpecArgIter++;
SILArgument *NewArg =
EntryBB->createFunctionArgument(SpecArg->getType(), SpecArg->getDecl());
Arguments.push_back(NewArg);
};
// ReInfo.NumIndirectResults corresponds to SubstTy's formal indirect
// results. SpecTy may have fewer formal indirect results.
assert(SubstType->getNumIndirectFormalResults()
>= SpecType->getNumIndirectFormalResults());
unsigned resultIdx = 0;
for (auto substRI : SubstType->getIndirectFormalResults()) {
if (ReInfo.isFormalResultConverted(resultIdx++)) {
// Convert an originally indirect to direct specialized result.
// Store the result later.
// FIXME: This only handles a single result! Partial specialization could
// induce some combination of direct and indirect results.
SILType ResultTy = SpecializedFunc->mapTypeIntoContext(
substConv.getSILType(substRI, Builder.getTypeExpansionContext()));
assert(ResultTy.isAddress());
assert(!ReturnValueAddr);
ReturnValueAddr = EntryBB->createFunctionArgument(ResultTy);
continue;
}
// If the specialized result is already indirect, simply clone the indirect
// result argument.
assert((*SpecArgIter)->getType().isAddress());
cloneSpecializedArgument();
}
assert(SpecArgIter
== SpecializedFunc->getArgumentsWithoutIndirectResults().begin());
unsigned numParams = SpecType->getNumParameters();
assert(numParams == SubstType->getNumParameters());
for (unsigned paramIdx = 0; paramIdx < numParams; ++paramIdx) {
if (ReInfo.isParamConverted(paramIdx)) {
// Convert an originally indirect to direct specialized parameter.
assert(!specConv.isSILIndirect(SpecType->getParameters()[paramIdx]));
// Instead of passing the address, pass the loaded value.
SILType ParamTy = SpecializedFunc->mapTypeIntoContext(
substConv.getSILType(SubstType->getParameters()[paramIdx],
Builder.getTypeExpansionContext()));
assert(ParamTy.isAddress());
SILArgument *SpecArg = *SpecArgIter++;
SILFunctionArgument *NewArg =
EntryBB->createFunctionArgument(ParamTy, SpecArg->getDecl());
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() == OwnershipKind::Guaranteed)
ArgsThatNeedEndBorrow.push_back(Arguments.size());
Arguments.push_back(argVal);
}
continue;
}
// Simply clone unconverted direct or indirect parameters.
cloneSpecializedArgument();
}
assert(SpecArgIter == SpecializedFunc->getArguments().end());
return ReturnValueAddr;
}
/// Create a pre-specialization of the library function with
/// \p UnspecializedName, using the substitutions from \p Apply.
static bool createPrespecialized(StringRef UnspecializedName,
ApplySite Apply,
SILOptFunctionBuilder &FuncBuilder) {
SILModule &M = FuncBuilder.getModule();
SILFunction *UnspecFunc = M.lookUpFunction(UnspecializedName);
if (UnspecFunc) {
if (!UnspecFunc->isDefinition())
M.loadFunction(UnspecFunc);
} else {
UnspecFunc = M.getSILLoader()->lookupSILFunction(UnspecializedName,
/*declarationOnly*/ false);
}
if (!UnspecFunc || !UnspecFunc->isDefinition())
return false;
ReabstractionInfo ReInfo(M.getSwiftModule(), M.isWholeModule(), ApplySite(),
UnspecFunc, Apply.getSubstitutionMap(),
IsNotSerialized,
/*ConvertIndirectToDirect=*/true, nullptr);
if (!ReInfo.canBeSpecialized())
return false;
GenericFuncSpecializer FuncSpecializer(FuncBuilder,
UnspecFunc, Apply.getSubstitutionMap(),
ReInfo);
SILFunction *SpecializedF = FuncSpecializer.lookupSpecialization();
if (!SpecializedF)
SpecializedF = FuncSpecializer.tryCreateSpecialization();
if (!SpecializedF)
return false;
// Link after prespecializing to pull in everything referenced from another
// module in case some referenced functions have non-public linkage.
M.linkFunction(SpecializedF, SILModule::LinkingMode::LinkAll);
SpecializedF->setLinkage(SILLinkage::Public);
SpecializedF->setSerialized(IsNotSerialized);
return true;
}
/// Create pre-specializations of the library function X if \p ProxyFunc has
/// @_semantics("prespecialize.X") attributes.
static bool createPrespecializations(ApplySite Apply, SILFunction *ProxyFunc,
SILOptFunctionBuilder &FuncBuilder) {
if (Apply.getSubstitutionMap().hasArchetypes())
return false;
SILModule &M = FuncBuilder.getModule();
bool prespecializeFound = false;
for (const std::string &semAttrStr : ProxyFunc->getSemanticsAttrs()) {
StringRef semAttr(semAttrStr);
if (semAttr.consume_front("prespecialize.")) {
prespecializeFound = true;
if (!createPrespecialized(semAttr, Apply, FuncBuilder)) {
M.getASTContext().Diags.diagnose(Apply.getLoc().getSourceLoc(),
diag::cannot_prespecialize,
semAttr);
}
}
}
return prespecializeFound;
}
static SILFunction *
lookupOrCreatePrespecialization(SILOptFunctionBuilder &funcBuilder,
SILFunction *origF, std::string clonedName,
ReabstractionInfo &reInfo) {
if (auto *specializedF = funcBuilder.getModule().lookUpFunction(clonedName)) {
assert(reInfo.getSpecializedType() ==
specializedF->getLoweredFunctionType() &&
"Previously specialized function does not match expected type.");
return specializedF;
}
auto *declaration =
GenericCloner::createDeclaration(funcBuilder, origF, reInfo, clonedName);
declaration->setLinkage(SILLinkage::PublicExternal);
return declaration;
}
static SILFunction *
usePrespecialized(SILOptFunctionBuilder &funcBuilder, ApplySite apply,
SILFunction *refF, const ReabstractionInfo &specializedReInfo,
ReabstractionInfo &prespecializedReInfo) {
for (auto *SA : refF->getSpecializeAttrs()) {
if (!SA->isExported())
continue;
// Check whether SPI allows using this function.
auto spiGroup = SA->getSPIGroup();
if (!spiGroup.empty()) {
auto currentModule = funcBuilder.getModule().getSwiftModule();
auto funcModule = SA->getSPIModule();
// Don't use this SPI if the current module does not import the function's
// module with @_spi(<spiGroup>).
if (currentModule != funcModule &&
!currentModule->isImportedAsSPI(spiGroup, funcModule))
continue;
}
ReabstractionInfo reInfo(funcBuilder.getModule().getSwiftModule(),
funcBuilder.getModule().isWholeModule(), refF,
SA->getSpecializedSignature(),
/*isPrespecialization*/ true);
if (specializedReInfo.getSpecializedType() != reInfo.getSpecializedType())
continue;
SubstitutionMap subs = reInfo.getCalleeParamSubstitutionMap();
Mangle::GenericSpecializationMangler mangler(refF, reInfo.isSerialized());
std::string name = reInfo.isPrespecialized() ?
mangler.manglePrespecialized(subs) :
mangler.mangleReabstracted(subs, reInfo.needAlternativeMangling());
prespecializedReInfo = reInfo;
return lookupOrCreatePrespecialization(funcBuilder, refF, name, reInfo);
}
return nullptr;
}
void swift::trySpecializeApplyOfGeneric(
SILOptFunctionBuilder &FuncBuilder,
ApplySite Apply, DeadInstructionSet &DeadApplies,
SmallVectorImpl<SILFunction *> &NewFunctions,
OptRemark::Emitter &ORE) {
assert(Apply.hasSubstitutions() && "Expected an apply with substitutions!");
auto *F = Apply.getFunction();
auto *RefF =
cast<FunctionRefInst>(Apply.getCallee())->getReferencedFunctionOrNull();
LLVM_DEBUG(llvm::dbgs() << "\n\n*** ApplyInst in function " << F->getName()
<< ":\n";
Apply.getInstruction()->dumpInContext());
// If the caller is fragile but the callee is not, bail out.
// Specializations have shared linkage, which means they do
// not have an external entry point, Since the callee is not
// fragile we cannot serialize the body of the specialized
// callee either.
if (F->isSerialized() && !RefF->hasValidLinkageForFragileInline())
return;
if (shouldNotSpecialize(RefF, F))
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.
IsSerialized_t Serialized = IsNotSerialized;
if (F->isSerialized() && RefF->isSerialized())
Serialized = IsSerializable;
// If it is OnoneSupport consider all specializations as non-serialized
// as we do not SIL serialize their bodies.
// It is important to set this flag here, because it affects the
// mangling of the specialization's name.
if (Apply.getModule().isOptimizedOnoneSupportModule()) {
if (createPrespecializations(Apply, RefF, FuncBuilder)) {
return;
}
Serialized = IsNotSerialized;
}
ReabstractionInfo ReInfo(FuncBuilder.getModule().getSwiftModule(),
FuncBuilder.getModule().isWholeModule(), Apply, RefF,
Apply.getSubstitutionMap(), Serialized,
/*ConvertIndirectToDirect=*/true, &ORE);
if (!ReInfo.canBeSpecialized())
return;
// Check if there is a pre-specialization available in a library.
SILFunction *prespecializedF = nullptr;
ReabstractionInfo prespecializedReInfo;
if ((prespecializedF = usePrespecialized(FuncBuilder, Apply, RefF, ReInfo,
prespecializedReInfo))) {
ReInfo = prespecializedReInfo;
}
// If there is not pre-specialization and we don't have a body give up.
if (!prespecializedF && !RefF->isDefinition())
return;
SILModule &M = F->getModule();
bool needAdaptUsers = false;
bool replacePartialApplyWithoutReabstraction = false;
auto *PAI = dyn_cast<PartialApplyInst>(Apply);
if (PAI && ReInfo.hasConversions()) {
// If we have a partial_apply and we converted some results/parameters from
// indirect to direct there are 3 cases:
// 1) All uses of the partial_apply are apply sites again. In this case
// we can just adapt all the apply sites which use the partial_apply.
// 2) The result of the partial_apply is re-abstracted anyway (and the
// re-abstracted function type matches with our specialized type). In
// 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;
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 (isIncidentalUse(User))
continue;
auto FAS = FullApplySite::isa(User);
if (FAS && FAS.getCallee() == PAI)
continue;
auto *PAIUser = dyn_cast<PartialApplyInst>(User);
if (PAIUser && isPartialApplyOfReabstractionThunk(PAIUser)) {
CanSILFunctionType NewPAType =
ReInfo.createSpecializedType(PAI->getFunctionType(), M);
if (PAIUser->getFunctionType() == NewPAType)
continue;
}
replacePartialApplyWithoutReabstraction = true;
break;
}
}
GenericFuncSpecializer FuncSpecializer(FuncBuilder,
RefF, Apply.getSubstitutionMap(),
ReInfo);
SILFunction *SpecializedF = prespecializedF
? prespecializedF
: FuncSpecializer.lookupSpecialization();
if (!SpecializedF) {
SpecializedF = FuncSpecializer.tryCreateSpecialization();
if (!SpecializedF)
return;
LLVM_DEBUG(llvm::dbgs() << "Created specialized function: "
<< SpecializedF->getName() << "\n"
<< "Specialized function type: "
<< SpecializedF->getLoweredFunctionType() << "\n");
NewFunctions.push_back(SpecializedF);
}
ORE.emit([&]() {
std::string Str;
llvm::raw_string_ostream OS(Str);
SpecializedF->getLoweredFunctionType().print(
OS, PrintOptions::printQuickHelpDeclaration());
using namespace OptRemark;
return RemarkPassed("Specialized", *Apply.getInstruction())
<< "Specialized function " << NV("Function", RefF) << " with type "
<< NV("FuncType", OS.str());
});
// Verify our function after we have finished fixing up call sites/etc. Dump
// the generic function if there is an assertion failure (or a crash) to make
// it easier to debug such problems since the original generic function is
// easily at hand.
SWIFT_DEFER {
if (VerifyFunctionsAfterSpecialization) {
PrettyStackTraceSILFunction SILFunctionDumper(
llvm::Twine("Generic function: ") + RefF->getName() +
". Specialized Function: " + SpecializedF->getName(),
RefF);
SpecializedF->verify();
}
};
assert(ReInfo.getSpecializedType()
== SpecializedF->getLoweredFunctionType() &&
"Previously specialized function does not match expected type.");
DeadApplies.insert(Apply.getInstruction());
if (replacePartialApplyWithoutReabstraction) {
// There are some unknown users of the partial_apply. Therefore we need a
// thunk which converts from the re-abstracted function back to the
// original function with indirect parameters/results.
auto *PAI = cast<PartialApplyInst>(Apply.getInstruction());
SILFunction *Thunk =
ReabstractionThunkGenerator(FuncBuilder, ReInfo, PAI, SpecializedF)
.createThunk();
if (VerifyFunctionsAfterSpecialization) {
PrettyStackTraceSILFunction SILFunctionDumper(
llvm::Twine("Thunk For Generic function: ") + RefF->getName() +
". Specialized Function: " + SpecializedF->getName(),
RefF);
Thunk->verify();
}
NewFunctions.push_back(Thunk);
SILBuilderWithScope Builder(PAI);
auto *FRI = Builder.createFunctionRef(PAI->getLoc(), Thunk);
SmallVector<SILValue, 4> Arguments;
for (auto &Op : PAI->getArgumentOperands()) {
Arguments.push_back(Op.get());
}
auto Subs = ReInfo.getCallerParamSubstitutionMap();
auto FnTy = Thunk->getLoweredFunctionType();
Subs = SubstitutionMap::get(FnTy->getSubstGenericSignature(), Subs);
auto *NewPAI = Builder.createPartialApply(
PAI->getLoc(), FRI, Subs, Arguments,
PAI->getType().getAs<SILFunctionType>()->getCalleeConvention(),
PAI->isOnStack());
PAI->replaceAllUsesWith(NewPAI);
DeadApplies.insert(PAI);
return;
}
// Make the required changes to the call site.
ApplySite newApply = replaceWithSpecializedFunction(Apply, SpecializedF,
ReInfo);
if (needAdaptUsers) {
// Adapt all known users of the partial_apply. This is needed in case we
// converted some indirect parameters/results to direct ones.
auto *NewPAI = cast<PartialApplyInst>(newApply);
ReInfo.prunePartialApplyArgs(NewPAI->getNumArguments());
for (Operand *Use : NewPAI->getUses()) {
SILInstruction *User = Use->getUser();
if (auto FAS = FullApplySite::isa(User)) {
replaceWithSpecializedCallee(FAS, NewPAI, ReInfo);
DeadApplies.insert(FAS.getInstruction());
continue;
}
if (auto *PAI = dyn_cast<PartialApplyInst>(User)) {
// This is a partial_apply of a re-abstraction thunk. Just skip this.
assert(PAI->getType() == NewPAI->getType());
PAI->replaceAllUsesWith(NewPAI);
DeadApplies.insert(PAI);
}
}
}
}
// =============================================================================
// Prespecialized symbol lookup.
// =============================================================================
#define PRESPEC_SYMBOL(s) MANGLE_AS_STRING(s),
static const char *PrespecSymbols[] = {
#include "OnonePrespecializations.def"
nullptr
};
#undef PRESPEC_SYMBOL
llvm::DenseSet<StringRef> PrespecSet;
bool swift::isKnownPrespecialization(StringRef SpecName) {
if (PrespecSet.empty()) {
const char **Pos = &PrespecSymbols[0];
while (const char *Sym = *Pos++) {
PrespecSet.insert(Sym);
}
assert(!PrespecSet.empty());
}
return PrespecSet.count(SpecName) != 0;
}
void swift::checkCompletenessOfPrespecializations(SILModule &M) {
const char **Pos = &PrespecSymbols[0];
while (const char *Sym = *Pos++) {
StringRef FunctionName(Sym);
SILFunction *F = M.lookUpFunction(FunctionName);
if (!F || F->getLinkage() != SILLinkage::Public) {
M.getASTContext().Diags.diagnose(SourceLoc(),
diag::missing_prespecialization,
FunctionName);
}
}
}
/// Try to look up an existing specialization in the specialization cache.
/// If it is found, it tries to link this specialization.
///
/// For now, it performs a lookup only in the standard library.
/// But in the future, one could think of maintaining a cache
/// of optimized specializations.
static SILFunction *lookupExistingSpecialization(SILModule &M,
StringRef FunctionName) {
// Try to link existing specialization only in -Onone mode.
// All other compilation modes perform specialization themselves.
// TODO: Cache optimized specializations and perform lookup here?
// Only check that this function exists, but don't read
// its body. It can save some compile-time.
if (isKnownPrespecialization(FunctionName))
return M.findFunction(FunctionName, SILLinkage::PublicExternal);
return nullptr;
}
SILFunction *swift::lookupPrespecializedSymbol(SILModule &M,
StringRef FunctionName) {
// First check if the module contains a required specialization already.
auto *Specialization = M.lookUpFunction(FunctionName);
if (Specialization) {
if (Specialization->getLinkage() == SILLinkage::PublicExternal)
return Specialization;
}
// Then check if the required specialization can be found elsewhere.
Specialization = lookupExistingSpecialization(M, FunctionName);
if (!Specialization)
return nullptr;
assert(hasPublicVisibility(Specialization->getLinkage()) &&
"Pre-specializations should have public visibility");
Specialization->setLinkage(SILLinkage::PublicExternal);
assert(Specialization->isExternalDeclaration() &&
"Specialization should be a public external declaration");
LLVM_DEBUG(llvm::dbgs() << "Found existing specialization for: "
<< FunctionName << '\n';
llvm::dbgs() << swift::Demangle::demangleSymbolAsString(
Specialization->getName())
<< "\n\n");
return Specialization;
}