mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
The existing simple mechanism for avoiding infinite generic specialization loops is based on checking the structural depth and width of types passed as generic type parameters. If the depth or the width of a type is above a certain threshold, the type is considered too complex for generic specialization and no specialization is produced. While this approach prevents the possibility of producing an infinite number of generic specializations for ever-growing generic type parameters, it catches the issue too late in some cases, leading to excessive CPU and memory usage. Therefore, the new method tries to solve the problem at its root. An infinite generic specialization loop can be triggered by specializing a given generic call-site if and only if: - Doing so would result in a loop inside the specialization graph represented by the `GenericSpecializationInformations`, i.e. it would produce direct or indirect recursion involving a generic call - The substitutions used by the current generic call-site are structurally more complex than the substitutions used by the same call-site in the previous iteration inside specialization graph. More complex in this context means that the new generic type parameter structurally contains the generic type parameter from a previous iteration inside the specialization graph and has greater structural depth, e.g. `Array<Int>` is more complex than `Int`. The generic specializer now records all the required information about specializations it produces and uses it later to detect and prevent any generic specializations which would result in an infinite specialization loop. It detects them as early as possible and thus reduces compile times, memory consumption and potentially also reduces the code-size by not generating useless specializations.
317 lines
11 KiB
C++
317 lines
11 KiB
C++
//===--- Generics.h - Utilities for transforming generics -------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This contains utilities for transforming generics.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SIL_GENERICS_H
|
|
#define SWIFT_SIL_GENERICS_H
|
|
|
|
#include "swift/AST/SubstitutionMap.h"
|
|
#include "swift/SIL/SILFunction.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SILOptimizer/Utils/Local.h"
|
|
#include "llvm/ADT/SmallBitVector.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
namespace swift {
|
|
|
|
class FunctionSignaturePartialSpecializer;
|
|
|
|
/// Tries to specialize an \p Apply of a generic function. It can be a full
|
|
/// apply site or a partial apply.
|
|
/// Replaced and now dead instructions are returned in \p DeadApplies.
|
|
/// New created functions, like the specialized callee and thunks, are returned
|
|
/// in \p NewFunctions.
|
|
///
|
|
/// This is the top-level entry point for specializing an existing call site.
|
|
void trySpecializeApplyOfGeneric(
|
|
ApplySite Apply, DeadInstructionSet &DeadApplies,
|
|
llvm::SmallVectorImpl<SILFunction *> &NewFunctions);
|
|
|
|
/// Helper class to describe re-abstraction of function parameters done during
|
|
/// specialization.
|
|
///
|
|
/// Specifically, it contains information which formal parameters and returns
|
|
/// are changed from indirect values to direct values.
|
|
class ReabstractionInfo {
|
|
/// A 1-bit means that this parameter/return value is converted from indirect
|
|
/// to direct.
|
|
llvm::SmallBitVector Conversions;
|
|
|
|
/// If set, indirect to direct conversions should be performed by the generic
|
|
/// specializer.
|
|
bool ConvertIndirectToDirect;
|
|
|
|
/// The first NumResults bits in Conversions refer to formal indirect
|
|
/// out-parameters.
|
|
unsigned NumFormalIndirectResults;
|
|
|
|
/// The function type after applying the substitutions used to call the
|
|
/// specialized function.
|
|
CanSILFunctionType SubstitutedType;
|
|
|
|
/// The function type after applying the re-abstractions on the
|
|
/// SubstitutedType.
|
|
CanSILFunctionType SpecializedType;
|
|
|
|
/// The generic environment to be used by the specialization.
|
|
GenericEnvironment *SpecializedGenericEnv;
|
|
|
|
/// The generic signature of the specialization.
|
|
/// It is nullptr if the specialization is not polymorphic.
|
|
GenericSignature *SpecializedGenericSig;
|
|
|
|
// Set of substitutions from callee's invocation before
|
|
// any transformations performed by the generic specializer.
|
|
//
|
|
// Maps callee's generic parameters to caller's archetypes.
|
|
SubstitutionList CalleeParamSubs;
|
|
|
|
// Set of substitutions to be used to invoke a specialized function.
|
|
//
|
|
// Maps generic parameters of the specialized callee function to caller's
|
|
// archetypes.
|
|
SubstitutionList CallerParamSubs;
|
|
|
|
// Replaces archetypes of the original callee with archetypes
|
|
// or concrete types, if they were made concrete) of the specialized
|
|
// callee.
|
|
//
|
|
// Maps original callee's generic parameters to specialized
|
|
// callee archetypes.
|
|
SubstitutionList ClonerParamSubs;
|
|
|
|
// Reference to the original generic non-specialized callee function.
|
|
SILFunction *Callee;
|
|
|
|
// The apply site which invokes the generic function.
|
|
ApplySite Apply;
|
|
|
|
// Set if a specialized function has unbound generic parameters.
|
|
bool HasUnboundGenericParams;
|
|
|
|
// Substitutions to be used for creating a new function type
|
|
// for the specialized function.
|
|
//
|
|
// Maps original callee's generic parameters to specialized callee's
|
|
// generic parameters.
|
|
// It uses interface types.
|
|
SubstitutionMap CallerInterfaceSubs;
|
|
|
|
// Create a new substituted type with the updated signature.
|
|
CanSILFunctionType createSubstitutedType(SILFunction *OrigF,
|
|
const SubstitutionMap &SubstMap,
|
|
bool HasUnboundGenericParams);
|
|
|
|
void createSubstitutedAndSpecializedTypes();
|
|
bool prepareAndCheck(ApplySite Apply, SILFunction *Callee,
|
|
SubstitutionList ParamSubs);
|
|
void performFullSpecializationPreparation(SILFunction *Callee,
|
|
SubstitutionList ParamSubs);
|
|
void performPartialSpecializationPreparation(SILFunction *Caller,
|
|
SILFunction *Callee,
|
|
SubstitutionList ParamSubs);
|
|
void finishPartialSpecializationPreparation(
|
|
FunctionSignaturePartialSpecializer &FSPS);
|
|
|
|
ReabstractionInfo() {}
|
|
public:
|
|
/// Constructs the ReabstractionInfo for generic function \p Callee with
|
|
/// substitutions \p ParamSubs.
|
|
/// If specialization is not possible getSpecializedType() will return an
|
|
/// invalid type.
|
|
ReabstractionInfo(ApplySite Apply, SILFunction *Callee,
|
|
SubstitutionList ParamSubs,
|
|
bool ConvertIndirectToDirect = true);
|
|
|
|
/// Constructs the ReabstractionInfo for generic function \p Callee with
|
|
/// additional requirements. Requirements may contain new layout,
|
|
/// conformances or same concrete type requirements.
|
|
ReabstractionInfo(SILFunction *Callee, ArrayRef<Requirement> Requirements);
|
|
|
|
/// Returns true if the \p ParamIdx'th (non-result) formal parameter is
|
|
/// converted from indirect to direct.
|
|
bool isParamConverted(unsigned ParamIdx) const {
|
|
return ConvertIndirectToDirect &&
|
|
Conversions.test(ParamIdx + NumFormalIndirectResults);
|
|
}
|
|
|
|
/// Returns true if the \p ResultIdx'th formal result is converted from
|
|
/// indirect to direct.
|
|
bool isFormalResultConverted(unsigned ResultIdx) const {
|
|
assert(ResultIdx < NumFormalIndirectResults);
|
|
return ConvertIndirectToDirect && Conversions.test(ResultIdx);
|
|
}
|
|
|
|
/// Gets the total number of original function arguments.
|
|
unsigned getNumArguments() const { return Conversions.size(); }
|
|
|
|
/// Returns true if the \p ArgIdx'th argument is converted from an
|
|
/// indirect
|
|
/// result or parameter to a direct result or parameter.
|
|
bool isArgConverted(unsigned ArgIdx) const {
|
|
return Conversions.test(ArgIdx);
|
|
}
|
|
|
|
/// Returns true if there are any conversions from indirect to direct values.
|
|
bool hasConversions() const { return Conversions.any(); }
|
|
|
|
/// Remove the arguments of a partial apply, leaving the arguments for the
|
|
/// partial apply result function.
|
|
void prunePartialApplyArgs(unsigned numPartialApplyArgs) {
|
|
assert(numPartialApplyArgs <= SubstitutedType->getNumParameters());
|
|
assert(numPartialApplyArgs <= Conversions.size());
|
|
Conversions.resize(Conversions.size() - numPartialApplyArgs);
|
|
}
|
|
|
|
/// Returns the index of the first argument of an apply site, which may be
|
|
/// > 0 in case of a partial_apply.
|
|
unsigned getIndexOfFirstArg(ApplySite Apply) const {
|
|
unsigned numArgs = Apply.getNumArguments();
|
|
assert(numArgs == Conversions.size() ||
|
|
(numArgs < Conversions.size() && isa<PartialApplyInst>(Apply)));
|
|
return Conversions.size() - numArgs;
|
|
}
|
|
|
|
/// Get the function type after applying the substitutions to the original
|
|
/// generic function.
|
|
CanSILFunctionType getSubstitutedType() const { return SubstitutedType; }
|
|
|
|
/// Get the function type after applying the re-abstractions on the
|
|
/// substituted type. Returns an invalid type if specialization is not
|
|
/// possible.
|
|
CanSILFunctionType getSpecializedType() const { return SpecializedType; }
|
|
|
|
GenericEnvironment *getSpecializedGenericEnvironment() const {
|
|
return SpecializedGenericEnv;
|
|
}
|
|
|
|
GenericSignature *getSpecializedGenericSignature() const {
|
|
return SpecializedGenericSig;
|
|
}
|
|
|
|
SubstitutionList getCallerParamSubstitutions() const {
|
|
return CallerParamSubs;
|
|
}
|
|
|
|
SubstitutionList getClonerParamSubstitutions() const {
|
|
return ClonerParamSubs;
|
|
}
|
|
|
|
SubstitutionList getCalleeParamSubstitutions() const {
|
|
return CalleeParamSubs;
|
|
}
|
|
|
|
/// Create a specialized function type for a specific substituted type \p
|
|
/// SubstFTy by applying the re-abstractions.
|
|
CanSILFunctionType createSpecializedType(CanSILFunctionType SubstFTy,
|
|
SILModule &M) const;
|
|
|
|
SILFunction *getNonSpecializedFunction() const { return Callee; }
|
|
|
|
/// Map type into a context of the specialized function.
|
|
Type mapTypeIntoContext(Type type) const;
|
|
|
|
/// Map SIL type into a context of the specialized function.
|
|
SILType mapTypeIntoContext(SILType type) const;
|
|
|
|
SILModule &getModule() const { return Callee->getModule(); }
|
|
|
|
/// Returns true if generic specialization is possible.
|
|
bool canBeSpecialized() const;
|
|
|
|
/// Returns true if it is a full generic specialization.
|
|
bool isFullSpecialization() const;
|
|
|
|
/// Returns true if it is a partial generic specialization.
|
|
bool isPartialSpecialization() const;
|
|
|
|
/// Returns true if a given apply can be specialized.
|
|
static bool canBeSpecialized(ApplySite Apply, SILFunction *Callee,
|
|
SubstitutionList ParamSubs);
|
|
|
|
/// Returns the apply site for the current generic specialization.
|
|
ApplySite getApply() const {
|
|
return Apply;
|
|
}
|
|
|
|
void verify() const;
|
|
};
|
|
|
|
/// Helper class for specializing a generic function given a list of
|
|
/// substitutions.
|
|
class GenericFuncSpecializer {
|
|
SILModule &M;
|
|
SILFunction *GenericFunc;
|
|
SubstitutionList ParamSubs;
|
|
IsSerialized_t Serialized;
|
|
const ReabstractionInfo &ReInfo;
|
|
|
|
SubstitutionMap ContextSubs;
|
|
std::string ClonedName;
|
|
|
|
public:
|
|
GenericFuncSpecializer(SILFunction *GenericFunc,
|
|
SubstitutionList ParamSubs,
|
|
IsSerialized_t Serialized,
|
|
const ReabstractionInfo &ReInfo);
|
|
|
|
/// If we already have this specialization, reuse it.
|
|
SILFunction *lookupSpecialization();
|
|
|
|
/// Return a newly created specialized function.
|
|
SILFunction *tryCreateSpecialization();
|
|
|
|
/// Try to specialize GenericFunc given a list of ParamSubs.
|
|
/// Returns either a new or existing specialized function, or nullptr.
|
|
SILFunction *trySpecialization() {
|
|
if (!ReInfo.getSpecializedType())
|
|
return nullptr;
|
|
|
|
SILFunction *SpecializedF = lookupSpecialization();
|
|
if (!SpecializedF)
|
|
SpecializedF = tryCreateSpecialization();
|
|
|
|
return SpecializedF;
|
|
}
|
|
|
|
StringRef getClonedName() {
|
|
return ClonedName;
|
|
}
|
|
};
|
|
|
|
// =============================================================================
|
|
// Prespecialized symbol lookup.
|
|
// =============================================================================
|
|
|
|
/// Checks if a given mangled name could be a name of a whitelisted
|
|
/// specialization.
|
|
bool isWhitelistedSpecialization(StringRef SpecName);
|
|
|
|
/// Create a new apply based on an old one, but with a different
|
|
/// function being applied.
|
|
ApplySite replaceWithSpecializedFunction(ApplySite AI, SILFunction *NewF,
|
|
const ReabstractionInfo &ReInfo);
|
|
|
|
/// Returns a SILFunction for the symbol specified by FunctioName if it is
|
|
/// visible to the current SILModule. This is used to link call sites to
|
|
/// externally defined specialization and should only be used when the function
|
|
/// body is not required for further optimization or inlining (-Onone).
|
|
SILFunction *lookupPrespecializedSymbol(SILModule &M, StringRef FunctionName);
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|