mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
First step in splitting out a utility for devirtualizing from the pass.
No functional change here, and this is not in its final form. This is just cut/paste/tweak to split the code apart in a first reasonable form. Swift SVN r25769
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define DEBUG_TYPE "sil-devirtualizer"
|
||||
#define DEBUG_TYPE "sil-devirtualizer-pass"
|
||||
#include "swift/Basic/DemangleWrappers.h"
|
||||
#include "swift/Basic/Fallthrough.h"
|
||||
#include "swift/SIL/SILArgument.h"
|
||||
@@ -25,9 +25,9 @@
|
||||
#include "swift/SILAnalysis/ClassHierarchyAnalysis.h"
|
||||
#include "swift/SILPasses/Utils/Generics.h"
|
||||
#include "swift/SILPasses/Passes.h"
|
||||
#include "swift/SILPasses/Utils/Local.h"
|
||||
#include "swift/SILPasses/PassManager.h"
|
||||
#include "swift/SILPasses/Transforms.h"
|
||||
#include "swift/SILPasses/Utils/Devirtualize.h"
|
||||
#include "swift/SILPasses/Utils/SILInliner.h"
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
@@ -38,596 +38,10 @@
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
using namespace swift;
|
||||
|
||||
STATISTIC(NumInlineCaches, "Number of monomorphic inline caches inserted");
|
||||
STATISTIC(NumClassDevirt, "Number of calls devirtualized");
|
||||
STATISTIC(NumWitnessDevirt, "Number of witness_method devirtualized");
|
||||
|
||||
// The number of subclasses to allow when placing polymorphic inline caches.
|
||||
static const int MaxNumPolymorphicInlineCaches = 6;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Class Method Optimization
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Return the dynamic class type of the value S, or nullptr if it
|
||||
/// cannot be determined whether S has a class type or what type that
|
||||
/// is.
|
||||
static ClassDecl *getClassFromConstructor(SILValue S) {
|
||||
// First strip off casts.
|
||||
S = S.stripCasts();
|
||||
|
||||
// Look for a a static ClassTypes in AllocRefInst or MetatypeInst.
|
||||
if (AllocRefInst *ARI = dyn_cast<AllocRefInst>(S))
|
||||
return ARI->getType().getClassOrBoundGenericClass();
|
||||
|
||||
auto *MTI = dyn_cast<MetatypeInst>(S);
|
||||
if (!MTI)
|
||||
return nullptr;
|
||||
|
||||
CanType instTy = MTI->getType().castTo<MetatypeType>().getInstanceType();
|
||||
return instTy.getClassOrBoundGenericClass();
|
||||
}
|
||||
|
||||
/// Return bound generic type for the unbound type Superclass,
|
||||
/// which is a superclass of a bound generic type BoundDerived
|
||||
/// (Base may be also the same as BoundDerived).
|
||||
static SILType bindSuperclass(Module *Module,
|
||||
CanType Superclass,
|
||||
SILType BoundDerived,
|
||||
ArrayRef<Substitution>& Subs) {
|
||||
assert(BoundDerived && "Expected non-null type!");
|
||||
|
||||
SILType BoundSuperclass = BoundDerived;
|
||||
|
||||
do {
|
||||
auto CanBoundSuperclass = BoundSuperclass.getSwiftRValueType();
|
||||
// Get declaration of the superclass.
|
||||
auto *Decl = CanBoundSuperclass.getNominalOrBoundGenericNominal();
|
||||
// Obtain the unbound variant of the current superclass
|
||||
CanType UnboundSuperclass = Decl->getDeclaredType()->getCanonicalType();
|
||||
// Check if we found a superclass we are looking for.
|
||||
if (UnboundSuperclass == Superclass) {
|
||||
auto BoundBaseType = dyn_cast<BoundGenericType>(CanBoundSuperclass);
|
||||
if (BoundBaseType)
|
||||
// If it is a bound generic type, look up its substitutions
|
||||
Subs = BoundBaseType->getSubstitutions(Module,
|
||||
nullptr);
|
||||
else
|
||||
// If it is a non-bound type, there are no substitutions.
|
||||
Subs.empty();
|
||||
return BoundSuperclass;
|
||||
}
|
||||
|
||||
// Get the superclass of current one
|
||||
BoundSuperclass = BoundSuperclass.getSuperclass(nullptr);
|
||||
} while (BoundSuperclass);
|
||||
|
||||
return SILType();
|
||||
}
|
||||
|
||||
// Returns true if any generic types parameters of the class are
|
||||
// unbound.
|
||||
static bool isClassWithUnboundGenericParameters(SILType C, SILModule &M) {
|
||||
auto *CD = C.getClassOrBoundGenericClass();
|
||||
if (CD && CD->getGenericSignature()) {
|
||||
auto InstanceTypeSubsts =
|
||||
C.gatherAllSubstitutions(M);
|
||||
|
||||
if (!InstanceTypeSubsts.empty()) {
|
||||
if (hasUnboundGenericTypes(InstanceTypeSubsts))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// A helper struct to keep information collected by
|
||||
/// the analysis which checks if it is possible to
|
||||
/// devirtualize a given class_method.
|
||||
struct DevirtClassMethodInfo {
|
||||
SILFunctionType::ParameterSILTypeArrayRef ParamTypes;
|
||||
SILFunction *F;
|
||||
CanSILFunctionType SubstCalleeType;
|
||||
ArrayRef<Substitution> Substitutions;
|
||||
|
||||
DevirtClassMethodInfo() : ParamTypes({}), F(nullptr) {}
|
||||
};
|
||||
|
||||
/// \brief Check if it is possible to devirtualize an Apply instruction
|
||||
/// and a class member obtained using the class_method instruction into
|
||||
/// a direct call to a specific member of a specific class.
|
||||
///
|
||||
/// \p AI is the apply to devirtualize.
|
||||
/// \p Member is the class member to devirtualize.
|
||||
/// \p ClassInstance is the operand for the ClassMethodInst or an alternative
|
||||
/// reference (such as downcasted class reference).
|
||||
/// \p CD is the class declaration of the class, where the lookup of the member
|
||||
/// should be performed.
|
||||
/// \p DCMI is the devirt. class_method analysis result to be used for
|
||||
/// performing the transformation.
|
||||
/// return true if it is possible to devirtualize, false - otherwise.
|
||||
static bool canDevirtualizeClassMethod(ApplyInst *AI,
|
||||
SILDeclRef Member,
|
||||
SILType ClassInstanceType,
|
||||
ClassDecl *CD,
|
||||
DevirtClassMethodInfo& DCMI) {
|
||||
DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI);
|
||||
|
||||
// First attempt to lookup the origin for our class method. The origin should
|
||||
// either be a metatype or an alloc_ref.
|
||||
DEBUG(llvm::dbgs() << " Origin Type: " << ClassInstanceType);
|
||||
|
||||
assert(CD && "Invalid class type");
|
||||
|
||||
// Otherwise lookup from the module the least derived implementing method from
|
||||
// the module vtables.
|
||||
SILModule &Mod = AI->getModule();
|
||||
// Find the implementation of the member which should be invoked.
|
||||
DCMI.F = Mod.lookUpFunctionInVTable(CD, Member);
|
||||
|
||||
// If we do not find any such function, we have no function to devirtualize
|
||||
// to... so bail.
|
||||
if (!DCMI.F) {
|
||||
DEBUG(llvm::dbgs() << " FAIL: Could not find matching VTable or "
|
||||
"vtable method for this class.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ok, we found a function F that we can devirtualize our class method
|
||||
// to. We want to do everything on the substituted type in the case of
|
||||
// generics. Thus construct our subst callee type for F.
|
||||
SILModule &M = DCMI.F->getModule();
|
||||
CanSILFunctionType GenCalleeType = DCMI.F->getLoweredFunctionType();
|
||||
unsigned CalleeGenericParamsNum = 0;
|
||||
if (GenCalleeType->isPolymorphic())
|
||||
CalleeGenericParamsNum = GenCalleeType->getGenericSignature()
|
||||
->getGenericParams().size();
|
||||
// Class F belongs to.
|
||||
CanType FSelfClass = GenCalleeType->getSelfParameter().getType();
|
||||
|
||||
// Bail if any generic types parameters of the class instance type are
|
||||
// unbound.
|
||||
// We cannot devirtualize unbound generic calls yet.
|
||||
if (isClassWithUnboundGenericParameters(ClassInstanceType, AI->getModule()))
|
||||
return false;
|
||||
|
||||
// *NOTE*:
|
||||
// Apply instruction substitutions are for the Member from a protocol or
|
||||
// class B, where this member was first defined, before it got overridden by
|
||||
// derived classes.
|
||||
//
|
||||
// The implementation F (the implementing method) which was found may have
|
||||
// a different set of generic parameters, e.g. because it is implemented by a
|
||||
// class D1 derived from B.
|
||||
//
|
||||
// ClassInstanceType may have a type different from both the type B
|
||||
// the Member belongs to and from the ClassInstanceType, e.g. if
|
||||
// ClassInstance is of a class D2, which is derived from D1, but does not
|
||||
// override the Member.
|
||||
//
|
||||
// As a result, substitutions provided by AI are for Member, whereas
|
||||
// substitutions in ClassInstanceType are for D2. And substitutions for D1
|
||||
// are not available directly in a general case. Therefore, they have to
|
||||
// be computed.
|
||||
//
|
||||
// What we know for sure:
|
||||
// B is a superclass of D1
|
||||
// D1 is a superclass of D2.
|
||||
// D1 can be the same as D2. D1 can be the same as B.
|
||||
//
|
||||
// So, substitutions from AI are for class B.
|
||||
// Substitutions for class D1 by means of bindSuperclass(), which starts
|
||||
// with a bound type ClassInstanceType and checks its superclasses until it
|
||||
// finds a bound superclass matching D1 and returns its substitutions.
|
||||
|
||||
SILType FSelfSubstType;
|
||||
|
||||
if (GenCalleeType->isPolymorphic()) {
|
||||
// Declaration of the class F belongs to.
|
||||
if (auto *FSelfTypeDecl = FSelfClass.getNominalOrBoundGenericNominal()) {
|
||||
// Get the unbound generic type F belongs to.
|
||||
CanType FSelfGenericType =
|
||||
FSelfTypeDecl->getDeclaredType()->getCanonicalType();
|
||||
|
||||
assert((isa<BoundGenericType>(ClassInstanceType.getSwiftRValueType()) ||
|
||||
isa<NominalType>(ClassInstanceType.getSwiftRValueType())) &&
|
||||
"Self type should be either a bound generic type"
|
||||
"or a non-generic type");
|
||||
|
||||
assert((isa<UnboundGenericType>(FSelfGenericType) ||
|
||||
isa<NominalType>(FSelfGenericType)) &&
|
||||
"Method implementation self type should be generic");
|
||||
|
||||
// We know that ClassInstanceType is derived from FSelfGenericType.
|
||||
// We need to determine the proper substitutions for FGenericSILClass
|
||||
// based on the bound generic type of ClassInstanceType.
|
||||
FSelfSubstType = bindSuperclass(AI->getModule().getSwiftModule(),
|
||||
FSelfGenericType,
|
||||
ClassInstanceType,
|
||||
DCMI.Substitutions);
|
||||
|
||||
// Bail if it was not possible to determine the bound generic class.
|
||||
if (FSelfSubstType == SILType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isa<BoundGenericType>(ClassInstanceType.getSwiftRValueType()) &&
|
||||
CalleeGenericParamsNum &&
|
||||
DCMI.Substitutions.empty())
|
||||
// If ClassInstance is not a bound generic type, try to derive
|
||||
// substitutions from the apply instruction.
|
||||
DCMI.Substitutions = AI->getSubstitutions();
|
||||
} else {
|
||||
// It is not a type or bound type.
|
||||
// It could be that GenCalleeType is generic, but its arguments cannot
|
||||
// be derived from the type of self. In this case, we can try to
|
||||
// approach it from another end and take the AI substitutions.
|
||||
DCMI.Substitutions = AI->getSubstitutions();
|
||||
}
|
||||
}
|
||||
|
||||
// If implementing method is not polymorphic, there is no need to
|
||||
// use any substitutions.
|
||||
if (CalleeGenericParamsNum == 0 && !DCMI.Substitutions.empty())
|
||||
DCMI.Substitutions = {};
|
||||
else if (CalleeGenericParamsNum != DCMI.Substitutions.size())
|
||||
// Bail if the number of generic parameters of the callee does not match
|
||||
// the number of substitutions, because we don't know how to handle this.
|
||||
return false;
|
||||
|
||||
DCMI.SubstCalleeType =
|
||||
GenCalleeType->substGenericArgs(M, M.getSwiftModule(), DCMI.Substitutions);
|
||||
|
||||
|
||||
// If F's this pointer has a different type from CMI's operand and the
|
||||
// "this" pointer type is a super class of the CMI's operand, insert an
|
||||
// upcast.
|
||||
DCMI.ParamTypes =
|
||||
DCMI.SubstCalleeType->getParameterSILTypesWithoutIndirectResult();
|
||||
|
||||
// We should always have a this pointer. Assert on debug builds, return
|
||||
// nullptr on release builds.
|
||||
assert(!DCMI.ParamTypes.empty() &&
|
||||
"Must have a this pointer when calling a class method inst.");
|
||||
if (DCMI.ParamTypes.empty())
|
||||
return false;
|
||||
|
||||
// If we reached this point, we can replace class_method by a concrete
|
||||
// function ref for sure.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Devirtualize an Apply instruction and a class member obtained
|
||||
/// using the class_method instruction into a direct call to a specific
|
||||
/// member of a specific class.
|
||||
///
|
||||
/// \p AI is the apply to devirtualize.
|
||||
/// \p Member is the class member to devirtualize.
|
||||
/// \p ClassInstance is the operand for the ClassMethodInst or an alternative
|
||||
/// reference (such as downcasted class reference).
|
||||
/// \p DCMI is the devirtualization class_method analysis result to be used for
|
||||
/// performing the transformation.
|
||||
/// return the new ApplyInst if created one or null.
|
||||
static ApplyInst *devirtualizeClassMethod(ApplyInst *AI,
|
||||
SILDeclRef Member,
|
||||
SILValue ClassInstance,
|
||||
DevirtClassMethodInfo& DCMI) {
|
||||
|
||||
DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI);
|
||||
|
||||
// Grab the self type from the function ref and the self type from the class
|
||||
// method inst.
|
||||
SILType FuncSelfTy = DCMI.ParamTypes[DCMI.ParamTypes.size() - 1];
|
||||
SILType OriginTy = ClassInstance.getType();
|
||||
SILBuilderWithScope<16> B(AI);
|
||||
|
||||
// Then compare the two types and if they are unequal...
|
||||
if (FuncSelfTy != OriginTy) {
|
||||
if (ClassInstance.stripUpCasts().getType().getAs<MetatypeType>()) {
|
||||
auto &Module = AI->getModule();
|
||||
(void) Module;
|
||||
assert(FuncSelfTy.getMetatypeInstanceType(Module).
|
||||
isSuperclassOf(OriginTy.getMetatypeInstanceType(Module)) &&
|
||||
"Can not call a class method"
|
||||
" on a non-subclass of the class_methods class.");
|
||||
} else {
|
||||
assert(FuncSelfTy.isSuperclassOf(OriginTy) &&
|
||||
"Can not call a class method"
|
||||
" on a non-subclass of the class_methods class.");
|
||||
}
|
||||
// Otherwise, upcast origin to the appropriate type.
|
||||
ClassInstance = B.createUpcast(AI->getLoc(), ClassInstance, FuncSelfTy);
|
||||
}
|
||||
|
||||
// Success! Perform the devirtualization.
|
||||
FunctionRefInst *FRI = B.createFunctionRef(AI->getLoc(), DCMI.F);
|
||||
|
||||
// Construct a new arg list. First process all non-self operands, ref, addr
|
||||
// casting them to the appropriate types for F so that we allow for covariant
|
||||
// indirect return types and contravariant arguments.
|
||||
llvm::SmallVector<SILValue, 8> NewArgs;
|
||||
auto Args = AI->getArguments();
|
||||
auto allParamTypes = DCMI.SubstCalleeType->getParameterSILTypes();
|
||||
|
||||
// For each old argument Op...
|
||||
for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) {
|
||||
SILValue Op = Args[i];
|
||||
SILType OpTy = Op.getType();
|
||||
SILType FOpTy = allParamTypes[i];
|
||||
|
||||
// If the type matches the type for the given parameter in F, just add it to
|
||||
// our arg list and continue.
|
||||
if (OpTy == FOpTy) {
|
||||
NewArgs.push_back(Op);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise we have either a covariant return type or a contravariant
|
||||
// argument type. Cast it appropriately.
|
||||
assert((OpTy.isAddress() || OpTy.isHeapObjectReferenceType()) &&
|
||||
"Only addresses and refs can have their types changed due to "
|
||||
"covariant return types or contravariant argument types.");
|
||||
|
||||
// If OpTy is an address, perform an unchecked_addr_cast.
|
||||
if (OpTy.isAddress()) {
|
||||
NewArgs.push_back(B.createUncheckedAddrCast(AI->getLoc(), Op, FOpTy));
|
||||
} else {
|
||||
// Otherwise perform a ref cast.
|
||||
NewArgs.push_back(B.createUncheckedRefCast(AI->getLoc(), Op, FOpTy));
|
||||
}
|
||||
}
|
||||
|
||||
// Add in self to the end.
|
||||
NewArgs.push_back(ClassInstance);
|
||||
|
||||
// If we have a direct return type, make sure we use the subst callee return
|
||||
// type. If we have an indirect return type, AI's return type of the empty
|
||||
// tuple should be ok.
|
||||
SILType ReturnType = AI->getType();
|
||||
if (!DCMI.SubstCalleeType->hasIndirectResult()) {
|
||||
ReturnType = DCMI.SubstCalleeType->getSILResult();
|
||||
}
|
||||
|
||||
SILType SubstCalleeSILType =
|
||||
SILType::getPrimitiveObjectType(DCMI.SubstCalleeType);
|
||||
ApplyInst *NewAI =
|
||||
B.createApply(AI->getLoc(), FRI, SubstCalleeSILType, ReturnType,
|
||||
DCMI.Substitutions, NewArgs,
|
||||
FRI->getReferencedFunction()->isTransparent());
|
||||
|
||||
// If our return type differs from AI's return type, then we know that we have
|
||||
// a covariant return type. Cast it before we RAUW. This can not happen
|
||||
if (ReturnType != AI->getType()) {
|
||||
|
||||
// Check if the return type is an optional of the apply_inst type
|
||||
// or the other way around
|
||||
bool UnwrapOptionalResult = false;
|
||||
OptionalTypeKind OTK;
|
||||
|
||||
auto OptionalReturnType = ReturnType.getSwiftRValueType()
|
||||
.getAnyOptionalObjectType();
|
||||
if (OptionalReturnType == AI->getType().getSwiftRValueType()) {
|
||||
ReturnType.getSwiftRValueType().getAnyOptionalObjectType(OTK);
|
||||
UnwrapOptionalResult = true;
|
||||
}
|
||||
|
||||
assert((ReturnType.isAddress() ||
|
||||
ReturnType.isHeapObjectReferenceType() ||
|
||||
UnwrapOptionalResult) &&
|
||||
"Only addresses and refs can have their types changed due to "
|
||||
"covariant return types or contravariant argument types.");
|
||||
|
||||
SILValue CastedAI = NewAI;
|
||||
if (UnwrapOptionalResult) {
|
||||
// The devirtualized method returns an optional result.
|
||||
// We need to extract the actual result from the optional.
|
||||
auto *SomeDecl = B.getASTContext().getOptionalSomeDecl(OTK);
|
||||
CastedAI = B.createUncheckedEnumData(AI->getLoc(), NewAI, SomeDecl);
|
||||
} else if (ReturnType.isAddress()) {
|
||||
CastedAI = B.createUncheckedAddrCast(AI->getLoc(), NewAI, AI->getType());
|
||||
} else {
|
||||
CastedAI = B.createUncheckedRefCast(AI->getLoc(), NewAI, AI->getType());
|
||||
}
|
||||
SILValue(AI).replaceAllUsesWith(CastedAI);
|
||||
} else {
|
||||
AI->replaceAllUsesWith(NewAI);
|
||||
}
|
||||
|
||||
AI->eraseFromParent();
|
||||
|
||||
DEBUG(llvm::dbgs() << " SUCCESS: " << DCMI.F->getName() << "\n");
|
||||
NumClassDevirt++;
|
||||
return NewAI;
|
||||
}
|
||||
|
||||
/// This is a simplified version of devirtualizeClassMethod, which can
|
||||
/// be called without the previously prepared DevirtClassMethodInfo.
|
||||
static ApplyInst *devirtualizeClassMethod(ApplyInst *AI,
|
||||
SILDeclRef Member,
|
||||
SILValue ClassInstance,
|
||||
ClassDecl *CD) {
|
||||
DevirtClassMethodInfo DCMI;
|
||||
if (!canDevirtualizeClassMethod(AI, Member, ClassInstance.getType(), CD,
|
||||
DCMI))
|
||||
return nullptr;
|
||||
return devirtualizeClassMethod(AI, Member, ClassInstance, DCMI);
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Witness Method Optimization
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Generate a new apply of a function_ref to replace an apply of a
|
||||
/// witness_method when we've determined the actual function we'll end
|
||||
/// up calling.
|
||||
static ApplyInst *devirtualizeWitness(ApplyInst *AI, SILFunction *F,
|
||||
ArrayRef<Substitution> Subs) {
|
||||
// We know the witness thunk and the corresponding set of substitutions
|
||||
// required to invoke the protocol method at this point.
|
||||
auto &Module = AI->getModule();
|
||||
|
||||
// Collect all the required substitutions.
|
||||
//
|
||||
// The complete set of substitutions may be different, e.g. because the found
|
||||
// witness thunk F may have been created by a specialization pass and have
|
||||
// additional generic parameters.
|
||||
SmallVector<Substitution, 16> NewSubstList(Subs.begin(), Subs.end());
|
||||
|
||||
// Add the non-self-derived substitutions from the original application.
|
||||
for (auto &origSub : AI->getSubstitutionsWithoutSelfSubstitution())
|
||||
if (!origSub.getArchetype()->isSelfDerived())
|
||||
NewSubstList.push_back(origSub);
|
||||
|
||||
// Figure out the exact bound type of the function to be called by
|
||||
// applying all substitutions.
|
||||
auto CalleeCanType = F->getLoweredFunctionType();
|
||||
auto SubstCalleeCanType = CalleeCanType->substGenericArgs(
|
||||
Module, Module.getSwiftModule(), NewSubstList);
|
||||
|
||||
// Collect arguments from the apply instruction.
|
||||
auto Arguments = SmallVector<SILValue, 4>();
|
||||
|
||||
auto ParamTypes = SubstCalleeCanType->getParameterSILTypes();
|
||||
// Type of the current parameter being processed
|
||||
auto ParamType = ParamTypes.begin();
|
||||
|
||||
// Iterate over the non self arguments and add them to the
|
||||
// new argument list, upcasting when required.
|
||||
SILBuilderWithScope<8> B(AI);
|
||||
for (SILValue A : AI->getArguments()) {
|
||||
if (A.getType() != *ParamType)
|
||||
A = B.createUpcast(AI->getLoc(), A, *ParamType);
|
||||
|
||||
Arguments.push_back(A);
|
||||
++ParamType;
|
||||
}
|
||||
|
||||
// Replace old apply instruction by a new apply instruction that invokes
|
||||
// the witness thunk.
|
||||
SILBuilderWithScope<2> Builder(AI);
|
||||
SILLocation Loc = AI->getLoc();
|
||||
FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F);
|
||||
|
||||
auto SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeCanType);
|
||||
auto ResultSILType = SubstCalleeCanType->getSILResult();
|
||||
auto *SAI = Builder.createApply(Loc, FRI, SubstCalleeSILType,
|
||||
ResultSILType, NewSubstList, Arguments,
|
||||
FRI->getReferencedFunction()->isTransparent());
|
||||
AI->replaceAllUsesWith(SAI);
|
||||
AI->eraseFromParent();
|
||||
NumWitnessDevirt++;
|
||||
return SAI;
|
||||
}
|
||||
|
||||
/// In the cases where we can statically determine the function that
|
||||
/// we'll call to, replace an apply of a witness_method with an apply
|
||||
/// of a function_ref, returning the new apply.
|
||||
static ApplyInst *devirtualizeWitnessMethod(ApplyInst *AI,
|
||||
WitnessMethodInst *WMI) {
|
||||
SILFunction *F;
|
||||
ArrayRef<Substitution> Subs;
|
||||
SILWitnessTable *WT;
|
||||
|
||||
std::tie(F, WT, Subs) =
|
||||
AI->getModule().lookUpFunctionInWitnessTable(WMI->getConformance(),
|
||||
WMI->getMember());
|
||||
|
||||
if (!F)
|
||||
return nullptr;
|
||||
|
||||
return devirtualizeWitness(AI, F, Subs);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Top Level Driver
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Return the final class decl based on access control information.
|
||||
static ClassDecl *getClassFromAccessControl(ClassMethodInst *CMI) {
|
||||
const DeclContext *associatedDC = CMI->getModule().getAssociatedContext();
|
||||
if (!associatedDC) {
|
||||
// Without an associated context, we can't perform any access-based
|
||||
// optimizations.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SILDeclRef Member = CMI->getMember();
|
||||
FuncDecl *FD = Member.getFuncDecl();
|
||||
SILType ClassType = CMI->getOperand().stripUpCasts().getType();
|
||||
ClassDecl *CD = ClassType.getClassOrBoundGenericClass();
|
||||
|
||||
// Only handle valid non-dynamic non-overridden members.
|
||||
if (!CD || !FD || FD->isInvalid() || FD->isDynamic() || FD->isOverridden())
|
||||
return nullptr;
|
||||
|
||||
// Only handle members defined within the SILModule's associated context.
|
||||
if (!FD->isChildContextOf(associatedDC))
|
||||
return nullptr;
|
||||
|
||||
if (!FD->hasAccessibility())
|
||||
return nullptr;
|
||||
|
||||
// Only consider 'private' members, unless we are in whole-module compilation.
|
||||
switch (FD->getAccessibility()) {
|
||||
case Accessibility::Public:
|
||||
return nullptr;
|
||||
case Accessibility::Internal:
|
||||
if (!CMI->getModule().isWholeModule())
|
||||
return nullptr;
|
||||
break;
|
||||
case Accessibility::Private:
|
||||
break;
|
||||
}
|
||||
|
||||
Type selfTypeInMember = FD->getDeclContext()->getDeclaredTypeInContext();
|
||||
return selfTypeInMember->getClassOrBoundGenericClass();
|
||||
}
|
||||
|
||||
/// Attempt to devirtualize the given apply if possible, and return a
|
||||
/// new apply in that case, or nullptr otherwise.
|
||||
static ApplyInst *devirtualizeApply(ApplyInst *AI) {
|
||||
DEBUG(llvm::dbgs() << " Trying to devirtualize: " << *AI);
|
||||
|
||||
// Devirtualize apply instructions that call witness_method instructions:
|
||||
//
|
||||
// %8 = witness_method $Optional<UInt16>, #LogicValue.boolValue!getter.1
|
||||
// %9 = apply %8<Self = CodeUnit?>(%6#1) : ...
|
||||
//
|
||||
if (auto *AMI = dyn_cast<WitnessMethodInst>(AI->getCallee()))
|
||||
return devirtualizeWitnessMethod(AI, AMI);
|
||||
|
||||
/// Optimize a class_method and alloc_ref pair into a direct function
|
||||
/// reference:
|
||||
///
|
||||
/// \code
|
||||
/// %XX = alloc_ref $Foo
|
||||
/// %YY = class_method %XX : $Foo, #Foo.get!1 : $@cc(method) @thin ...
|
||||
/// \endcode
|
||||
///
|
||||
/// or
|
||||
///
|
||||
/// %XX = metatype $...
|
||||
/// %YY = class_method %XX : ...
|
||||
///
|
||||
/// into
|
||||
///
|
||||
/// %YY = function_ref @...
|
||||
if (auto *CMI = dyn_cast<ClassMethodInst>(AI->getCallee())) {
|
||||
// Check if the class member is known to be final.
|
||||
if (ClassDecl *C = getClassFromAccessControl(CMI))
|
||||
return devirtualizeClassMethod(AI, CMI->getMember(), CMI->getOperand(),
|
||||
C);
|
||||
|
||||
// Try to search for the point of construction.
|
||||
if (ClassDecl *C = getClassFromConstructor(CMI->getOperand()))
|
||||
return devirtualizeClassMethod(AI, CMI->getMember(),
|
||||
CMI->getOperand().stripUpCasts(), C);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
STATISTIC(NumInlineCaches, "Number of monomorphic inline caches inserted");
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user