blob: d47e48ff9c41d48038c1a6fcc6fe8ebb389e0b3b [file] [log] [blame]
//===--- 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"
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;
}
TypeSubstitutionMap InterfaceSubs;
if (OrigF->getLoweredFunctionType()->getGenericSignature())
InterfaceSubs = OrigF->getLoweredFunctionType()->getGenericSignature()
->getSubstitutionMap(ParamSubs);
// We do not support partial specialization.
if (hasUnboundGenericTypes(InterfaceSubs)) {
DEBUG(llvm::dbgs() <<
" Cannot specialize with unbound interface substitutions.\n");
DEBUG(for (auto Sub : ParamSubs) {
Sub.dump();
});
return;
}
if (hasDynamicSelfTypes(InterfaceSubs)) {
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,
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!");
if (GenericFunc->getContextGenericParams())
ContextSubs = GenericFunc->getContextGenericParams()
->getSubstitutionMap(ParamSubs);
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, ContextSubs,
ParamSubs, ClonedName);
// 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());
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->getNumBBArg() == 1);
Builder.setInsertionPoint(ResultBB->begin());
fixUsedVoidType(ResultBB->getBBArg(0), Loc, Builder);
SILArgument *Arg =
ResultBB->replaceBBArg(0, StoreResultTo->getType().getObjectType());
// Store the direct result to the original result address.
Builder.createStore(Loc, Arg, StoreResultTo);
}
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);
}
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 = new (M) SILBasicBlock(Thunk);
SILBuilder Builder(EntryBB);
SILBasicBlock *SpecEntryBB = &*SpecializedFunc->begin();
CanSILFunctionType SpecType = SpecializedFunc->getLoweredFunctionType();
SILArgument *ReturnValueAddr = nullptr;
// Convert indirect to direct parameters/results.
SmallVector<SILValue, 4> Arguments;
auto SpecArgIter = SpecEntryBB->bbarg_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);
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 = new (M) SILBasicBlock(Thunk);
SILBasicBlock *ErrorBB = new (M) SILBasicBlock(Thunk);
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);
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;
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;
}
/// Check of a given name could be a name of a white-listed
/// specialization.
bool swift::isWhitelistedSpecialization(StringRef SpecName) {
// The whitelist of classes and functions from the stdlib,
// whose specializations we want to preserve.
ArrayRef<StringRef> Whitelist = {
"Array",
"_ArrayBuffer",
"_ContiguousArrayBuffer",
"Range",
"RangeIterator",
"CountableRange",
"CountableRangeIterator",
"ClosedRange",
"ClosedRangeIterator",
"CountableClosedRange",
"CountableClosedRangeIterator",
"IndexingIterator",
"Collection",
"MutableCollection",
"BidirectionalCollection",
"RandomAccessCollection",
"RangeReplaceableCollection",
"_allocateUninitializedArray",
"UTF8",
"UTF16",
"String",
"_StringBuffer",
"_toStringReadOnlyPrintable",
};
// 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 Name: Whitelist) {
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;
}