Teach EagerSpecializer how to use the new form of the @_specialize attribute

In addition to supporting the creation of full specializations, the EagerSpecializer changes contain some code for generating the layout-constrained partial specializations as well.
This commit is contained in:
Roman Levenstein
2017-01-13 14:47:23 -08:00
parent 2b9444e03a
commit ec4f28eff2
3 changed files with 887 additions and 184 deletions

View File

@@ -27,6 +27,8 @@
/// will be a tradeoff between utility of the attribute vs. cost of the check.
#define DEBUG_TYPE "eager-specializer"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Type.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/Generics.h"
@@ -129,8 +131,9 @@ static void addReturnValueImpl(SILBasicBlock *RetBB, SILBasicBlock *NewRetBB,
}
/// Adds a CFG edge from the unterminated NewRetBB to a merged "return" block.
static void addReturnValue(SILBasicBlock *NewRetBB, SILValue NewRetVal) {
auto *RetBB = &*NewRetBB->getParent()->findReturnBB();
static void addReturnValue(SILBasicBlock *NewRetBB, SILBasicBlock *OldRetBB,
SILValue NewRetVal) {
auto *RetBB = OldRetBB;
addReturnValueImpl(RetBB, NewRetBB, NewRetVal);
}
@@ -150,6 +153,7 @@ emitApplyWithRethrow(SILBuilder &Builder,
SILLocation Loc,
SILValue FuncRef,
CanSILFunctionType CanSILFuncTy,
SubstitutionList Subs,
ArrayRef<SILValue> CallArgs,
void (*EmitCleanup)(SILBuilder&, SILLocation)) {
@@ -158,13 +162,15 @@ emitApplyWithRethrow(SILBuilder &Builder,
SILBasicBlock *ErrorBB = F.createBasicBlock();
SILBasicBlock *NormalBB = F.createBasicBlock();
Builder.createTryApply(Loc,
FuncRef,
SILType::getPrimitiveObjectType(CanSILFuncTy),
SubstitutionList(),
Subs,
CallArgs,
NormalBB,
ErrorBB);
{
// Emit the rethrow logic.
Builder.emitBlock(ErrorBB);
@@ -188,27 +194,69 @@ emitApplyWithRethrow(SILBuilder &Builder,
ValueOwnershipKind::Owned);
}
/// Emits code to invoke the specified nonpolymorphic CalleeFunc using the
/// Emits code to invoke the specified specialized CalleeFunc using the
/// provided SILBuilder.
///
///
/// TODO: Move this to Utils.
static SILValue
emitInvocation(SILBuilder &Builder, SILLocation Loc,
emitInvocation(SILBuilder &Builder,
const ReabstractionInfo &ReInfo,
SILLocation Loc,
SILFunction *CalleeFunc,
ArrayRef<SILValue> CallArgs,
void (*EmitCleanup)(SILBuilder&, SILLocation)) {
auto *FuncRefInst = Builder.createFunctionRef(Loc, CalleeFunc);
auto CanSILFuncTy = CalleeFunc->getLoweredFunctionType();
assert(!CanSILFuncTy->isPolymorphic());
auto CalleeSubstFnTy = CanSILFuncTy;
SubstitutionList Subs;
if (CanSILFuncTy->isPolymorphic()) {
// Create a substituted callee type.
assert(CanSILFuncTy == ReInfo.getSpecializedType() &&
"Types should be the same");
// We form here the list of substitutions and the substituted callee
// type. For specializations with layout constraints, we claim that
// the substitution T satisfies the specialized requirement
// 'TS : LayoutConstraint', where LayoutConstraint could be
// e.g. _Trivial(64). We claim it, because we ensure it by the
// method how this call is constructed.
// This is a hack and works currently just by coincidence.
// But it is not quite true from the SIL type system
// point of view as we do not really cast at the SIL level the original
// parameter value of type T into a more specialized generic
// type 'TS : LayoutConstraint'.
//
// TODO: Introduce a proper way to express such a cast.
// It could be an instruction similar to checked_cast_br, e.g.
// something like:
// 'checked_constraint_cast_br %1 : T to $opened("") <TS : _Trivial(64)>',
// where <TS: _Trivial(64)> introduces a new archetype with the given
// constraints.
if (ReInfo.getSpecializedType()->isPolymorphic()) {
Subs = ReInfo.getCallerParamSubstitutions();
CalleeSubstFnTy = CanSILFuncTy->substGenericArgs(
Builder.getModule(), ReInfo.getCallerParamSubstitutions());
assert(!CalleeSubstFnTy->isPolymorphic() &&
"Substituted callee type should not be polymorphic");
assert(!CalleeSubstFnTy->hasTypeParameter() &&
"Substituted callee type should not have type parameters");
}
}
auto CalleeSILSubstFnTy = SILType::getPrimitiveObjectType(CalleeSubstFnTy);
SILFunctionConventions fnConv(CalleeSILSubstFnTy.castTo<SILFunctionType>(),
Builder.getModule());
if (!CanSILFuncTy->hasErrorResult()) {
assert(!CanSILFuncTy->isPolymorphic());
return Builder.createApply(CalleeFunc->getLocation(), FuncRefInst, CallArgs,
false);
return Builder.createApply(
CalleeFunc->getLocation(), FuncRefInst, CalleeSILSubstFnTy,
fnConv.getSILResultType(), Subs, CallArgs, false);
}
return emitApplyWithRethrow(Builder, CalleeFunc->getLocation(), FuncRefInst,
CanSILFuncTy, CallArgs, EmitCleanup);
return emitApplyWithRethrow(Builder, CalleeFunc->getLocation(),
FuncRefInst, CalleeSubstFnTy, Subs,
CallArgs,
EmitCleanup);
}
/// Returns the thick metatype for the given SILType.
@@ -222,25 +270,26 @@ namespace {
/// Helper class for emitting code to dispatch to a specialized function.
class EagerDispatch {
SILFunction *GenericFunc;
#if 0
const SILSpecializeAttr &SA;
#endif
const ReabstractionInfo &ReInfo;
const SILFunctionConventions substConv;
SILBuilder Builder;
SILLocation Loc;
// Function to check if a given object is a class.
SILFunction *IsClassF;
public:
// Instantiate a SILBuilder for inserting instructions at the top of the
// original generic function.
EagerDispatch(SILFunction *GenericFunc, const SILSpecializeAttr &SA,
EagerDispatch(SILFunction *GenericFunc,
const ReabstractionInfo &ReInfo)
: GenericFunc(GenericFunc), ReInfo(ReInfo),
substConv(ReInfo.getSubstitutedType(), GenericFunc->getModule()),
Builder(*GenericFunc), Loc(GenericFunc->getLocation()) {
Builder.setCurrentDebugScope(GenericFunc->getDebugScope());
IsClassF = Builder.getModule().hasFunction(
"_swift_isClassOrObjCExistentialType", SILLinkage::PublicExternal);
assert(IsClassF);
}
void emitDispatchTo(SILFunction *NewFunc);
@@ -249,7 +298,23 @@ protected:
void emitTypeCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy);
SILValue emitArgumentCast(SILFunctionArgument *OrigArg, unsigned Idx);
void emitTrivialAndSizeCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy,
LayoutConstraint Layout);
void emitIsTrivialCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy,
LayoutConstraint Layout);
void emitRefCountedObjectCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy,
LayoutConstraint Layout);
void emitLayoutCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy);
SILValue emitArgumentCast(CanSILFunctionType CalleeSubstFnTy,
SILFunctionArgument *OrigArg, unsigned Idx);
SILValue emitArgumentConversion(SmallVectorImpl<SILValue> &CallArgs);
};
@@ -259,7 +324,7 @@ protected:
/// given specialized function. Converts call arguments. Emits an invocation of
/// the specialized function. Handle the return value.
void EagerDispatch::emitDispatchTo(SILFunction *NewFunc) {
SILBasicBlock *OldReturnBB = &*GenericFunc->findReturnBB();
// 1. Emit a cascading sequence of type checks blocks.
// First split the entry BB, moving all instructions to the FailedTypeCheckBB.
@@ -270,26 +335,49 @@ void EagerDispatch::emitDispatchTo(SILFunction *NewFunc) {
// Iterate over all dependent types in the generic signature, which will match
// the specialized attribute's substitution list. Visit only
// SubstitutableTypes, skipping DependentTypes.
// TODO: Uncomment when Generics.cpp is updated to use the
// new @_specialize attribute for partial specializations.
#if 0
auto GenericSig =
GenericFunc->getLoweredFunctionType()->getGenericSignature();
auto SubIt = SA.getSubstitutions().begin();
auto SubEnd = SA.getSubstitutions().end();
for (auto DepTy : GenericSig->getAllDependentTypes()) {
auto SubIt = ReInfo.getClonerParamSubstitutions().begin();
auto SubEnd = ReInfo.getClonerParamSubstitutions().end();
auto DepTypes = GenericSig->getAllDependentTypes();
for (auto DepTy : DepTypes) {
assert(SubIt != SubEnd && "Not enough substitutions.");
if (auto ParamTy = DepTy->getAs<SubstitutableType>())
emitTypeCheck(FailedTypeCheckBB, ParamTy, SubIt->getReplacement());
if (auto ParamTy = DepTy->getAs<SubstitutableType>()) {
auto Replacement = SubIt->getReplacement();
auto GenericEnv = ReInfo.getSpecializedGenericEnvironment();
if (!Replacement->hasArchetype()) {
if (GenericEnv)
Replacement = GenericEnv->mapTypeIntoContext(Replacement);
assert(!Replacement->hasTypeParameter());
// Dispatch on concrete type.
emitTypeCheck(FailedTypeCheckBB, ParamTy, Replacement);
} else {
// If Replacement has a layout constraint, then dispatch based
// on its size and the fact that it is trivial.
auto LayoutInfo = Replacement->getLayoutConstraint();
if (LayoutInfo && LayoutInfo->isTrivial()) {
// Emit a check that it is a trivial type of a certain size.
emitTrivialAndSizeCheck(FailedTypeCheckBB, ParamTy,
GenericEnv->mapTypeIntoContext(Replacement),
LayoutInfo);
} else if (LayoutInfo && LayoutInfo->isRefCountedObject()) {
// Emit a check that it is an object of a reference counted type.
emitRefCountedObjectCheck(FailedTypeCheckBB, ParamTy,
GenericEnv->mapTypeIntoContext(Replacement),
LayoutInfo);
}
}
}
++SubIt;
}
assert(SubIt == SubEnd && "Too many substitutions.");
(void) SubEnd;
#else
static_cast<void>(FailedTypeCheckBB);
#endif
if (OldReturnBB == &EntryBB) {
OldReturnBB = FailedTypeCheckBB;
}
// 2. Convert call arguments, casting and adjusting for calling convention.
SmallVector<SILValue, 8> CallArgs;
@@ -300,7 +388,8 @@ void EagerDispatch::emitDispatchTo(SILFunction *NewFunc) {
// Emit any rethrow with no cleanup since all args have been forwarded and
// nothing has been locally allocated or copied.
auto NoCleanup = [](SILBuilder&, SILLocation){};
SILValue Result = emitInvocation(Builder, Loc, NewFunc, CallArgs, NoCleanup);
SILValue Result =
emitInvocation(Builder, ReInfo, Loc, NewFunc, CallArgs, NoCleanup);
// 4. Handle the return value.
@@ -323,7 +412,7 @@ void EagerDispatch::emitDispatchTo(SILFunction *NewFunc) {
auto resultTy = GenericFunc->getConventions().getSILResultType();
auto GenResultTy = GenericFunc->mapTypeIntoContext(resultTy);
auto CastResult = Builder.createUncheckedBitCast(Loc, Result, GenResultTy);
addReturnValue(Builder.getInsertionBB(), CastResult);
addReturnValue(Builder.getInsertionBB(), OldReturnBB, CastResult);
}
}
@@ -372,10 +461,138 @@ emitTypeCheck(SILBasicBlock *FailedTypeCheckBB, SubstitutableType *ParamTy,
Builder.emitBlock(SuccessBB);
}
void EagerDispatch::emitIsTrivialCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy,
LayoutConstraint Layout) {
auto &Ctx = Builder.getASTContext();
// Instantiate a thick metatype for T.Type
auto ContextTy = GenericFunc->mapTypeIntoContext(ParamTy);
auto GenericMT = Builder.createMetatype(
Loc, getThickMetatypeType(ContextTy->getCanonicalType()));
auto BoolTy = SILType::getBuiltinIntegerType(1, Ctx);
Substitution Sub(ContextTy, {});
// Emit a check that it is a pod object.
auto IsPOD = Builder.createBuiltin(Loc, Ctx.getIdentifier("ispod"), BoolTy,
Sub, {GenericMT});
auto *SuccessBB = Builder.getFunction().createBasicBlock();
Builder.createCondBranch(Loc, IsPOD, SuccessBB, FailedTypeCheckBB);
Builder.emitBlock(SuccessBB);
}
void EagerDispatch::emitTrivialAndSizeCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy,
Type SubTy,
LayoutConstraint Layout) {
if (Layout->isAddressOnlyTrivial()) {
emitIsTrivialCheck(FailedTypeCheckBB, ParamTy, SubTy, Layout);
return;
}
auto &Ctx = Builder.getASTContext();
// Instantiate a thick metatype for T.Type
auto ContextTy = GenericFunc->mapTypeIntoContext(ParamTy);
auto GenericMT = Builder.createMetatype(
Loc, getThickMetatypeType(ContextTy->getCanonicalType()));
auto WordTy = SILType::getBuiltinWordType(Ctx);
auto BoolTy = SILType::getBuiltinIntegerType(1, Ctx);
Substitution Sub(ContextTy, {});
auto ParamSize = Builder.createBuiltin(Loc, Ctx.getIdentifier("sizeof"),
WordTy, Sub, { GenericMT });
auto LayoutSize =
Builder.createIntegerLiteral(Loc, WordTy, Layout->getTrivialSizeInBytes());
const char *CmpOpName = Layout->isFixedSizeTrivial() ? "cmp_eq" : "cmp_le";
auto Cmp =
Builder.createBuiltinBinaryFunction(Loc, CmpOpName, WordTy,
BoolTy,
{ParamSize, LayoutSize});
auto *SuccessBB1 = Builder.getFunction().createBasicBlock();
Builder.createCondBranch(Loc, Cmp, SuccessBB1, FailedTypeCheckBB);
Builder.emitBlock(SuccessBB1);
// Emit a check that it is a pod object.
// TODO: Perform this check before all the fixed size checks!
auto IsPOD = Builder.createBuiltin(Loc, Ctx.getIdentifier("ispod"),
BoolTy, Sub, { GenericMT });
auto *SuccessBB2 = Builder.getFunction().createBasicBlock();
Builder.createCondBranch(Loc, IsPOD, SuccessBB2, FailedTypeCheckBB);
Builder.emitBlock(SuccessBB2);
}
void EagerDispatch::emitRefCountedObjectCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy,
Type SubTy,
LayoutConstraint Layout) {
auto &Ctx = Builder.getASTContext();
// Instantiate a thick metatype for T.Type
auto ContextTy = GenericFunc->mapTypeIntoContext(ParamTy);
auto GenericMT = Builder.createMetatype(
Loc, getThickMetatypeType(ContextTy->getCanonicalType()));
auto Int8Ty = SILType::getBuiltinIntegerType(8, Ctx);
auto BoolTy = SILType::getBuiltinIntegerType(1, Ctx);
Substitution Sub(ContextTy, {});
// Emit a check that it is a reference-counted object.
// TODO: Perform this check before all fixed size checks.
// FIXME: What builtin do we use to check it????
auto CanBeClass = Builder.createBuiltin(
Loc, Ctx.getIdentifier("canBeClass"), Int8Ty, Sub, {GenericMT});
auto ClassConst =
Builder.createIntegerLiteral(Loc, Int8Ty, 1);
auto Cmp1 =
Builder.createBuiltinBinaryFunction(Loc, "cmp_eq", Int8Ty,
BoolTy,
{CanBeClass, ClassConst});
auto *SuccessBB = Builder.getFunction().createBasicBlock();
auto *MayBeCalssCheckBB = Builder.getFunction().createBasicBlock();
Builder.createCondBranch(Loc, Cmp1, SuccessBB,
MayBeCalssCheckBB);
Builder.emitBlock(MayBeCalssCheckBB);
auto MayBeClassConst =
Builder.createIntegerLiteral(Loc, Int8Ty, 2);
auto Cmp2 =
Builder.createBuiltinBinaryFunction(Loc, "cmp_eq", Int8Ty,
BoolTy,
{CanBeClass, MayBeClassConst});
auto *IsClassCheckBB = Builder.getFunction().createBasicBlock();
Builder.createCondBranch(Loc, Cmp2, IsClassCheckBB,
FailedTypeCheckBB);
Builder.emitBlock(IsClassCheckBB);
auto *FRI = Builder.createFunctionRef(Loc, IsClassF);
auto CanFnTy = IsClassF->getLoweredFunctionType()->substGenericArgs(
Builder.getModule(), {Sub});
auto SILFnTy = SILType::getPrimitiveObjectType(CanFnTy);
SILFunctionConventions fnConv(CanFnTy, Builder.getModule());
auto SILResultTy = fnConv.getSILResultType();
auto IsClassRuntimeCheck =
Builder.createApply(Loc, FRI, SILFnTy, SILResultTy, {Sub}, {GenericMT},
/* isNonThrowing */ false);
// Extract the i1 from the Bool struct.
StructDecl *BoolStruct = cast<StructDecl>(Ctx.getBoolDecl());
auto Members = BoolStruct->lookupDirect(Ctx.Id_value_);
assert(Members.size() == 1 &&
"Bool should have only one property with name '_value'");
auto Member = dyn_cast<VarDecl>(Members[0]);
assert(Member &&"Bool should have a property with name '_value' of type Int1");
auto BoolValue =
Builder.emitStructExtract(Loc, IsClassRuntimeCheck, Member, BoolTy);
Builder.createCondBranch(Loc, BoolValue, SuccessBB, FailedTypeCheckBB);
Builder.emitBlock(SuccessBB);
}
/// Cast a generic argument to its specialized type.
SILValue EagerDispatch::emitArgumentCast(SILFunctionArgument *OrigArg,
SILValue EagerDispatch::emitArgumentCast(CanSILFunctionType CalleeSubstFnTy,
SILFunctionArgument *OrigArg,
unsigned Idx) {
SILFunctionConventions substConv(ReInfo.getSubstitutedType(),
SILFunctionConventions substConv(CalleeSubstFnTy,
Builder.getModule());
auto CastTy = substConv.getSILArgumentType(Idx);
assert(CastTy.isAddress()
@@ -401,13 +618,32 @@ emitArgumentConversion(SmallVectorImpl<SILValue> &CallArgs) {
auto OrigArgs = GenericFunc->begin()->getFunctionArguments();
assert(OrigArgs.size() == substConv.getNumSILArguments()
&& "signature mismatch");
// Create a substituted callee type.
auto SubstitutedType = ReInfo.getSubstitutedType();
auto SpecializedType = ReInfo.getSpecializedType();
auto CanSILFuncTy = SubstitutedType;
auto CalleeSubstFnTy = CanSILFuncTy;
if (CanSILFuncTy->isPolymorphic()) {
CalleeSubstFnTy = CanSILFuncTy->substGenericArgs(
Builder.getModule(), ReInfo.getCallerParamSubstitutions());
assert(!CalleeSubstFnTy->isPolymorphic() &&
"Substituted callee type should not be polymorphic");
assert(!CalleeSubstFnTy->hasTypeParameter() &&
"Substituted callee type should not have type parameters");
SubstitutedType = CalleeSubstFnTy;
SpecializedType =
ReInfo.createSpecializedType(SubstitutedType, Builder.getModule());
}
assert(OrigArgs.size() == ReInfo.getNumArguments() && "signature mismatch");
CallArgs.reserve(OrigArgs.size());
SILValue StoreResultTo;
for (auto *OrigArg : OrigArgs) {
unsigned ArgIdx = OrigArg->getIndex();
auto CastArg = emitArgumentCast(OrigArg, ArgIdx);
auto CastArg = emitArgumentCast(SubstitutedType, OrigArg, ArgIdx);
DEBUG(dbgs() << " Cast generic arg: "; CastArg->print(dbgs()));
if (!substConv.useLoweredAddresses()) {
@@ -431,6 +667,15 @@ emitArgumentConversion(SmallVectorImpl<SILValue> &CallArgs) {
if (ReInfo.isParamConverted(paramIdx)) {
// An argument is converted from indirect to direct. Instead of the
// address we pass the loaded value.
// FIXME: If type of CastArg is an archetype, but it is loadable because
// of a layout constraint on the caller side, we have a problem here
// We need to load the value on the caller side, but this archetype is
// not statically known to be loadable on the caller side (though we
// have proven dynamically that it has a fixed size).
// We can try to load it as an int value of width N, but then it is not
// clear how to convert it into a value of the archetype type, which is
// expected. May be we should pass it as @in parameter and make it
// loadable on the caller's side?
SILValue Val = Builder.createLoad(Loc, CastArg,
LoadOwnershipQualifier::Unqualified);
CallArgs.push_back(Val);
@@ -455,9 +700,6 @@ public:
};
} // end anonymous namespace
// TODO: Uncomment when Generics.cpp is updated to use the
// new @_specialize attribute for partial specializations.
#if 0
/// Specializes a generic function for a concrete type list.
static SILFunction *eagerSpecialize(SILFunction *GenericFunc,
const SILSpecializeAttr &SA,
@@ -475,66 +717,67 @@ static SILFunction *eagerSpecialize(SILFunction *GenericFunc,
[]{ dbgs() << ", "; });
dbgs() << "> with ";
SA.print(dbgs()); dbgs() << "\n");
// Create a specialized function.
// TODO: Uncomment when Generics.cpp is updated to use the
// new @_specialize attribute for partial specializations.
#if 0
GenericFuncSpecializer
FuncSpecializer(GenericFunc, SA.getSubstitutions(),
FuncSpecializer(GenericFunc, ReInfo.getClonerParamSubstitutions(),
GenericFunc->isFragile(), ReInfo);
SILFunction *NewFunc = FuncSpecializer.trySpecialization();
if (!NewFunc)
DEBUG(dbgs() << " Failed. Cannot specialize function.\n");
return NewFunc;
#else
return nullptr;
#endif
}
#endif
/// Run the pass.
void EagerSpecializerTransform::run() {
if (!EagerSpecializeFlag)
return;
// Process functions in any order.
bool Changed = false;
for (auto &F : *getModule()) {
if (!F.shouldOptimize()) {
DEBUG(dbgs() << " Cannot specialize function " << F.getName()
<< " marked to be excluded from optimizations.\n");
<< " marked to be excluded from optimizations.\n");
continue;
}
// Only specialize functions in their home module.
if (F.isExternalDeclaration() || F.isAvailableExternally())
continue;
if (!F.getLoweredFunctionType()->getGenericSignature())
continue;
// Create a specialized function with ReabstractionInfo for each attribute.
SmallVector<SILFunction*, 8> SpecializedFuncs;
SmallVector<SILFunction *, 8> SpecializedFuncs;
SmallVector<ReabstractionInfo, 4> ReInfoVec;
ReInfoVec.reserve(F.getSpecializeAttrs().size());
// TODO: Uncomment when Generics.cpp is updated to use the
// new @_specialize attribute for partial specializations.
#if 0
// TODO: Use a decision-tree to reduce the amount of dynamic checks being
// performed.
for (auto *SA : F.getSpecializeAttrs()) {
ReInfoVec.emplace_back(&F, SA->getSubstitutions());
auto AttrRequirements = SA->getRequirements();
ReInfoVec.emplace_back(&F, AttrRequirements);
auto *NewFunc = eagerSpecialize(&F, *SA, ReInfoVec.back());
SpecializedFuncs.push_back(NewFunc);
if (SA->isExported()) {
NewFunc->setKeepAsPublic(true);
continue;
}
}
// Emit a type check and dispatch to each specialized function.
// TODO: Optimize the dispatch code to minimize the amount
// of checks. Use decision trees for this purpose.
for_each3(F.getSpecializeAttrs(), SpecializedFuncs, ReInfoVec,
[&](const SILSpecializeAttr *SA, SILFunction *NewFunc,
const ReabstractionInfo &ReInfo) {
if (NewFunc) {
Changed = true;
EagerDispatch(&F, *SA, ReInfo).emitDispatchTo(NewFunc);
}
});
#endif
[&](const SILSpecializeAttr *SA, SILFunction *NewFunc,
const ReabstractionInfo &ReInfo) {
if (NewFunc) {
Changed = true;
EagerDispatch(&F, ReInfo).emitDispatchTo(NewFunc);
}
});
// As specializations are created, the attributes should be removed.
F.clearSpecializeAttrs();
}