Files
swift-mirror/lib/SILOptimizer/Utils/Generics.cpp
Michael Gottesman bf6920650c [gardening] Drop BB from all argument related code in SILBasicBlock.
Before this commit all code relating to handling arguments in SILBasicBlock had
somewhere in the name BB. This is redundant given that the class's name is
already SILBasicBlock. This commit drops those names.

Some examples:

getBBArg() => getArgument()
BBArgList => ArgumentList
bbarg_begin() => args_begin()
2016-11-25 01:14:36 -06:00

781 lines
30 KiB
C++

//===--- Generics.cpp ---- Utilities for transforming generics ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "generic-specializer"
#include "swift/Strings.h"
#include "swift/SILOptimizer/Utils/Generics.h"
#include "swift/SILOptimizer/Utils/GenericCloner.h"
#include "swift/SIL/DebugUtils.h"
#include "swift/AST/GenericEnvironment.h"
using namespace swift;
// Max depth of a bound generic 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 BoundGenericDepthThreshold = 50;
static unsigned getBoundGenericDepth(Type t) {
unsigned Depth = 0;
if (auto BGT = t->getAs<BoundGenericType>()) {
Depth++;
auto GenericArgs = BGT->getGenericArgs();
unsigned MaxGenericArgDepth = 0;
for (auto GenericArg : GenericArgs) {
auto ArgDepth = getBoundGenericDepth(GenericArg);
if (ArgDepth > MaxGenericArgDepth)
MaxGenericArgDepth = ArgDepth;
}
Depth += MaxGenericArgDepth;
}
return Depth;
}
// =============================================================================
// ReabstractionInfo
// =============================================================================
// Initialize SpecializedType iff the specialization is allowed.
ReabstractionInfo::ReabstractionInfo(SILFunction *OrigF,
ArrayRef<Substitution> ParamSubs) {
if (!OrigF->shouldOptimize()) {
DEBUG(llvm::dbgs() << " Cannot specialize function " << OrigF->getName()
<< " marked to be excluded from optimizations.\n");
return;
}
SubstitutionMap InterfaceSubs;
if (OrigF->getLoweredFunctionType()->getGenericSignature())
InterfaceSubs = OrigF->getLoweredFunctionType()->getGenericSignature()
->getSubstitutionMap(ParamSubs);
// We do not support partial specialization.
if (hasUnboundGenericTypes(InterfaceSubs.getMap())) {
DEBUG(llvm::dbgs() <<
" Cannot specialize with unbound interface substitutions.\n");
DEBUG(for (auto Sub : ParamSubs) {
Sub.dump();
});
return;
}
if (hasDynamicSelfTypes(InterfaceSubs.getMap())) {
DEBUG(llvm::dbgs() << " Cannot specialize with dynamic self.\n");
return;
}
// 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 Sub : ParamSubs) {
auto Replacement = Sub.getReplacement();
if (Replacement.findIf([](Type ty) -> bool {
return getBoundGenericDepth(ty) >= BoundGenericDepthThreshold;
})) {
return;
}
}
SILModule &M = OrigF->getModule();
Module *SM = M.getSwiftModule();
SubstitutedType = SILType::substFuncType(M, SM, InterfaceSubs.getMap(),
OrigF->getLoweredFunctionType(),
/*dropGenerics = */ true);
NumResults = SubstitutedType->getNumIndirectResults();
Conversions.resize(NumResults + SubstitutedType->getParameters().size());
if (SubstitutedType->getNumDirectResults() == 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->getIndirectResults()) {
assert(RI.isIndirect());
if (RI.getSILType().isLoadable(M) && !RI.getType()->isVoid()) {
Conversions.set(IdxForResult);
break;
}
++IdxForResult;
}
}
// Try to convert indirect incoming parameters to direct parameters.
unsigned IdxForParam = NumResults;
for (SILParameterInfo PI : SubstitutedType->getParameters()) {
if (PI.getSILType().isLoadable(M) &&
PI.getConvention() == ParameterConvention::Indirect_In) {
Conversions.set(IdxForParam);
}
++IdxForParam;
}
SpecializedType = createSpecializedType(SubstitutedType, M);
}
// Convert the substituted function type into a specialized function type based
// on the ReabstractionInfo.
CanSILFunctionType ReabstractionInfo::
createSpecializedType(CanSILFunctionType SubstFTy, SILModule &M) const {
llvm::SmallVector<SILResultInfo, 8> SpecializedResults;
llvm::SmallVector<SILParameterInfo, 8> SpecializedParams;
unsigned ResultIdx = 0;
for (SILResultInfo RI : SubstFTy->getAllResults()) {
if (RI.isDirect()) {
SpecializedResults.push_back(RI);
} else {
if (isResultConverted(ResultIdx++)) {
// Convert the indirect result to a direct result.
SILType SILResTy = SILType::getPrimitiveObjectType(RI.getType());
// 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 = (SILResTy.isTrivial(M) ? ResultConvention::Unowned :
ResultConvention::Owned);
SpecializedResults.push_back(SILResultInfo(RI.getType(), C));
} else {
// No conversion: re-use the original result info.
SpecializedResults.push_back(RI);
}
}
}
unsigned ParamIdx = 0;
for (SILParameterInfo PI : SubstFTy->getParameters()) {
if (isParamConverted(ParamIdx++)) {
// Convert the indirect parameter to a direct parameter.
SILType SILParamTy = SILType::getPrimitiveObjectType(PI.getType());
// Indirect parameters are passed as owned, so we also need to pass the
// direct parameter as owned (except it's a trivial type).
auto C = (SILParamTy.isTrivial(M) ? ParameterConvention::Direct_Unowned :
ParameterConvention::Direct_Owned);
SpecializedParams.push_back(SILParameterInfo(PI.getType(), C));
} else {
// No conversion: re-use the original parameter info.
SpecializedParams.push_back(PI);
}
}
return
SILFunctionType::get(SubstFTy->getGenericSignature(),
SubstFTy->getExtInfo(),
SubstFTy->getCalleeConvention(), SpecializedParams,
SpecializedResults, SubstFTy->getOptionalErrorResult(),
M.getASTContext());
}
// =============================================================================
// GenericFuncSpecializer
// =============================================================================
GenericFuncSpecializer::GenericFuncSpecializer(SILFunction *GenericFunc,
ArrayRef<Substitution> ParamSubs,
IsFragile_t Fragile,
const ReabstractionInfo &ReInfo)
: M(GenericFunc->getModule()),
GenericFunc(GenericFunc),
ParamSubs(ParamSubs),
Fragile(Fragile),
ReInfo(ReInfo) {
assert(GenericFunc->isDefinition() && "Expected definition to specialize!");
Mangle::Mangler Mangler;
GenericSpecializationMangler GenericMangler(Mangler, GenericFunc,
ParamSubs, Fragile);
GenericMangler.mangle();
ClonedName = Mangler.finalize();
DEBUG(llvm::dbgs() << " Specialized function " << ClonedName << '\n');
}
// Return an existing specialization if one exists.
SILFunction *GenericFuncSpecializer::lookupSpecialization() {
if (SILFunction *SpecializedF = M.lookUpFunction(ClonedName)) {
assert(ReInfo.getSpecializedType()
== SpecializedF->getLoweredFunctionType() &&
"Previously specialized function does not match expected type.");
DEBUG(llvm::dbgs() << "Found an existing specialization for: " << ClonedName
<< "\n");
return SpecializedF;
}
DEBUG(llvm::dbgs() << "Could not find an existing specialization for: "
<< ClonedName << "\n");
return nullptr;
}
// Forward decl for prespecialization support.
static bool linkSpecialization(SILModule &M, SILFunction *F);
// Create a new specialized function if possible, and cache it.
SILFunction *GenericFuncSpecializer::tryCreateSpecialization() {
// Do not create any new specializations at Onone.
if (M.getOptions().Optimization <= SILOptions::SILOptMode::None)
return nullptr;
DEBUG(
if (M.getOptions().Optimization <= SILOptions::SILOptMode::Debug) {
llvm::dbgs() << "Creating a specialization: " << ClonedName << "\n"; });
// Create a new function.
SILFunction * SpecializedF =
GenericCloner::cloneFunction(GenericFunc, Fragile, ReInfo,
ParamSubs, ClonedName);
assert(SpecializedF->hasUnqualifiedOwnership());
// Check if this specialization should be linked for prespecialization.
linkSpecialization(M, SpecializedF);
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);
}
// Create a new apply based on an old one, but with a different
// function being applied.
static ApplySite replaceWithSpecializedCallee(ApplySite AI,
SILValue Callee,
SILBuilder &Builder,
const ReabstractionInfo &ReInfo) {
SILLocation Loc = AI.getLoc();
SmallVector<SILValue, 4> Arguments;
SILValue StoreResultTo;
unsigned Idx = ReInfo.getIndexOfFirstArg(AI);
for (auto &Op : AI.getArgumentOperands()) {
if (ReInfo.isArgConverted(Idx)) {
if (ReInfo.isResultIndex(Idx)) {
// The result is converted from indirect to direct. We need to insert
// a store later.
assert(!StoreResultTo);
StoreResultTo = Op.get();
} else {
// An argument is converted from indirect to direct. Instead of the
// address we pass the loaded value.
SILValue Val = Builder.createLoad(Loc, Op.get(),
LoadOwnershipQualifier::Unqualified);
Arguments.push_back(Val);
}
} else {
Arguments.push_back(Op.get());
}
++Idx;
}
if (auto *TAI = dyn_cast<TryApplyInst>(AI)) {
SILBasicBlock *ResultBB = TAI->getNormalBB();
assert(ResultBB->getSinglePredecessor() == TAI->getParent());
auto *NewTAI =
Builder.createTryApply(Loc, Callee, Callee->getType(), {},
Arguments, ResultBB, TAI->getErrorBB());
if (StoreResultTo) {
// The original normal result of the try_apply is an empty tuple.
assert(ResultBB->getNumArguments() == 1);
Builder.setInsertionPoint(ResultBB->begin());
fixUsedVoidType(ResultBB->getArgument(0), Loc, Builder);
SILArgument *Arg = ResultBB->replaceArgument(
0, StoreResultTo->getType().getObjectType());
// Store the direct result to the original result address.
Builder.createStore(Loc, Arg, StoreResultTo,
StoreOwnershipQualifier::Unqualified);
}
return NewTAI;
}
if (auto *A = dyn_cast<ApplyInst>(AI)) {
auto *NewAI = Builder.createApply(Loc, Callee, Arguments, A->isNonThrowing());
if (StoreResultTo) {
// Store the direct result to the original result address.
fixUsedVoidType(A, Loc, Builder);
Builder.createStore(Loc, NewAI, StoreResultTo,
StoreOwnershipQualifier::Unqualified);
}
A->replaceAllUsesWith(NewAI);
return NewAI;
}
if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) {
CanSILFunctionType NewPAType =
ReInfo.createSpecializedType(PAI->getFunctionType(), Builder.getModule());
SILType PTy = SILType::getPrimitiveObjectType(ReInfo.getSpecializedType());
auto *NewPAI =
Builder.createPartialApply(Loc, Callee, PTy, {}, Arguments,
SILType::getPrimitiveObjectType(NewPAType));
PAI->replaceAllUsesWith(NewPAI);
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, Builder, ReInfo);
}
/// Create a re-abstraction thunk for a partial_apply.
/// This is needed in case we converted some parameters/results of the
/// specialized function from indirect to direct but the result function of the
/// partial_apply still needs them as indirect.
/// We create a thunk which converts the direct parameters/results back to
/// indirect ones.
static SILFunction *createReabstractionThunk(const ReabstractionInfo &ReInfo,
PartialApplyInst *OrigPAI,
SILFunction *SpecializedFunc) {
SILFunction *OrigF = OrigPAI->getCalleeFunction();
SILModule &M = OrigF->getModule();
IsFragile_t Fragile = IsNotFragile;
if (OrigF->isFragile() && OrigPAI->getFunction()->isFragile())
Fragile = IsFragile;
std::string ThunkName;
{
Mangle::Mangler M;
GenericSpecializationMangler Mangler(M, OrigF,
OrigPAI->getSubstitutions(), Fragile,
GenericSpecializationMangler::NotReabstracted);
Mangler.mangle();
ThunkName = M.finalize();
}
auto Loc = RegularLocation::getAutoGeneratedLocation();
SILFunction *Thunk =
M.getOrCreateSharedFunction(Loc, ThunkName, ReInfo.getSubstitutedType(),
IsBare, IsTransparent, Fragile, IsThunk);
// Re-use an existing thunk.
if (!Thunk->empty())
return Thunk;
SILBasicBlock *EntryBB = Thunk->createBasicBlock();
SILBuilder Builder(EntryBB);
SILBasicBlock *SpecEntryBB = &*SpecializedFunc->begin();
CanSILFunctionType SpecType = SpecializedFunc->getLoweredFunctionType();
SILArgument *ReturnValueAddr = nullptr;
// 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->hasUnqualifiedOwnership()) {
Thunk->setUnqualifiedOwnership();
}
// Convert indirect to direct parameters/results.
SmallVector<SILValue, 4> Arguments;
auto SpecArgIter = SpecEntryBB->args_begin();
for (unsigned Idx = 0; Idx < ReInfo.getNumArguments(); Idx++) {
if (ReInfo.isArgConverted(Idx)) {
if (ReInfo.isResultIndex(Idx)) {
// Store the result later.
SILType Ty = SpecType->getSILResult().getAddressType();
ReturnValueAddr = new (M) SILArgument(EntryBB, Ty);
} else {
// Instead of passing the address, pass the loaded value.
SILArgument *SpecArg = *SpecArgIter++;
SILType Ty = SpecArg->getType().getAddressType();
SILArgument *NewArg = new (M) SILArgument(EntryBB, Ty,
SpecArg->getDecl());
auto *ArgVal = Builder.createLoad(Loc, NewArg,
LoadOwnershipQualifier::Unqualified);
Arguments.push_back(ArgVal);
}
} else {
// No change to the argument.
SILArgument *SpecArg = *SpecArgIter++;
SILArgument *NewArg = new (M) SILArgument(EntryBB, SpecArg->getType(),
SpecArg->getDecl());
Arguments.push_back(NewArg);
}
}
auto *FRI = Builder.createFunctionRef(Loc, SpecializedFunc);
SILValue ReturnValue;
if (SpecType->hasErrorResult()) {
// Create the logic for calling a throwing function.
SILBasicBlock *NormalBB = Thunk->createBasicBlock();
SILBasicBlock *ErrorBB = Thunk->createBasicBlock();
Builder.createTryApply(Loc, FRI, SpecializedFunc->getLoweredType(),
{}, Arguments, NormalBB, ErrorBB);
auto *ErrorVal = new (M) SILArgument(ErrorBB,
SpecType->getErrorResult().getSILType());
Builder.setInsertionPoint(ErrorBB);
Builder.createThrow(Loc, ErrorVal);
ReturnValue = new (M) SILArgument(NormalBB, SpecType->getSILResult());
Builder.setInsertionPoint(NormalBB);
} else {
ReturnValue = Builder.createApply(Loc, FRI, SpecializedFunc->getLoweredType(),
SpecType->getSILResult(), {}, Arguments, false);
}
if (ReturnValueAddr) {
// Need to store the direct results to the original indirect address.
Builder.createStore(Loc, ReturnValue, ReturnValueAddr,
StoreOwnershipQualifier::Unqualified);
SILType VoidTy = OrigPAI->getSubstCalleeType()->getSILResult();
assert(VoidTy.isVoid());
ReturnValue = Builder.createTuple(Loc, VoidTy, { });
}
Builder.createReturn(Loc, ReturnValue);
return Thunk;
}
void swift::trySpecializeApplyOfGeneric(
ApplySite Apply, DeadInstructionSet &DeadApplies,
llvm::SmallVectorImpl<SILFunction *> &NewFunctions) {
assert(Apply.hasSubstitutions() && "Expected an apply with substitutions!");
auto *F = Apply.getInstruction()->getFunction();
auto *RefF = cast<FunctionRefInst>(Apply.getCallee())->getReferencedFunction();
DEBUG(llvm::dbgs() << " ApplyInst:\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->isFragile() && !RefF->hasValidLinkageForFragileInline())
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.
IsFragile_t Fragile = IsNotFragile;
if (F->isFragile() && RefF->isFragile())
Fragile = IsFragile;
ReabstractionInfo ReInfo(RefF, Apply.getSubstitutions());
if (!ReInfo.getSpecializedType())
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;
for (Operand *Use : PAI->getUses()) {
SILInstruction *User = Use->getUser();
if (isa<RefCountingInst>(User))
continue;
if (isDebugInst(User))
continue;
auto FAS = FullApplySite::isa(User);
if (FAS && FAS.getCallee() == Apply.getInstruction())
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(RefF, Apply.getSubstitutions(),
Fragile, ReInfo);
SILFunction *SpecializedF = FuncSpecializer.lookupSpecialization();
if (SpecializedF) {
// Even if the pre-specialization exists already, try to preserve it
// if it is whitelisted.
linkSpecialization(M, SpecializedF);
} else {
SpecializedF = FuncSpecializer.tryCreateSpecialization();
if (!SpecializedF)
return;
assert(SpecializedF->hasUnqualifiedOwnership());
NewFunctions.push_back(SpecializedF);
}
assert(ReInfo.getSpecializedType()
== SpecializedF->getLoweredFunctionType() &&
"Previously specialized function does not match expected type.");
// FIXME: Replace pre-specialization's "keep as public" hack with something
// more principled
assert((Fragile == SpecializedF->isFragile() ||
SpecializedF->isKeepAsPublic()) &&
"Previously specialized function does not match expected "
"resilience level.");
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());
SILBuilderWithScope Builder(PAI);
SILFunction *Thunk = createReabstractionThunk(ReInfo, PAI, SpecializedF);
NewFunctions.push_back(Thunk);
auto *FRI = Builder.createFunctionRef(PAI->getLoc(), Thunk);
SmallVector<SILValue, 4> Arguments;
for (auto &Op : PAI->getArgumentOperands()) {
Arguments.push_back(Op.get());
}
auto *NewPAI = Builder.createPartialApply(PAI->getLoc(), FRI,
PAI->getSubstCalleeSILType(),
{},
Arguments,
PAI->getType());
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)) {
SILBuilder Builder(User);
replaceWithSpecializedCallee(FAS, NewPAI, Builder, 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.
//
// This uses the SIL linker to checks for the does not load the body of the pres
// =============================================================================
static void keepSpecializationAsPublic(SILFunction *F) {
DEBUG(auto DemangledNameString =
swift::Demangle::demangleSymbolAsString(F->getName());
StringRef DemangledName = DemangledNameString;
llvm::dbgs() << "Keep specialization public: " << DemangledName << " : "
<< F->getName() << "\n");
// Make it public, so that others can refer to it.
//
// NOTE: This function may refer to non-public symbols, which may lead to
// problems, if you ever try to inline this function. Therefore, these
// specializations should only be used to refer to them, but should never
// be inlined! The general rule could be: Never inline specializations
// from stdlib!
//
// NOTE: Making these specializations public at this point breaks
// some optimizations. Therefore, just mark the function.
// DeadFunctionElimination pass will check if the function is marked
// and preserve it if required.
F->setKeepAsPublic(true);
}
/// Link a specialization for generating prespecialized code.
///
/// For now, it is performed only for specializations in the
/// standard library. But in the future, one could think of
/// maintaining a cache of optimized specializations.
///
/// Mark specializations as public, so that they can be used by user
/// applications. These specializations are generated during -O compilation of
/// the library, but only used only by client code compiled at -Onone. They
/// should be never inlined.
static bool linkSpecialization(SILModule &M, SILFunction *F) {
if (F->isKeepAsPublic())
return true;
// Do not remove functions from the white-list. Keep them around.
// Change their linkage to public, so that other applications can refer to it.
if (M.getOptions().Optimization >= SILOptions::SILOptMode::Optimize &&
F->getModule().getSwiftModule()->getName().str() == SWIFT_ONONE_SUPPORT) {
if (isWhitelistedSpecialization(F->getName())) {
keepSpecializationAsPublic(F);
return true;
}
}
return false;
}
// The whitelist of classes and functions from the stdlib,
// whose specializations we want to preserve.
static const char *const WhitelistedSpecializations[] = {
"Array",
"_ArrayBuffer",
"_ContiguousArrayBuffer",
"Range",
"RangeIterator",
"CountableRange",
"CountableRangeIterator",
"ClosedRange",
"ClosedRangeIterator",
"CountableClosedRange",
"CountableClosedRangeIterator",
"IndexingIterator",
"Collection",
"MutableCollection",
"BidirectionalCollection",
"RandomAccessCollection",
"RangeReplaceableCollection",
"_allocateUninitializedArray",
"UTF8",
"UTF16",
"String",
"_StringBuffer",
"_toStringReadOnlyPrintable",
};
/// Check of a given name could be a name of a white-listed
/// specialization.
bool swift::isWhitelistedSpecialization(StringRef SpecName) {
// TODO: Once there is an efficient API to check if
// a given symbol is a specialization of a specific type,
// use it instead. Doing demangling just for this check
// is just wasteful.
auto DemangledNameString =
swift::Demangle::demangleSymbolAsString(SpecName);
StringRef DemangledName = DemangledNameString;
DEBUG(llvm::dbgs() << "Check if whitelisted: " << DemangledName << "\n");
auto pos = DemangledName.find("generic ", 0);
auto oldpos = pos;
if (pos == StringRef::npos)
return false;
// Create "of Swift"
llvm::SmallString<64> OfString;
llvm::raw_svector_ostream buffer(OfString);
buffer << "of ";
buffer << STDLIB_NAME <<'.';
StringRef OfStr = buffer.str();
DEBUG(llvm::dbgs() << "Check substring: " << OfStr << "\n");
pos = DemangledName.find(OfStr, oldpos);
if (pos == StringRef::npos) {
// Create "of (extension in Swift).Swift"
llvm::SmallString<64> OfString;
llvm::raw_svector_ostream buffer(OfString);
buffer << "of (extension in " << STDLIB_NAME << "):";
buffer << STDLIB_NAME << '.';
OfStr = buffer.str();
pos = DemangledName.find(OfStr, oldpos);
DEBUG(llvm::dbgs() << "Check substring: " << OfStr << "\n");
if (pos == StringRef::npos)
return false;
}
pos += OfStr.size();
for (auto NameStr: WhitelistedSpecializations) {
StringRef Name = NameStr;
auto pos1 = DemangledName.find(Name, pos);
if (pos1 == pos && !isalpha(DemangledName[pos1+Name.size()])) {
return true;
}
}
return false;
}
/// 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 (isWhitelistedSpecialization(FunctionName))
return M.hasFunction(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");
DEBUG(llvm::dbgs() << "Found existing specialization for: " << FunctionName
<< '\n';
llvm::dbgs() << swift::Demangle::demangleSymbolAsString(
Specialization->getName())
<< "\n\n");
return Specialization;
}