mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
2303 lines
89 KiB
C++
2303 lines
89 KiB
C++
//===--- 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/Strings.h"
|
|
#include "swift/SILOptimizer/Utils/Generics.h"
|
|
#include "swift/SILOptimizer/Utils/GenericCloner.h"
|
|
#include "swift/SILOptimizer/Utils/SpecializationMangler.h"
|
|
#include "swift/SIL/DebugUtils.h"
|
|
#include "swift/AST/GenericSignatureBuilder.h"
|
|
#include "swift/AST/GenericEnvironment.h"
|
|
|
|
using namespace swift;
|
|
|
|
/// Set to true to enable the support for partial specialization.
|
|
llvm::cl::opt<bool> EnablePartialSpecialization(
|
|
"sil-partial-specialization", llvm::cl::init(true),
|
|
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> SpecializeGenericSubstitutions(
|
|
"sil-partial-specialization-with-generic-substitutions",
|
|
llvm::cl::init(false),
|
|
llvm::cl::desc("Enable partial specialization with generic substitutions"));
|
|
|
|
static bool OptimizeGenericSubstitutions = false;
|
|
static bool SupportGenericSubstitutions = true;
|
|
|
|
// 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 += std::distance(StoredProperties.begin(), StoredProperties.end());
|
|
}
|
|
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.getType());
|
|
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.getType());
|
|
if (TypeDepth > MaxTypeDepth)
|
|
MaxTypeDepth = TypeDepth;
|
|
Width += TypeWidth;
|
|
}
|
|
if (FnTy->hasErrorResult()) {
|
|
Width += 1;
|
|
unsigned TypeWidth;
|
|
unsigned TypeDepth;
|
|
std::tie(TypeDepth, TypeWidth) =
|
|
getTypeDepthAndWidth(FnTy->getErrorResult().getType());
|
|
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;
|
|
unsigned TypeWidth;
|
|
unsigned TypeDepth;
|
|
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(FnTy->getInput());
|
|
if (TypeDepth > MaxTypeDepth)
|
|
MaxTypeDepth = TypeDepth;
|
|
Width += TypeWidth;
|
|
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;
|
|
}
|
|
|
|
// =============================================================================
|
|
// ReabstractionInfo
|
|
// =============================================================================
|
|
|
|
static bool shouldNotSpecializeCallee(SILFunction *Callee) {
|
|
if (!Callee->shouldOptimize()) {
|
|
DEBUG(llvm::dbgs() << " Cannot specialize function " << Callee->getName()
|
|
<< " marked to be excluded from optimizations.\n");
|
|
return true;
|
|
}
|
|
|
|
if (Callee->hasSemanticsAttr("optimize.sil.specialize.generic.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,
|
|
SubstitutionList ParamSubs) {
|
|
if (shouldNotSpecializeCallee(Callee))
|
|
return false;
|
|
|
|
SpecializedGenericEnv = nullptr;
|
|
SpecializedGenericSig = nullptr;
|
|
OriginalParamSubs = ParamSubs;
|
|
auto CalleeGenericSig = Callee->getLoweredFunctionType()->getGenericSignature();
|
|
auto CalleeGenericEnv = Callee->getGenericEnvironment();
|
|
|
|
OriginalF = Callee;
|
|
this->Apply = Apply;
|
|
|
|
SubstitutionMap InterfaceSubs;
|
|
|
|
// Get the original substitution map.
|
|
if (CalleeGenericSig)
|
|
InterfaceSubs = CalleeGenericSig->getSubstitutionMap(ParamSubs);
|
|
|
|
// We do not support partial specialization.
|
|
if (!EnablePartialSpecialization && InterfaceSubs.hasArchetypes()) {
|
|
DEBUG(llvm::dbgs() << " Partial specialization is not supported.\n");
|
|
DEBUG(for (auto Sub : ParamSubs) { Sub.dump(); });
|
|
return false;
|
|
}
|
|
|
|
// Perform some checks to see if we need to bail.
|
|
if (InterfaceSubs.hasDynamicSelf()) {
|
|
DEBUG(llvm::dbgs() << " Cannot specialize with dynamic self.\n");
|
|
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 Sub : ParamSubs) {
|
|
auto Replacement = Sub.getReplacement();
|
|
if (isTypeTooComplex(Replacement)) {
|
|
DEBUG(llvm::dbgs()
|
|
<< " Cannot specialize because the generic type is too deep.\n");
|
|
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;
|
|
for (auto GP : CalleeGenericSig->getSubstitutableParams()) {
|
|
// Check only the substitutions for the generic parameters.
|
|
// Ignore any dependent types, etc.
|
|
auto Replacement = Type(GP).subst(InterfaceSubs);
|
|
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;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
HasConcreteGenericParams = true;
|
|
}
|
|
|
|
if (HasUnboundGenericParams) {
|
|
// Bail if we cannot specialize generic substitutions, but all substitutions
|
|
// were generic.
|
|
if (!HasConcreteGenericParams && !SpecializeGenericSubstitutions)
|
|
return false;
|
|
|
|
if (!HasNonArchetypeGenericParams && !HasConcreteGenericParams) {
|
|
DEBUG(llvm::dbgs() << " Partial specialization is not supported if "
|
|
"all substitutions are archetypes.\n");
|
|
DEBUG(for (auto Sub : ParamSubs) {
|
|
Sub.dump();
|
|
});
|
|
return false;
|
|
}
|
|
|
|
// We need a generic environment for the partial specialization.
|
|
if (!CalleeGenericEnv)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ReabstractionInfo::canBeSpecialized(ApplySite Apply, SILFunction *Callee,
|
|
SubstitutionList ParamSubs) {
|
|
ReabstractionInfo ReInfo;
|
|
return ReInfo.prepareAndCheck(Apply, Callee, ParamSubs);
|
|
}
|
|
|
|
ReabstractionInfo::ReabstractionInfo(ApplySite Apply, SILFunction *Callee,
|
|
ArrayRef<Substitution> ParamSubs,
|
|
bool ConvertIndirectToDirect) {
|
|
if (!prepareAndCheck(Apply, Callee, ParamSubs))
|
|
return;
|
|
|
|
this->ConvertIndirectToDirect = ConvertIndirectToDirect;
|
|
|
|
if (SpecializeGenericSubstitutions) {
|
|
specializeConcreteAndGenericSubstitutions(Apply, Callee, ParamSubs);
|
|
} else {
|
|
specializeConcreteSubstitutions(Apply, Callee, ParamSubs);
|
|
}
|
|
verify();
|
|
if (SpecializedGenericSig) {
|
|
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 sanity checks.
|
|
auto SpecializedFnTy = getSpecializedType();
|
|
auto SpecializedSubstFnTy = SpecializedFnTy;
|
|
|
|
if (SpecializedFnTy->isPolymorphic() &&
|
|
!getCallerParamSubstitutions().empty()) {
|
|
auto CalleeFnTy = Callee->getLoweredFunctionType();
|
|
assert(CalleeFnTy->isPolymorphic());
|
|
auto CalleeSubstFnTy = CalleeFnTy->substGenericArgs(
|
|
Callee->getModule(), getOriginalParamSubstitutions());
|
|
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(), getCallerParamSubstitutions());
|
|
|
|
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";
|
|
for (auto Sub : getCallerParamSubstitutions()) {
|
|
llvm::dbgs() << "Sub:\n";
|
|
Sub.dump();
|
|
}
|
|
llvm::dbgs() << "\n\n";
|
|
|
|
llvm::dbgs() << "CalleeFnTy:\n" << CalleeFnTy << "\n";
|
|
llvm::dbgs() << "SpecializedCalleeSubstFnTy:\n" << SpecializedCalleeSubstFnTy << "\n";
|
|
for (auto Sub : ParamSubs) {
|
|
llvm::dbgs() << "Sub:\n";
|
|
Sub.dump();
|
|
}
|
|
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()) {
|
|
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 specializaiton.
|
|
DEBUG(llvm::dbgs() << "Specializing the call:\n";
|
|
Apply.getInstruction()->dumpInContext();
|
|
llvm::dbgs() << "\n\nPartially specialized types for function: "
|
|
<< Callee->getName() << "\n\n";
|
|
llvm::dbgs() << "Original generic function type:\n"
|
|
<< Callee->getLoweredFunctionType() << "\n\n";
|
|
llvm::dbgs() << "\nOriginal call substitution:\n";
|
|
for (auto Sub : getOriginalParamSubstitutions()) {
|
|
llvm::dbgs() << "Sub:\n";
|
|
Sub.dump();
|
|
llvm::dbgs() << "\n";
|
|
}
|
|
|
|
llvm::dbgs() << "Partially specialized generic function type:\n"
|
|
<< getSpecializedType() << "\n\n";
|
|
llvm::dbgs() << "\nSpecialization call substitution:\n";
|
|
for (auto Sub : getCallerParamSubstitutions()) {
|
|
llvm::dbgs() << "Sub:\n";
|
|
Sub.dump();
|
|
llvm::dbgs() << "\n";
|
|
});
|
|
}
|
|
}
|
|
|
|
bool ReabstractionInfo::canBeSpecialized() const {
|
|
return getSpecializedType();
|
|
}
|
|
|
|
bool ReabstractionInfo::isFullSpecialization() const {
|
|
return !hasArchetypes(getOriginalParamSubstitutions());
|
|
}
|
|
|
|
bool ReabstractionInfo::isPartialSpecialization() const {
|
|
return hasArchetypes(getOriginalParamSubstitutions());
|
|
}
|
|
|
|
void ReabstractionInfo::createSubstitutedAndSpecializedTypes() {
|
|
auto &M = OriginalF->getModule();
|
|
|
|
// Find out how the function type looks like after applying the provided
|
|
// substitutions.
|
|
if (!SubstitutedType) {
|
|
SubstitutedType = createSubstitutedType(
|
|
OriginalF, 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();
|
|
Conversions.resize(NumFormalIndirectResults +
|
|
SubstitutedType->getParameters().size());
|
|
|
|
CanGenericSignature CanSig;
|
|
if (SpecializedGenericSig)
|
|
CanSig = SpecializedGenericSig->getCanonicalSignature();
|
|
Lowering::GenericContextScope GenericScope(M.Types, CanSig);
|
|
|
|
SILFunctionConventions substConv(SubstitutedType, M);
|
|
|
|
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());
|
|
if (substConv.getSILType(RI).isLoadable(M) && !RI.getType()->isVoid()) {
|
|
Conversions.set(IdxForResult);
|
|
break;
|
|
}
|
|
++IdxForResult;
|
|
}
|
|
}
|
|
|
|
// Try to convert indirect incoming parameters to direct parameters.
|
|
unsigned IdxForParam = NumFormalIndirectResults;
|
|
for (SILParameterInfo PI : SubstitutedType->getParameters()) {
|
|
if (substConv.getSILType(PI).isLoadable(M) &&
|
|
PI.getConvention() == ParameterConvention::Indirect_In) {
|
|
Conversions.set(IdxForParam);
|
|
}
|
|
++IdxForParam;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
/// Create a new substituted type with the updated signature.
|
|
CanSILFunctionType
|
|
ReabstractionInfo::createSubstitutedType(SILFunction *OrigF,
|
|
const SubstitutionMap &SubstMap,
|
|
bool HasUnboundGenericParams) {
|
|
auto &M = OrigF->getModule();
|
|
if ((SpecializedGenericSig &&
|
|
SpecializedGenericSig->areAllParamsConcrete()) ||
|
|
!HasUnboundGenericParams) {
|
|
SpecializedGenericSig = nullptr;
|
|
SpecializedGenericEnv = nullptr;
|
|
}
|
|
|
|
CanGenericSignature CanSpecializedGenericSig;
|
|
if (SpecializedGenericSig)
|
|
CanSpecializedGenericSig = SpecializedGenericSig->getCanonicalSignature();
|
|
|
|
// First substitute concrete types into the existing function type.
|
|
CanSILFunctionType FnTy;
|
|
{
|
|
Lowering::GenericContextScope GenericScope(M.Types,
|
|
CanSpecializedGenericSig);
|
|
FnTy = OrigF->getLoweredFunctionType()->substGenericArgs(M, SubstMap);
|
|
// 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.
|
|
FnTy = cast<SILFunctionType>(
|
|
CanSpecializedGenericSig->getCanonicalTypeInContext(
|
|
FnTy, *M.getSwiftModule()));
|
|
}
|
|
assert(FnTy);
|
|
|
|
// Use the new specialized generic signature.
|
|
auto NewFnTy = SILFunctionType::get(
|
|
CanSpecializedGenericSig, FnTy->getExtInfo(), FnTy->getCalleeConvention(),
|
|
FnTy->getParameters(), FnTy->getResults(), FnTy->getOptionalErrorResult(),
|
|
M.getASTContext());
|
|
|
|
// 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 {
|
|
llvm::SmallVector<SILResultInfo, 8> SpecializedResults;
|
|
llvm::SmallVector<SILParameterInfo, 8> SpecializedParams;
|
|
|
|
unsigned IndirectResultIdx = 0;
|
|
for (SILResultInfo RI : SubstFTy->getResults()) {
|
|
if (RI.isFormalIndirect()) {
|
|
if (isFormalResultConverted(IndirectResultIdx++)) {
|
|
// 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));
|
|
continue;
|
|
}
|
|
}
|
|
// No conversion: re-use the original, substituted 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, substituted parameter info.
|
|
SpecializedParams.push_back(PI);
|
|
}
|
|
}
|
|
return
|
|
SILFunctionType::get(SubstFTy->getGenericSignature(),
|
|
SubstFTy->getExtInfo(),
|
|
SubstFTy->getCalleeConvention(), SpecializedParams,
|
|
SpecializedResults, SubstFTy->getOptionalErrorResult(),
|
|
M.getASTContext());
|
|
}
|
|
|
|
static std::pair<GenericEnvironment *, GenericSignature *>
|
|
getGenericEnvironmentAndSignature(GenericSignatureBuilder &Builder,
|
|
SILModule &M) {
|
|
auto *GenericSig =
|
|
Builder.getGenericSignature()->getCanonicalSignature().getPointer();
|
|
auto *GenericEnv = GenericSig->createGenericEnvironment(*M.getSwiftModule());
|
|
|
|
// FIXME: This is a workaround for the signature minimization bug.
|
|
GenericSignatureBuilder TmpBuilder(
|
|
M.getASTContext(), LookUpConformanceInModule(M.getSwiftModule()));
|
|
TmpBuilder.addGenericSignature(GenericSig);
|
|
TmpBuilder.finalize(SourceLoc(), GenericSig->getGenericParams(),
|
|
/*allowConcreteGenericParams=*/true);
|
|
GenericSig =
|
|
TmpBuilder.getGenericSignature()->getCanonicalSignature().getPointer();
|
|
GenericEnv = GenericSig->createGenericEnvironment(*M.getSwiftModule());
|
|
|
|
assert(!GenericSig || GenericEnv);
|
|
return std::make_pair(GenericEnv, GenericSig);
|
|
}
|
|
|
|
std::pair<GenericEnvironment *, GenericSignature *>
|
|
getSignatureWithRequirements(GenericSignature *OrigGenSig,
|
|
GenericEnvironment *OrigGenericEnv,
|
|
ArrayRef<Requirement> Requirements,
|
|
SILModule &M) {
|
|
// Form a new generic signature based on the old one.
|
|
GenericSignatureBuilder Builder(M.getASTContext(),
|
|
LookUpConformanceInModule(M.getSwiftModule()));
|
|
|
|
// First, add the old generic signature.
|
|
Builder.addGenericSignature(OrigGenSig);
|
|
|
|
auto Source =
|
|
GenericSignatureBuilder::FloatingRequirementSource::forAbstract();
|
|
// For each substitution with a concrete type as a replacement,
|
|
// add a new concrete type equality requirement.
|
|
for (auto &Req : Requirements) {
|
|
Builder.addRequirement(Req, Source);
|
|
}
|
|
|
|
Builder.finalize(SourceLoc(), OrigGenSig->getGenericParams(),
|
|
/*allowConcreteGenericParams=*/true);
|
|
return getGenericEnvironmentAndSignature(Builder, M);
|
|
}
|
|
|
|
/// Perform some sanity checks for the requirements
|
|
static void
|
|
checkSpecializationRequirements(ArrayRef<Requirement> Requirements) {
|
|
for (auto &Req : Requirements) {
|
|
if (Req.getKind() == RequirementKind::SameType) {
|
|
auto FirstType = Req.getFirstType();
|
|
auto SecondType = Req.getSecondType();
|
|
assert(FirstType && SecondType);
|
|
assert(!FirstType->hasArchetype());
|
|
assert(!SecondType->hasArchetype());
|
|
|
|
// Only one of the types should be concrete.
|
|
assert(FirstType->hasTypeParameter() != SecondType->hasTypeParameter() &&
|
|
"Only concrete type same-type requirements are supported by "
|
|
"generic specialization");
|
|
|
|
(void) FirstType;
|
|
(void) SecondType;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (Req.getKind() == RequirementKind::Layout) {
|
|
continue;
|
|
}
|
|
|
|
llvm_unreachable("Unknown type of requirement in generic specialization");
|
|
}
|
|
}
|
|
|
|
ReabstractionInfo::ReabstractionInfo(SILFunction *OrigF,
|
|
ArrayRef<Requirement> Requirements) {
|
|
if (shouldNotSpecializeCallee(OrigF))
|
|
return;
|
|
|
|
// Perform some sanity checks for the requirements
|
|
checkSpecializationRequirements(Requirements);
|
|
|
|
OriginalF = OrigF;
|
|
ConvertIndirectToDirect = true;
|
|
|
|
SILModule &M = OrigF->getModule();
|
|
auto &Ctx = M.getASTContext();
|
|
|
|
auto OrigGenericSig = OrigF->getLoweredFunctionType()->getGenericSignature();
|
|
auto OrigGenericEnv = OrigF->getGenericEnvironment();
|
|
|
|
std::tie(SpecializedGenericEnv, SpecializedGenericSig) =
|
|
getSignatureWithRequirements(OrigGenericSig, OrigGenericEnv,
|
|
Requirements, M);
|
|
|
|
{
|
|
SmallVector<Substitution, 4> List;
|
|
|
|
OrigGenericSig->getSubstitutions(
|
|
[&](SubstitutableType *type) -> Type {
|
|
return SpecializedGenericEnv->mapTypeIntoContext(type);
|
|
},
|
|
LookUpConformanceInSignature(*SpecializedGenericSig),
|
|
List);
|
|
ClonerParamSubs = Ctx.AllocateCopy(List);
|
|
}
|
|
|
|
{
|
|
SmallVector<Substitution, 4> List;
|
|
|
|
SpecializedGenericSig->getSubstitutions(
|
|
[&](SubstitutableType *type) -> Type {
|
|
return OrigGenericEnv->mapTypeIntoContext(type);
|
|
},
|
|
LookUpConformanceInSignature(*SpecializedGenericSig),
|
|
List);
|
|
CallerParamSubs = Ctx.AllocateCopy(List);
|
|
}
|
|
|
|
{
|
|
CallerInterfaceSubs = OrigGenericSig->getSubstitutionMap(
|
|
[&](SubstitutableType *type) -> Type {
|
|
return SpecializedGenericEnv->mapTypeOutOfContext(
|
|
SpecializedGenericEnv->mapTypeIntoContext(type));
|
|
},
|
|
LookUpConformanceInSignature(*SpecializedGenericSig));
|
|
}
|
|
|
|
OriginalParamSubs = CallerParamSubs;
|
|
|
|
HasUnboundGenericParams = !SpecializedGenericSig->areAllParamsConcrete();
|
|
createSubstitutedAndSpecializedTypes();
|
|
}
|
|
|
|
static void verifySubstitutionList(SubstitutionList Subs, StringRef Name) {
|
|
DEBUG(llvm::dbgs() << "\nSubstitutions for " << Name << "\n";
|
|
for (auto Sub : Subs) {
|
|
Sub.getReplacement()->dump();
|
|
});
|
|
for (auto Sub : Subs) {
|
|
assert(!Sub.getReplacement()->hasError() &&
|
|
"There should be no error types in substitutions");
|
|
}
|
|
}
|
|
|
|
// Builds a new signature based on the old one and adds same type
|
|
// requirements for those generic type parameters that are concrete
|
|
// according to the partial substitution. This way, the signature
|
|
// has exactly the same generic parameter types, just with more
|
|
// requirements.
|
|
//
|
|
// Initialize SpecializedType if and only if the specialization is allowed.
|
|
void ReabstractionInfo::specializeConcreteSubstitutions(
|
|
ApplySite Apply, SILFunction *Callee, ArrayRef<Substitution> ParamSubs) {
|
|
|
|
SILModule &M = Callee->getModule();
|
|
auto &Ctx = M.getASTContext();
|
|
|
|
OriginalF = Callee;
|
|
|
|
auto OrigGenericSig = Callee->getLoweredFunctionType()->getGenericSignature();
|
|
auto OrigGenericEnv = Callee->getGenericEnvironment();
|
|
|
|
SubstitutionMap InterfaceSubs;
|
|
// Get the original substitution map.
|
|
if (Callee->getLoweredFunctionType()->getGenericSignature())
|
|
InterfaceSubs = Callee->getLoweredFunctionType()->getGenericSignature()
|
|
->getSubstitutionMap(ParamSubs);
|
|
|
|
// This is a workaround for the rdar://30610428
|
|
if (!EnablePartialSpecialization) {
|
|
SubstitutedType =
|
|
Callee->getLoweredFunctionType()->substGenericArgs(M, InterfaceSubs);
|
|
ClonerParamSubs = OriginalParamSubs;
|
|
CallerParamSubs = {};
|
|
createSubstitutedAndSpecializedTypes();
|
|
return;
|
|
}
|
|
|
|
// Build a set of requirements.
|
|
SmallVector<Requirement, 4> Requirements;
|
|
|
|
for (auto DP : OrigGenericSig->getGenericParams()) {
|
|
auto Replacement = Type(DP).subst(InterfaceSubs);
|
|
if (Replacement->hasArchetype())
|
|
continue;
|
|
// Replacemengt is concrete. Add a same type requirement.
|
|
Requirement Req(RequirementKind::SameType, DP, Replacement);
|
|
Requirements.push_back(Req);
|
|
}
|
|
|
|
std::tie(SpecializedGenericEnv, SpecializedGenericSig) =
|
|
getSignatureWithRequirements(OrigGenericSig, OrigGenericEnv,
|
|
Requirements, M);
|
|
HasUnboundGenericParams = !SpecializedGenericSig->areAllParamsConcrete();
|
|
|
|
{
|
|
SmallVector<Substitution, 4> List;
|
|
|
|
OrigGenericSig->getSubstitutions(
|
|
[&](SubstitutableType *type) -> Type {
|
|
return SpecializedGenericEnv->mapTypeIntoContext(type);
|
|
},
|
|
LookUpConformanceInSignature(*SpecializedGenericSig),
|
|
List);
|
|
ClonerParamSubs = Ctx.AllocateCopy(List);
|
|
verifySubstitutionList(ClonerParamSubs, "ClonerParamSubs");
|
|
}
|
|
|
|
{
|
|
SmallVector<Substitution, 4> List;
|
|
SpecializedGenericSig->getSubstitutions(InterfaceSubs, List);
|
|
CallerParamSubs = Ctx.AllocateCopy(List);
|
|
verifySubstitutionList(CallerParamSubs, "CallerParamSubs");
|
|
}
|
|
|
|
{
|
|
CallerInterfaceSubs = OrigGenericSig->getSubstitutionMap(
|
|
[&](SubstitutableType *type) -> Type {
|
|
return SpecializedGenericEnv->mapTypeOutOfContext(
|
|
SpecializedGenericEnv->mapTypeIntoContext(type));
|
|
},
|
|
//LookUpConformanceInSignature(*SpecializedGenericSig));
|
|
LookUpConformanceInSignature(*OrigGenericSig));
|
|
}
|
|
|
|
HasUnboundGenericParams = !SpecializedGenericSig->areAllParamsConcrete();
|
|
createSubstitutedAndSpecializedTypes();
|
|
}
|
|
|
|
/// 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,
|
|
ArrayRef<ProtocolConformanceRef> Conformances) {
|
|
// 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 (OptimizeGenericSubstitutions) {
|
|
// Is it an unconstrained generic parameter?
|
|
if (Conformances.empty()) {
|
|
if (Replacement->is<ArchetypeType>()) {
|
|
// TODO: If Replacement add a new layout constraint, then
|
|
// it may be still useful to perform the partial specialization.
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void remapRequirements(GenericSignature *GenSig,
|
|
SubstitutionMap &SubsMap,
|
|
GenericSignatureBuilder &Builder,
|
|
ModuleDecl *SM) {
|
|
auto source =
|
|
GenericSignatureBuilder::FloatingRequirementSource::forAbstract();
|
|
|
|
if (!GenSig)
|
|
return;
|
|
|
|
// Next, add each of the requirements (mapped from the requirement's
|
|
// interface types into the specialized interface type parameters).
|
|
// RequirementSource source(RequirementSource::Explicit, SourceLoc());
|
|
|
|
// Add requirements derived from the caller signature for the
|
|
// caller's archetypes mapped to the specialized signature.
|
|
for (auto &reqReq : GenSig->getRequirements()) {
|
|
DEBUG(llvm::dbgs() << "\n\nRe-mapping the requirement:\n"; reqReq.dump());
|
|
|
|
auto first = reqReq.getFirstType();
|
|
// Is this generic type equivalent to a concrete type?
|
|
if (first->hasTypeParameter() &&
|
|
!GenSig->getCanonicalTypeInContext(first, *SM)->hasTypeParameter())
|
|
continue;
|
|
|
|
first = reqReq.getFirstType().subst(SubsMap);
|
|
assert(!first->hasError());
|
|
|
|
auto Kind = reqReq.getKind();
|
|
|
|
switch (Kind) {
|
|
case RequirementKind::SameType:
|
|
case RequirementKind::Superclass:
|
|
case RequirementKind::Conformance: {
|
|
auto second = reqReq.getSecondType().subst(SubsMap);
|
|
assert(!second->hasError());
|
|
// Substitute the constrained types.
|
|
if (Kind != RequirementKind::SameType && !first->hasTypeParameter())
|
|
break;
|
|
if (Kind == RequirementKind::SameType && !first->hasTypeParameter() &&
|
|
!second->hasTypeParameter())
|
|
break;
|
|
|
|
Requirement Req(Kind, first, second);
|
|
auto Failure = Builder.addRequirement(Req, source);
|
|
assert(!Failure);
|
|
DEBUG(llvm::dbgs() << "\nRe-mapped requirement:\n"; Req.dump());
|
|
break;
|
|
}
|
|
case RequirementKind::Layout: {
|
|
if (!first->hasTypeParameter())
|
|
break;
|
|
|
|
Requirement Req(RequirementKind::Layout, first,
|
|
reqReq.getLayoutConstraint());
|
|
auto Failure = Builder.addRequirement(Req, source);
|
|
assert(!Failure);
|
|
DEBUG(llvm::dbgs() << "\nRe-mapped requirement:\n"; Req.dump());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// A helper class for creating partially specialized function signatures.
|
|
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 SubstitutionList.
|
|
SubstitutionMap CalleeInterfaceToCallerArchetypeMap;
|
|
|
|
// Maps callee's interface types to specialzed functions interface types.
|
|
SubstitutionMap CalleeInterfaceToSpecializedInterfaceMap;
|
|
|
|
SILModule &M;
|
|
ModuleDecl *SM;
|
|
ASTContext &Ctx;
|
|
|
|
// This is a builder for a new partially specialized generic signature.
|
|
GenericSignatureBuilder Builder;
|
|
|
|
// Set of newly created generic type parameters.
|
|
SmallVector<GenericTypeParamType*, 4> AllGenericParams;
|
|
|
|
// 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(
|
|
GenericEnvironment *CallerGenericEnv);
|
|
|
|
void createGenericParamsForCalleeGenericParams(
|
|
GenericSignature *CalleeGenericSig, GenericEnvironment *CallerGenericEnv);
|
|
|
|
void addRequirements(GenericSignature *CalleeGenericSig);
|
|
|
|
std::pair<GenericEnvironment *, GenericSignature *>
|
|
getSpecializedGenericEnvironmentAndSignature();
|
|
|
|
void computeCallerInterfaceToSpecializedInterfaceMap(
|
|
GenericSignature *CallerGenericSig);
|
|
|
|
void computeCalleeInterfaceToSpecializedInterfaceMap(
|
|
GenericSignature *CalleeGenericSig);
|
|
|
|
/// 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 callectUsedCallerArchetypes(SubstitutionList ParamSubs);
|
|
|
|
// Create a new generic parameter.
|
|
GenericTypeParamType *createGenericParam();
|
|
|
|
public:
|
|
FunctionSignaturePartialSpecializer(SILModule &M)
|
|
: M(M), SM(M.getSwiftModule()), Ctx(M.getASTContext()),
|
|
Builder(Ctx, LookUpConformanceInModule(SM)) {}
|
|
|
|
std::pair<GenericEnvironment *, GenericSignature *>
|
|
createSpecializedGenericSignature(GenericSignature *CallerGenericSig,
|
|
GenericEnvironment *CallerGenericEnv,
|
|
GenericSignature *CalleeGenericSig,
|
|
SubstitutionList ParamSubs);
|
|
|
|
void computeClonerParamSubs(GenericSignature *CalleeGenericSig,
|
|
ReabstractionInfo &ReInfo,
|
|
SubstitutionList &ClonerParamSubs);
|
|
|
|
void computeCallerParamSubs(GenericSignature *SpecializedGenericSig,
|
|
SubstitutionList &CallerParamSubs);
|
|
|
|
void computeCallerInterfaceSubs(GenericSignature *CalleeGenericSig,
|
|
ReabstractionInfo &ReInfo,
|
|
SubstitutionMap &CallerInterfaceSubs);
|
|
|
|
CanSILFunctionType
|
|
getSpecializedSubstFunctionType(CanSILFunctionType CalleeFnTy,
|
|
ReabstractionInfo &ReInfo);
|
|
};
|
|
|
|
} // end of namespace
|
|
|
|
GenericTypeParamType *
|
|
FunctionSignaturePartialSpecializer::createGenericParam() {
|
|
auto GP = GenericTypeParamType::get(0, GPIdx++, Ctx);
|
|
AllGenericParams.push_back(GP);
|
|
Builder.addGenericParameter(GP);
|
|
return GP;
|
|
}
|
|
|
|
/// Collect all used caller's archetypes from all the substitutions.
|
|
void FunctionSignaturePartialSpecializer::callectUsedCallerArchetypes(
|
|
SubstitutionList ParamSubs) {
|
|
for (auto Sub : ParamSubs) {
|
|
auto Replacement = Sub.getReplacement();
|
|
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, Sub.getConformances()))
|
|
continue;
|
|
|
|
// Add used generic parameters/archetypes.
|
|
Replacement.visit([&](Type Ty) {
|
|
if (auto Archetype = Ty->getAs<ArchetypeType>()) {
|
|
UsedCallerArchetypes.insert(Archetype->getPrimary());
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void FunctionSignaturePartialSpecializer::
|
|
computeCallerInterfaceToSpecializedInterfaceMap(
|
|
GenericSignature *CallerGenericSig) {
|
|
if (!CallerGenericSig)
|
|
return;
|
|
|
|
CallerInterfaceToSpecializedInterfaceMap =
|
|
CallerGenericSig->getSubstitutionMap(
|
|
[&](SubstitutableType *type) -> Type {
|
|
return CallerInterfaceToSpecializedInterfaceMapping.lookup(type);
|
|
},
|
|
LookUpConformanceInSignature(*CallerGenericSig));
|
|
|
|
DEBUG(llvm::dbgs() << "\n\nCallerInterfaceToSpecializedInterfaceMap map:\n";
|
|
CallerInterfaceToSpecializedInterfaceMap.dump(llvm::dbgs()));
|
|
}
|
|
|
|
void FunctionSignaturePartialSpecializer::
|
|
computeCalleeInterfaceToSpecializedInterfaceMap(
|
|
GenericSignature *CalleeGenericSig) {
|
|
CalleeInterfaceToSpecializedInterfaceMap =
|
|
CalleeGenericSig->getSubstitutionMap(
|
|
[&](SubstitutableType *type) -> Type {
|
|
return CalleeInterfaceToSpecializedInterfaceMapping.lookup(type);
|
|
},
|
|
LookUpConformanceInSignature(*CalleeGenericSig));
|
|
|
|
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(
|
|
GenericEnvironment *CallerGenericEnv) {
|
|
for (auto CallerArchetype : UsedCallerArchetypes) {
|
|
auto CallerGenericParam =
|
|
CallerGenericEnv->mapTypeOutOfContext(CallerArchetype);
|
|
assert(CallerGenericParam->is<GenericTypeParamType>());
|
|
|
|
DEBUG(llvm::dbgs() << "\n\nChecking used caller archetype:\n";
|
|
CallerArchetype->dump();
|
|
llvm::dbgs() << "It corresponds to the caller generic parameter:\n";
|
|
CallerGenericParam->dump());
|
|
|
|
// Create an equivalent generic parameter.
|
|
auto SubstGenericParam = createGenericParam();
|
|
auto SubstGenericParamCanTy = SubstGenericParam->getCanonicalType();
|
|
|
|
CallerInterfaceToSpecializedInterfaceMapping
|
|
[CallerGenericParam->getCanonicalType()
|
|
->castTo<GenericTypeParamType>()] = SubstGenericParam;
|
|
|
|
SpecializedInterfaceToCallerArchetypeMapping[SubstGenericParam] =
|
|
CallerArchetype;
|
|
|
|
DEBUG(llvm::dbgs() << "\nCreated a new specialized generic parameter:\n";
|
|
SubstGenericParam->dump();
|
|
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 require a substition.
|
|
void FunctionSignaturePartialSpecializer::
|
|
createGenericParamsForCalleeGenericParams(
|
|
GenericSignature *CalleeGenericSig,
|
|
GenericEnvironment *CallerGenericEnv) {
|
|
auto Source =
|
|
GenericSignatureBuilder::FloatingRequirementSource::forAbstract();
|
|
for (auto GP : CalleeGenericSig->getGenericParams()) {
|
|
auto CanTy = GP->getCanonicalType();
|
|
auto CanTyInContext =
|
|
CalleeGenericSig->getCanonicalTypeInContext(CanTy, *SM);
|
|
auto Replacement = CanTyInContext.subst(CalleeInterfaceToCallerArchetypeMap);
|
|
DEBUG(llvm::dbgs() << "\n\nChecking callee generic parameter:\n";
|
|
CanTy->dump());
|
|
if (!Replacement) {
|
|
DEBUG(llvm::dbgs() << "No replacement found. Skipping.\n");
|
|
continue;
|
|
}
|
|
|
|
DEBUG(llvm::dbgs() << "Replacement found:\n"; Replacement->dump());
|
|
|
|
bool ShouldSpecializeGP = shouldBePartiallySpecialized(
|
|
Replacement,
|
|
{});
|
|
//CalleeInterfaceToCallerArchetypeMap.lookupConformances(CanTy));
|
|
|
|
if (ShouldSpecializeGP) {
|
|
DEBUG(llvm::dbgs() << "Should be partially specialized.\n");
|
|
} else {
|
|
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;
|
|
|
|
DEBUG(llvm::dbgs() << "\nCreated a new specialized generic parameter:\n";
|
|
SubstGenericParam->dump();
|
|
llvm::dbgs() << "Created a mapping "
|
|
"(callee interface -> specialized interface):\n"
|
|
<< CanTy << " -> " << SubstGenericParamCanTy << "\n");
|
|
|
|
// if (!Replacement->hasArchetype()) {
|
|
if (!ShouldSpecializeGP) {
|
|
// Remember the original subsitution from the apply instruction.
|
|
SpecializedInterfaceToCallerArchetypeMapping[SubstGenericParam] =
|
|
Replacement;
|
|
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;
|
|
if (CallerGenericEnv)
|
|
ReplacementCallerInterfaceTy =
|
|
CallerGenericEnv->mapTypeOutOfContext(Replacement);
|
|
|
|
auto SpecializedReplacementCallerInterfaceTy =
|
|
ReplacementCallerInterfaceTy.subst(
|
|
CallerInterfaceToSpecializedInterfaceMap);
|
|
assert(!SpecializedReplacementCallerInterfaceTy->hasError());
|
|
|
|
Requirement Req(RequirementKind::SameType, SubstGenericParamCanTy,
|
|
SpecializedReplacementCallerInterfaceTy);
|
|
Builder.addRequirement(Req, Source);
|
|
|
|
DEBUG(llvm::dbgs() << "Added a requirement:\n"; Req.dump());
|
|
|
|
if (ReplacementCallerInterfaceTy->is<GenericTypeParamType>()) {
|
|
// Remember that the new generic parameter corrsponds
|
|
// to the same caller archetype, which correponds to
|
|
// the ReplacementCallerInterfaceTy.
|
|
SpecializedInterfaceToCallerArchetypeMapping[SubstGenericParam] =
|
|
SpecializedInterfaceToCallerArchetypeMapping.lookup(
|
|
ReplacementCallerInterfaceTy
|
|
.subst(CallerInterfaceToSpecializedInterfaceMap)
|
|
->castTo<SubstitutableType>());
|
|
DEBUG(llvm::dbgs()
|
|
<< "Created a mapping (specialized interface -> "
|
|
"caller archetype):\n"
|
|
<< Type(SubstGenericParam) << " -> "
|
|
<< SpecializedInterfaceToCallerArchetypeMapping[SubstGenericParam]
|
|
->getCanonicalType()
|
|
<< "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add each of the requirements (mapped from the requirement's
|
|
// interface types into the specialized interface type parameters).
|
|
// TODO: Do we need to add requirements of the caller's archetypes, which
|
|
// stem from the caller's generic signature? If so, which ones? All of them?
|
|
// Just some of them? Most likely we need to add only those which are not
|
|
// present in the callee's signature.
|
|
void FunctionSignaturePartialSpecializer::addRequirements(
|
|
GenericSignature *CalleeGenericSig) {
|
|
remapRequirements(CalleeGenericSig, CalleeInterfaceToSpecializedInterfaceMap,
|
|
Builder, SM);
|
|
}
|
|
|
|
std::pair<GenericEnvironment *, GenericSignature *>
|
|
FunctionSignaturePartialSpecializer::
|
|
getSpecializedGenericEnvironmentAndSignature() {
|
|
// Finalize the archetype builder.
|
|
Builder.finalize(SourceLoc(), AllGenericParams,
|
|
/*allowConcreteGenericParams=*/true);
|
|
// Get the specialized generic signature and generic environment.
|
|
if (!AllGenericParams.empty()) {
|
|
// Produce the generic signature and environment.
|
|
return getGenericEnvironmentAndSignature(Builder, M);
|
|
}
|
|
return std::make_pair(nullptr, nullptr);
|
|
}
|
|
|
|
void FunctionSignaturePartialSpecializer::computeClonerParamSubs(
|
|
GenericSignature *CalleeGenericSig, ReabstractionInfo &ReInfo,
|
|
SubstitutionList &ClonerParamSubs) {
|
|
SmallVector<Substitution, 4> List;
|
|
|
|
CalleeGenericSig->getSubstitutions(
|
|
[&](SubstitutableType *type) -> Type {
|
|
DEBUG(llvm::dbgs() << "\ngetSubstitution for ClonerParamSubs:\n"
|
|
<< Type(type) << "\n"
|
|
<< "in generic signature:\n";
|
|
CalleeGenericSig->dump());
|
|
auto SpecializedInterfaceTy =
|
|
Type(type).subst(CalleeInterfaceToSpecializedInterfaceMap);
|
|
return ReInfo.getSpecializedGenericEnvironment()->mapTypeIntoContext(
|
|
SpecializedInterfaceTy);
|
|
},
|
|
LookUpConformanceInSignature(*ReInfo.getSpecializedGenericSignature()),
|
|
List);
|
|
ClonerParamSubs = Ctx.AllocateCopy(List);
|
|
verifySubstitutionList(ClonerParamSubs, "ClonerParamSubs");
|
|
}
|
|
|
|
void FunctionSignaturePartialSpecializer::computeCallerParamSubs(
|
|
GenericSignature *SpecializedGenericSig,
|
|
SubstitutionList &CallerParamSubs) {
|
|
// TODO: Handle the case where calle's generic type parameter should
|
|
// not be partially specialized. In this case,
|
|
// CalleeInterfaceToCallerArchetypeMap would not contian a mapping for it.
|
|
|
|
SmallVector<Substitution, 4> List;
|
|
|
|
// Define a substitution map for re-mapping interface types of
|
|
// the specialized function to contextual types of the caller.
|
|
auto SpecializedInterfaceToCallerArchetypeMap =
|
|
SpecializedGenericSig->getSubstitutionMap(
|
|
[&](SubstitutableType *type) -> Type {
|
|
return SpecializedInterfaceToCallerArchetypeMapping.lookup(type);
|
|
},
|
|
LookUpConformanceInSignature(*SpecializedGenericSig));
|
|
|
|
DEBUG(llvm::dbgs() << "\n\nSpecializedInterfaceToCallerArchetypeMap map:\n";
|
|
SpecializedInterfaceToCallerArchetypeMap.dump(llvm::dbgs()));
|
|
|
|
SpecializedGenericSig->getSubstitutions(
|
|
SpecializedInterfaceToCallerArchetypeMap, List);
|
|
|
|
CallerParamSubs = Ctx.AllocateCopy(List);
|
|
verifySubstitutionList(CallerParamSubs, "CallerParamSubs");
|
|
}
|
|
|
|
void FunctionSignaturePartialSpecializer::computeCallerInterfaceSubs(
|
|
GenericSignature *CalleeGenericSig, ReabstractionInfo &ReInfo,
|
|
SubstitutionMap &CallerInterfaceSubs) {
|
|
CallerInterfaceSubs = CalleeGenericSig->getSubstitutionMap(
|
|
[&](SubstitutableType *type) -> Type {
|
|
// First, map callee's interface type to specialized interface type.
|
|
auto Ty = Type(type).subst(CalleeInterfaceToSpecializedInterfaceMap);
|
|
Type SpecializedInterfaceTy =
|
|
ReInfo.getSpecializedGenericEnvironment()->mapTypeOutOfContext(
|
|
ReInfo.getSpecializedGenericEnvironment()->mapTypeIntoContext(
|
|
Ty));
|
|
assert(!SpecializedInterfaceTy->hasError());
|
|
return SpecializedInterfaceTy;
|
|
},
|
|
LookUpConformanceInSignature(*CalleeGenericSig));
|
|
|
|
DEBUG(llvm::dbgs() << "\n\nCallerInterfaceSubs map:\n";
|
|
CallerInterfaceSubs.dump(llvm::dbgs()));
|
|
}
|
|
|
|
CanSILFunctionType
|
|
FunctionSignaturePartialSpecializer::getSpecializedSubstFunctionType(
|
|
CanSILFunctionType CalleeFnTy, ReabstractionInfo &ReInfo) {
|
|
// TODO: Should we try to minimize the number of generic parameters?
|
|
// We can remove the generic parameter if it is not used
|
|
// anywhere in the function signature.
|
|
// This is a specialized interface type of the callee.
|
|
|
|
auto *SpecializedGenericSig = ReInfo.getSpecializedGenericSignature();
|
|
auto *SpecializedGenericEnv = ReInfo.getSpecializedGenericEnvironment();
|
|
|
|
CanGenericSignature CanSpecializedGenericSig;
|
|
if (SpecializedGenericSig && !SpecializedGenericSig->areAllParamsConcrete())
|
|
CanSpecializedGenericSig = SpecializedGenericSig->getCanonicalSignature();
|
|
|
|
CanSILFunctionType SpecializedSubstFnTy;
|
|
{
|
|
Lowering::GenericContextScope GenericScope(M.Types,
|
|
CanSpecializedGenericSig);
|
|
SpecializedSubstFnTy = CalleeFnTy->substGenericArgs(
|
|
M,
|
|
[&](SubstitutableType *type) -> Type {
|
|
auto SpecializedInterfaceTy =
|
|
Type(type).subst(CalleeInterfaceToSpecializedInterfaceMap);
|
|
|
|
return SpecializedGenericEnv->mapTypeOutOfContext(
|
|
SpecializedGenericEnv->mapTypeIntoContext(
|
|
SpecializedInterfaceTy));
|
|
},
|
|
LookUpConformanceInSignature(*SpecializedGenericSig));
|
|
}
|
|
|
|
return SpecializedSubstFnTy;
|
|
}
|
|
|
|
std::pair<GenericEnvironment *, GenericSignature *>
|
|
FunctionSignaturePartialSpecializer::createSpecializedGenericSignature(
|
|
GenericSignature *CallerGenericSig, GenericEnvironment *CallerGenericEnv,
|
|
GenericSignature *CalleeGenericSig, SubstitutionList ParamSubs) {
|
|
CalleeInterfaceToCallerArchetypeMap =
|
|
CalleeGenericSig->getSubstitutionMap(ParamSubs);
|
|
|
|
// Collect all used caller's archetypes from all the substitutions.
|
|
callectUsedCallerArchetypes(ParamSubs);
|
|
|
|
// Generate a new generic type parameter for each used archetype from
|
|
// the caller.
|
|
createGenericParamsForUsedCallerArchetypes(CallerGenericEnv);
|
|
|
|
// Create a SubstitutionMap for re-mapping caller's interface types
|
|
// to interface types of the specialized function.
|
|
computeCallerInterfaceToSpecializedInterfaceMap(CallerGenericSig);
|
|
|
|
// 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(CalleeGenericSig, CallerGenericEnv);
|
|
|
|
computeCalleeInterfaceToSpecializedInterfaceMap(CalleeGenericSig);
|
|
|
|
// Add requirements from the callee signature.
|
|
addRequirements(CalleeGenericSig);
|
|
|
|
return getSpecializedGenericEnvironmentAndSignature();
|
|
}
|
|
|
|
// Builds a new generic and function signatures for a partial specialization.
|
|
// Allows for partial specializations even if substitututions 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.
|
|
|
|
// Current issues:
|
|
// - If Sig2 = GenericSignatureBuilder(Sig2 + Req), then
|
|
// GenericSignatureBuilder(Sig2) != Sig2
|
|
// - The set of requirements is not really minimized.
|
|
// - Some requirements are lost, when you add a same type parameter to the
|
|
// builder.
|
|
void ReabstractionInfo::specializeConcreteAndGenericSubstitutions(
|
|
ApplySite Apply, SILFunction *Callee, ArrayRef<Substitution> ParamSubs) {
|
|
SILModule &M = Callee->getModule();
|
|
auto &Ctx = M.getASTContext();
|
|
|
|
// Caller is the SILFunction containing the apply instruction.
|
|
auto CallerGenericSig =
|
|
Apply.getFunction()->getLoweredFunctionType()->getGenericSignature();
|
|
auto CallerGenericEnv = Apply.getFunction()->getGenericEnvironment();
|
|
|
|
// Callee is the generic function being called by the apply instruction.
|
|
auto CalleeFnTy = Callee->getLoweredFunctionType();
|
|
auto CalleeGenericSig = CalleeFnTy->getGenericSignature();
|
|
|
|
// Used naming convention:
|
|
// Caller - the function containing the provided apply instruction.
|
|
// Callee - the callee of the provided apply instruction.
|
|
// Specialized - the specialized callee which is being created.
|
|
|
|
DEBUG(llvm::dbgs() << "\n\nTrying partial specialization for: "
|
|
<< Callee->getName() << "\n";
|
|
llvm::dbgs() << "Callee generic signature is:\n";
|
|
CalleeGenericSig->dump());
|
|
|
|
FunctionSignaturePartialSpecializer FSPS(M);
|
|
|
|
// Get the partially specialized generic signature and generic environment.
|
|
auto GenPair = FSPS.createSpecializedGenericSignature(
|
|
CallerGenericSig, CallerGenericEnv, CalleeGenericSig, ParamSubs);
|
|
|
|
if (GenPair.second) {
|
|
SpecializedGenericSig = GenPair.second->getCanonicalSignature();
|
|
SpecializedGenericEnv = GenPair.first;
|
|
DEBUG(llvm::dbgs() << "\nCreated SpecializedGenericSig:\n";
|
|
SpecializedGenericSig->dump());
|
|
}
|
|
|
|
// Create substitution lists for the caller and cloner.
|
|
FSPS.computeClonerParamSubs(CalleeGenericSig, *this, ClonerParamSubs);
|
|
FSPS.computeCallerParamSubs(SpecializedGenericSig, CallerParamSubs);
|
|
// Create a substitution map for the caller interface substitutions.
|
|
FSPS.computeCallerInterfaceSubs(CalleeGenericSig, *this, CallerInterfaceSubs);
|
|
|
|
// Create a function type for the new specialized function signature using
|
|
// the new generic signature.
|
|
auto SpecializedSubstFnTy =
|
|
FSPS.getSpecializedSubstFunctionType(CalleeFnTy, *this);
|
|
|
|
if (!SpecializedGenericSig || SpecializedGenericSig->areAllParamsConcrete()) {
|
|
SpecializedGenericSig = nullptr;
|
|
SpecializedGenericEnv = nullptr;
|
|
}
|
|
assert(SpecializedGenericSig || !SpecializedSubstFnTy->hasTypeParameter());
|
|
|
|
SubstitutedType = SILFunctionType::get(
|
|
SpecializedGenericSig, SpecializedSubstFnTy->getExtInfo(),
|
|
SpecializedSubstFnTy->getCalleeConvention(),
|
|
SpecializedSubstFnTy->getParameters(), SpecializedSubstFnTy->getResults(),
|
|
SpecializedSubstFnTy->getOptionalErrorResult(), Ctx);
|
|
|
|
assert(!SubstitutedType->hasArchetype() &&
|
|
"Function type should not contain archetypes");
|
|
|
|
HasUnboundGenericParams =
|
|
SpecializedGenericSig && !SpecializedGenericSig->areAllParamsConcrete();
|
|
createSubstitutedAndSpecializedTypes();
|
|
|
|
if (getSubstitutedType() != Callee->getLoweredFunctionType()) {
|
|
if (getSubstitutedType()->isPolymorphic())
|
|
DEBUG(llvm::dbgs() << "Created new specialized type: " << SpecializedType
|
|
<< "\n");
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// GenericFuncSpecializer
|
|
// =============================================================================
|
|
|
|
GenericFuncSpecializer::GenericFuncSpecializer(SILFunction *GenericFunc,
|
|
SubstitutionList ParamSubs,
|
|
IsSerialized_t Serialized,
|
|
const ReabstractionInfo &ReInfo)
|
|
: M(GenericFunc->getModule()),
|
|
GenericFunc(GenericFunc),
|
|
ParamSubs(ParamSubs),
|
|
Serialized(Serialized),
|
|
ReInfo(ReInfo) {
|
|
|
|
assert(GenericFunc->isDefinition() && "Expected definition to specialize!");
|
|
auto FnTy = ReInfo.getSpecializedType();
|
|
|
|
if (ReInfo.isPartialSpecialization()) {
|
|
Mangle::PartialSpecializationMangler Mangler(
|
|
GenericFunc, FnTy, Serialized, /*isReAbstracted*/ true);
|
|
ClonedName = Mangler.mangle();
|
|
} else {
|
|
Mangle::GenericSpecializationMangler Mangler(
|
|
GenericFunc, ParamSubs, Serialized, /*isReAbstracted*/ true);
|
|
ClonedName = Mangler.mangle();
|
|
}
|
|
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);
|
|
|
|
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() {
|
|
// 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"; });
|
|
|
|
ReInfo.verify();
|
|
|
|
// Create a new function.
|
|
SILFunction *SpecializedF = GenericCloner::cloneFunction(
|
|
GenericFunc, Serialized, ReInfo,
|
|
// Use these substitutions inside the new specialized function being
|
|
// created.
|
|
ReInfo.getClonerParamSubstitutions(),
|
|
ClonedName);
|
|
assert((SpecializedF->getLoweredFunctionType()->isPolymorphic() &&
|
|
SpecializedF->getGenericEnvironment()) ||
|
|
(!SpecializedF->getLoweredFunctionType()->isPolymorphic() &&
|
|
!SpecializedF->getGenericEnvironment()));
|
|
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);
|
|
}
|
|
|
|
// Prepare call arguments. Perform re-abstraction if required.
|
|
static void prepareCallArguments(ApplySite AI, SILBuilder &Builder,
|
|
const ReabstractionInfo &ReInfo,
|
|
SmallVectorImpl<SILValue> &Arguments,
|
|
SILValue &StoreResultTo) {
|
|
/// SIL function conventions for the original apply site with substitutions.
|
|
SILLocation Loc = AI.getLoc();
|
|
auto substConv = AI.getSubstCalleeConv();
|
|
unsigned ArgIdx = AI.getCalleeArgIndexOfFirstAppliedArg();
|
|
for (auto &Op : AI.getArgumentOperands()) {
|
|
auto handleConversion = [&]() {
|
|
// 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)) {
|
|
// The result is converted from indirect to direct. We need to insert
|
|
// a store later.
|
|
assert(!StoreResultTo);
|
|
StoreResultTo = Op.get();
|
|
return true;
|
|
}
|
|
} else {
|
|
// Handle arguments for formal parameters.
|
|
unsigned paramIdx = ArgIdx - substConv.getSILArgIndexOfFirstParam();
|
|
if (ReInfo.isParamConverted(paramIdx)) {
|
|
// An argument is converted from indirect to direct. Instead of the
|
|
// address we pass the loaded value.
|
|
SILValue Val = Builder.createLoad(
|
|
Loc, Op.get(), LoadOwnershipQualifier::Unqualified);
|
|
Arguments.push_back(Val);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
if (!handleConversion())
|
|
Arguments.push_back(Op.get());
|
|
|
|
++ArgIdx;
|
|
}
|
|
}
|
|
|
|
/// Return a substituted callee function type.
|
|
static CanSILFunctionType
|
|
getCalleeSubstFunctionType(SILValue Callee, SubstitutionList Subs) {
|
|
// Create a substituted callee type.
|
|
auto CanFnTy = Callee->getType().castTo<SILFunctionType>();
|
|
auto CalleeSubstFnTy = CanFnTy;
|
|
|
|
if (CanFnTy->isPolymorphic() && !Subs.empty()) {
|
|
CalleeSubstFnTy = CanFnTy->substGenericArgs(*Callee->getModule(), Subs);
|
|
assert(!CalleeSubstFnTy->isPolymorphic() &&
|
|
"Substituted callee type should not be polymorphic");
|
|
assert(!CalleeSubstFnTy->hasTypeParameter() &&
|
|
"Substituted callee type should not have type parameters");
|
|
}
|
|
|
|
return CalleeSubstFnTy;
|
|
}
|
|
|
|
// 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;
|
|
|
|
prepareCallArguments(AI, Builder, ReInfo, Arguments, StoreResultTo);
|
|
|
|
// Create a substituted callee type.
|
|
ArrayRef<Substitution> Subs;
|
|
if (ReInfo.getSpecializedType()->isPolymorphic()) {
|
|
Subs = ReInfo.getCallerParamSubstitutions();
|
|
if (auto FRI = dyn_cast<FunctionRefInst>(Callee)) {
|
|
assert(Subs.size() ==
|
|
FRI->getReferencedFunction()
|
|
->getLoweredFunctionType()
|
|
->getGenericSignature()
|
|
->getSubstitutionListSize());
|
|
}
|
|
}
|
|
|
|
auto CalleeSubstFnTy = getCalleeSubstFunctionType(Callee, Subs);
|
|
auto CalleeSILSubstFnTy = SILType::getPrimitiveObjectType(CalleeSubstFnTy);
|
|
SILFunctionConventions substConv(CalleeSubstFnTy, Builder.getModule());
|
|
|
|
if (auto *TAI = dyn_cast<TryApplyInst>(AI)) {
|
|
SILBasicBlock *ResultBB = TAI->getNormalBB();
|
|
assert(ResultBB->getSinglePredecessorBlock() == TAI->getParent());
|
|
auto *NewTAI =
|
|
Builder.createTryApply(Loc, Callee, CalleeSILSubstFnTy, Subs, Arguments,
|
|
ResultBB, TAI->getErrorBB());
|
|
if (StoreResultTo) {
|
|
assert(substConv.useLoweredAddresses());
|
|
// 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->replacePHIArgument(
|
|
0, StoreResultTo->getType().getObjectType(),
|
|
ValueOwnershipKind::Owned);
|
|
// 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, CalleeSILSubstFnTy,
|
|
substConv.getSILResultType(), Subs,
|
|
Arguments, A->isNonThrowing());
|
|
if (StoreResultTo) {
|
|
assert(substConv.useLoweredAddresses());
|
|
// 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());
|
|
SILType PTy = CalleeSILSubstFnTy;
|
|
auto *NewPAI =
|
|
Builder.createPartialApply(Loc, Callee, PTy, Subs, 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);
|
|
}
|
|
|
|
namespace {
|
|
class ReabstractionThunkGenerator {
|
|
SILFunction *OrigF;
|
|
SILModule &M;
|
|
SILFunction *SpecializedFunc;
|
|
const ReabstractionInfo &ReInfo;
|
|
PartialApplyInst *OrigPAI;
|
|
|
|
IsSerialized_t Serialized = IsNotSerialized;
|
|
std::string ThunkName;
|
|
RegularLocation Loc;
|
|
SmallVector<SILValue, 4> Arguments;
|
|
|
|
public:
|
|
ReabstractionThunkGenerator(const ReabstractionInfo &ReInfo,
|
|
PartialApplyInst *OrigPAI,
|
|
SILFunction *SpecializedFunc)
|
|
: OrigF(OrigPAI->getCalleeFunction()), M(OrigF->getModule()),
|
|
SpecializedFunc(SpecializedFunc), ReInfo(ReInfo), OrigPAI(OrigPAI),
|
|
Loc(RegularLocation::getAutoGeneratedLocation()) {
|
|
if (OrigF->isSerialized() && OrigPAI->getFunction()->isSerialized())
|
|
Serialized = IsSerializable;
|
|
|
|
{
|
|
if (!ReInfo.isPartialSpecialization()) {
|
|
Mangle::GenericSpecializationMangler Mangler(
|
|
OrigF, ReInfo.getOriginalParamSubstitutions(), Serialized,
|
|
/*isReAbstracted*/ false);
|
|
|
|
ThunkName = Mangler.mangle();
|
|
} else {
|
|
Mangle::PartialSpecializationMangler Mangler(
|
|
OrigF, ReInfo.getSpecializedType(), Serialized,
|
|
/*isReAbstracted*/ false);
|
|
|
|
ThunkName = Mangler.mangle();
|
|
}
|
|
}
|
|
}
|
|
|
|
SILFunction *createThunk();
|
|
|
|
protected:
|
|
SILValue createReabstractionThunkApply(SILBuilder &Builder);
|
|
SILArgument *convertReabstractionThunkArguments(SILBuilder &Builder);
|
|
};
|
|
} // anonymous namespace
|
|
|
|
SILFunction *ReabstractionThunkGenerator::createThunk() {
|
|
SILFunction *Thunk =
|
|
M.getOrCreateSharedFunction(Loc, ThunkName, ReInfo.getSubstitutedType(),
|
|
IsBare, IsTransparent, Serialized, IsThunk);
|
|
// 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->hasUnqualifiedOwnership()) {
|
|
Thunk->setUnqualifiedOwnership();
|
|
}
|
|
|
|
if (!SILModuleConventions(M).useLoweredAddresses()) {
|
|
for (auto SpecArg : SpecializedFunc->getArguments()) {
|
|
SILArgument *NewArg = EntryBB->createFunctionArgument(SpecArg->getType(),
|
|
SpecArg->getDecl());
|
|
Arguments.push_back(NewArg);
|
|
}
|
|
SILValue ReturnValue = createReabstractionThunkApply(Builder);
|
|
Builder.createReturn(Loc, ReturnValue);
|
|
return Thunk;
|
|
}
|
|
// Handle lowered addresses.
|
|
SILArgument *ReturnValueAddr = convertReabstractionThunkArguments(Builder);
|
|
|
|
SILValue ReturnValue = createReabstractionThunkApply(Builder);
|
|
|
|
if (ReturnValueAddr) {
|
|
// Need to store the direct results to the original indirect address.
|
|
Builder.createStore(Loc, ReturnValue, ReturnValueAddr,
|
|
StoreOwnershipQualifier::Unqualified);
|
|
SILType VoidTy =
|
|
OrigPAI->getSubstCalleeType()->getDirectFormalResultsType();
|
|
assert(VoidTy.isVoid());
|
|
ReturnValue = Builder.createTuple(Loc, VoidTy, {});
|
|
}
|
|
Builder.createReturn(Loc, ReturnValue);
|
|
return Thunk;
|
|
}
|
|
|
|
// Create a call to a reabstraction thunk. Return the call's direct result.
|
|
SILValue ReabstractionThunkGenerator::createReabstractionThunkApply(
|
|
SILBuilder &Builder) {
|
|
SILFunction *Thunk = &Builder.getFunction();
|
|
auto *FRI = Builder.createFunctionRef(Loc, SpecializedFunc);
|
|
auto Subs = Thunk->getForwardingSubstitutions();
|
|
auto CalleeSubstFnTy = getCalleeSubstFunctionType(FRI, Subs);
|
|
auto CalleeSILSubstFnTy = SILType::getPrimitiveObjectType(CalleeSubstFnTy);
|
|
auto specConv = SpecializedFunc->getConventions();
|
|
if (!SpecializedFunc->getLoweredFunctionType()->hasErrorResult()) {
|
|
return Builder.createApply(Loc, FRI, CalleeSILSubstFnTy,
|
|
specConv.getSILResultType(), Subs, Arguments,
|
|
false);
|
|
}
|
|
// Create the logic for calling a throwing function.
|
|
SILBasicBlock *NormalBB = Thunk->createBasicBlock();
|
|
SILBasicBlock *ErrorBB = Thunk->createBasicBlock();
|
|
Builder.createTryApply(Loc, FRI, CalleeSILSubstFnTy, Subs,
|
|
Arguments, NormalBB, ErrorBB);
|
|
auto *ErrorVal = ErrorBB->createPHIArgument(specConv.getSILErrorType(),
|
|
ValueOwnershipKind::Owned);
|
|
Builder.setInsertionPoint(ErrorBB);
|
|
Builder.createThrow(Loc, ErrorVal);
|
|
SILValue ReturnValue = NormalBB->createPHIArgument(
|
|
specConv.getSILResultType(), ValueOwnershipKind::Owned);
|
|
Builder.setInsertionPoint(NormalBB);
|
|
return ReturnValue;
|
|
}
|
|
|
|
// 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) {
|
|
SILFunction *Thunk = &Builder.getFunction();
|
|
CanSILFunctionType SpecType = SpecializedFunc->getLoweredFunctionType();
|
|
CanSILFunctionType SubstType = ReInfo.getSubstitutedType();
|
|
auto specConv = SpecializedFunc->getConventions();
|
|
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 = substConv.getSILType(substRI);
|
|
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 =
|
|
substConv.getSILType(SubstType->getParameters()[paramIdx]);
|
|
assert(ParamTy.isAddress());
|
|
SILArgument *SpecArg = *SpecArgIter++;
|
|
SILArgument *NewArg =
|
|
EntryBB->createFunctionArgument(ParamTy, SpecArg->getDecl());
|
|
auto *ArgVal =
|
|
Builder.createLoad(Loc, NewArg, LoadOwnershipQualifier::Unqualified);
|
|
Arguments.push_back(ArgVal);
|
|
continue;
|
|
}
|
|
// Simply clone unconverted direct or indirect parameters.
|
|
cloneSpecializedArgument();
|
|
}
|
|
assert(SpecArgIter == SpecializedFunc->getArguments().end());
|
|
return ReturnValueAddr;
|
|
}
|
|
|
|
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() << "\n\n*** 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->isSerialized() && !RefF->hasValidLinkageForFragileInline())
|
|
return;
|
|
|
|
if (shouldNotSpecializeCallee(RefF))
|
|
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;
|
|
|
|
ReabstractionInfo ReInfo(Apply, RefF, Apply.getSubstitutions());
|
|
if (!ReInfo.canBeSpecialized())
|
|
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(),
|
|
Serialized, 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;
|
|
DEBUG(llvm::dbgs() << "Created specialized function: "
|
|
<< SpecializedF->getName() << "\n"
|
|
<< "Specialized function type: "
|
|
<< SpecializedF->getLoweredFunctionType() << "\n");
|
|
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((Serialized == SpecializedF->isSerialized() ||
|
|
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 =
|
|
ReabstractionThunkGenerator(ReInfo, PAI, SpecializedF).createThunk();
|
|
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 Subs = ReInfo.getCallerParamSubstitutions();
|
|
auto CalleeSubstFnTy = getCalleeSubstFunctionType(FRI, Subs);
|
|
auto CalleeSILSubstFnTy = SILType::getPrimitiveObjectType(CalleeSubstFnTy);
|
|
auto *NewPAI = Builder.createPartialApply(PAI->getLoc(), FRI,
|
|
CalleeSILSubstFnTy, Subs,
|
|
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",
|
|
"ReversedCollection",
|
|
"MutableCollection",
|
|
"BidirectionalCollection",
|
|
"RandomAccessCollection",
|
|
"ReversedRandomAccessCollection",
|
|
"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.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");
|
|
|
|
DEBUG(llvm::dbgs() << "Found existing specialization for: " << FunctionName
|
|
<< '\n';
|
|
llvm::dbgs() << swift::Demangle::demangleSymbolAsString(
|
|
Specialization->getName())
|
|
<< "\n\n");
|
|
|
|
return Specialization;
|
|
}
|
|
|