static void getWitnessMethodSubstitutions(ApplySite AI, SILFunction *F, ArrayRef<Substitution> Subs, SmallVectorImpl<Substitution> &NewSubs) { auto &Module = AI.getModule(); auto CalleeCanType = F->getLoweredFunctionType(); ProtocolDecl *proto = nullptr; if (CalleeCanType->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod) { proto = CalleeCanType->getDefaultWitnessMethodProtocol( *Module.getSwiftModule()); } ArrayRef<Substitution> origSubs = AI.getSubstitutions(); if (proto != nullptr) { // If the callee is a default witness method thunk, preserve substitutions // from the call site. NewSubs.append(origSubs.begin(), origSubs.end()); return; } // If the callee is a concrete witness method thunk, apply substitutions // from the conformance, and drop any substitutions derived from the Self // type. NewSubs.append(Subs.begin(), Subs.end()); if (auto generics = AI.getOrigCalleeType()->getGenericSignature()) { for (auto genericParam : generics->getAllDependentTypes()) { auto origSub = origSubs.front(); origSubs = origSubs.slice(1); // If the callee is a concrete witness method thunk, we ignore // generic parameters derived from 'self', the generic parameter at // depth 0, index 0. auto type = genericParam->getCanonicalType(); while (auto memberType = dyn_cast<DependentMemberType>(type)) { type = memberType.getBase(); } auto paramType = cast<GenericTypeParamType>(type); if (paramType->getDepth() == 0) { // There shouldn't be any other parameters at this depth. assert(paramType->getIndex() == 0); continue; } // Okay, remember this substitution. NewSubs.push_back(origSub); } } assert(origSubs.empty() && "subs not parallel to dependent types"); }
/// 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. ApplySite swift::tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE) { if (!canDevirtualizeWitnessMethod(AI)) return ApplySite(); SILFunction *F; SILWitnessTable *WT; auto *WMI = cast<WitnessMethodInst>(AI.getCallee()); std::tie(F, WT) = AI.getModule().lookUpFunctionInWitnessTable(WMI->getConformance(), WMI->getMember()); return devirtualizeWitnessMethod(AI, F, WMI->getConformance(), ORE); }
/// 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. DevirtualizationResult swift::tryDevirtualizeWitnessMethod(ApplySite AI) { if (!canDevirtualizeWitnessMethod(AI)) return std::make_pair(nullptr, FullApplySite()); SILFunction *F; SILWitnessTable *WT; auto *WMI = cast<WitnessMethodInst>(AI.getCallee()); std::tie(F, WT) = AI.getModule().lookUpFunctionInWitnessTable(WMI->getConformance(), WMI->getMember()); auto Result = devirtualizeWitnessMethod(AI, F, WMI->getConformance()); return std::make_pair(Result.getInstruction(), Result); }
/// Find the concrete type of the existential argument. Wrapper /// for findInitExistential in Local.h/cpp. In future, this code /// can move to Local.h/cpp. static bool findConcreteType(ApplySite AI, int ArgIdx, CanType &ConcreteType) { bool isCopied = false; auto Arg = AI.getArgument(ArgIdx); /// Ignore any unconditional cast instructions. Is it Safe? Do we need to /// also add UnconditionalCheckedCastAddrInst? TODO. if (auto *Instance = dyn_cast<UnconditionalCheckedCastInst>(Arg)) { Arg = Instance->getOperand(); } /// Return init_existential if the Arg is global_addr. if (auto *GAI = dyn_cast<GlobalAddrInst>(Arg)) { SILValue InitExistential = findInitExistentialFromGlobalAddrAndApply(GAI, AI, ArgIdx); /// If the Arg is already init_existential, return the concrete type. if (InitExistential && findConcreteTypeFromInitExistential(InitExistential, ConcreteType)) { return true; } } /// Handle AllocStack instruction separately. if (auto *Instance = dyn_cast<AllocStackInst>(Arg)) { if (SILValue Src = getAddressOfStackInit(Instance, AI.getInstruction(), isCopied)) { Arg = Src; } } /// If the Arg is already init_existential after getAddressofStackInit /// call, return the concrete type. if (findConcreteTypeFromInitExistential(Arg, ConcreteType)) { return true; } /// Call findInitExistential and determine the init_existential. ArchetypeType *OpenedArchetype = nullptr; SILValue OpenedArchetypeDef; auto FAS = FullApplySite::isa(AI.getInstruction()); if (!FAS) return false; SILInstruction *InitExistential = findInitExistential(FAS.getArgumentOperands()[ArgIdx], OpenedArchetype, OpenedArchetypeDef, isCopied); if (!InitExistential) { LLVM_DEBUG(llvm::dbgs() << "ExistentialSpecializer Pass: Bail! Due to " "findInitExistential\n";);
ApplySite SILPerformanceInliner::specializeGeneric( ApplySite Apply, llvm::SmallVectorImpl<ApplySite> &NewApplies) { assert(NewApplies.empty() && "Expected out parameter for new applies!"); if (!Apply.hasSubstitutions()) return ApplySite(); auto *Callee = Apply.getCalleeFunction(); if (!Callee || Callee->isExternalDeclaration()) return ApplySite(); auto Filter = [](SILInstruction *I) -> bool { return ApplySite::isa(I) != ApplySite(); }; CloneCollector Collector(Filter); SILFunction *SpecializedFunction; auto Specialized = trySpecializeApplyOfGeneric(Apply, SpecializedFunction, Collector); if (!Specialized) return ApplySite(); // Track the new applies from the specialization. for (auto NewCallSite : Collector.getInstructionPairs()) NewApplies.push_back(ApplySite(NewCallSite.first)); auto FullApply = FullApplySite::isa(Apply.getInstruction()); if (!FullApply) { assert(!FullApplySite::isa(Specialized.getInstruction()) && "Unexpected full apply generated!"); // Replace the old apply with the new and delete the old. replaceDeadApply(Apply, Specialized.getInstruction()); return ApplySite(Specialized); } // Replace the old apply with the new and delete the old. replaceDeadApply(Apply, Specialized.getInstruction()); return Specialized; }
/// 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 ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F, ProtocolConformanceRef C, OptRemark::Emitter *ORE) { // 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. auto SubMap = getWitnessMethodSubstitutions(Module, AI, F, C); // 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, SubMap); // Collect arguments from the apply instruction. auto Arguments = SmallVector<SILValue, 4>(); // Iterate over the non self arguments and add them to the // new argument list, upcasting when required. SILBuilderWithScope B(AI.getInstruction()); SILFunctionConventions substConv(SubstCalleeCanType, Module); unsigned substArgIdx = AI.getCalleeArgIndexOfFirstAppliedArg(); for (auto arg : AI.getArguments()) { auto paramType = substConv.getSILArgumentType(substArgIdx++); if (arg->getType() != paramType) arg = castValueToABICompatibleType(&B, AI.getLoc(), arg, arg->getType(), paramType); Arguments.push_back(arg); } assert(substArgIdx == substConv.getNumSILArguments()); // Replace old apply instruction by a new apply instruction that invokes // the witness thunk. SILBuilderWithScope Builder(AI.getInstruction()); SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); ApplySite SAI = replaceApplySite(Builder, Loc, AI, FRI, SubMap, Arguments, substConv); if (ORE) ORE->emit([&]() { using namespace OptRemark; return RemarkPassed("WitnessMethodDevirtualized", *AI.getInstruction()) << "Devirtualized call to " << NV("Method", F); }); NumWitnessDevirt++; return SAI; }
/// Recursively check for conflicts with in-progress accesses at the given /// apply. /// /// Any captured variable accessed by a noescape closure is considered to be /// accessed at the point that the closure is fully applied. This includes /// variables captured by address by the noescape closure being applied or by /// any other noescape closure that is itself passed as an argument to that /// closure. /// /// (1) Use AccessSummaryAnalysis to check each argument for statically /// enforced accesses nested within the callee. /// /// (2) If an applied argument is itself a function type, recursively check for /// violations on the closure being passed as an argument. /// /// (3) Walk up the chain of partial applies to recursively visit all arguments. /// /// Note: This handles closures that are called immediately: /// var i = 7 /// ({ (p: inout Int) in i = 8})(&i) // Overlapping access to 'i' /// /// Note: This handles chains of partial applies: /// pa1 = partial_apply f(c) : $(a, b, c) /// pa2 = partial_apply pa1(b) : $(a, b) /// apply pa2(a) static void checkForViolationAtApply(ApplySite Apply, AccessState &State) { // First, check access to variables immediately captured at this apply site. checkCaptureAccess(Apply, State); // Next, recursively check any noescape closures passed as arguments at this // apply site. TinyPtrVector<PartialApplyInst *> partialApplies; for (SILValue Argument : Apply.getArguments()) { auto FnType = getSILFunctionTypeForValue(Argument); if (!FnType || !FnType->isNoEscape()) continue; findClosuresForFunctionValue(Argument, partialApplies); } // Continue recursively walking up the chain of applies if necessary. findClosuresForFunctionValue(Apply.getCallee(), partialApplies); for (auto *PAI : partialApplies) checkForViolationAtApply(ApplySite(PAI), State); }
static void getWitnessMethodSubstitutions(ApplySite AI, SILFunction *F, ProtocolConformanceRef CRef, SmallVectorImpl<Substitution> &NewSubs) { auto &Module = AI.getModule(); auto requirementSig = AI.getOrigCalleeType()->getGenericSignature(); auto witnessThunkSig = F->getLoweredFunctionType()->getGenericSignature(); SubstitutionList origSubs = AI.getSubstitutions(); bool isDefaultWitness = F->getLoweredFunctionType()->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod && F->getLoweredFunctionType()->getDefaultWitnessMethodProtocol( *Module.getSwiftModule()) == CRef.getRequirement(); getWitnessMethodSubstitutions(Module, CRef, requirementSig, witnessThunkSig, origSubs, isDefaultWitness, NewSubs); }
/// Check if we can pass/convert all arguments of the original apply /// as required by the found devirtualized method. static bool canPassOrConvertAllArguments(ApplySite AI, CanSILFunctionType SubstCalleeCanType) { for (unsigned ArgN = 0, ArgE = AI.getNumArguments(); ArgN != ArgE; ++ArgN) { SILValue A = AI.getArgument(ArgN); auto ParamType = SubstCalleeCanType->getSILArgumentType( SubstCalleeCanType->getNumSILArguments() - AI.getNumArguments() + ArgN); // Check if we can cast the provided argument into the required // parameter type. auto FromTy = A->getType(); auto ToTy = ParamType; // If types are the same, no conversion will be required. if (FromTy == ToTy) continue; // Otherwise, it should be possible to upcast the arguments. if (!isLegalUpcast(FromTy, ToTy)) return false; } return true; }
/// For each argument in the range of the callee arguments being applied at the /// given apply site, use the summary analysis to determine whether the /// arguments will be accessed in a way that conflicts with any currently in /// progress accesses. If so, diagnose. static void checkCaptureAccess(ApplySite Apply, AccessState &State) { SILFunction *Callee = Apply.getCalleeFunction(); if (!Callee || Callee->empty()) return; const AccessSummaryAnalysis::FunctionSummary &FS = State.ASA->getOrCreateSummary(Callee); for (unsigned ArgumentIndex : range(Apply.getNumArguments())) { unsigned CalleeIndex = Apply.getCalleeArgIndexOfFirstAppliedArg() + ArgumentIndex; const AccessSummaryAnalysis::ArgumentSummary &AS = FS.getAccessForArgument(CalleeIndex); const auto &SubAccesses = AS.getSubAccesses(); // Is the capture accessed in the callee? if (SubAccesses.empty()) continue; SILValue Argument = Apply.getArgument(ArgumentIndex); assert(Argument->getType().isAddress()); // A valid AccessedStorage should alway sbe found because Unsafe accesses // are not tracked by AccessSummaryAnalysis. const AccessedStorage &Storage = findValidAccessedStorage(Argument); auto AccessIt = State.Accesses->find(Storage); // Are there any accesses in progress at the time of the call? if (AccessIt == State.Accesses->end()) continue; const AccessInfo &Info = AccessIt->getSecond(); if (auto Conflict = findConflictingArgumentAccess(AS, Storage, Info)) State.ConflictingAccesses.push_back(*Conflict); } }
static bool canDevirtualizeWitnessMethod(ApplySite AI) { SILFunction *F; SILWitnessTable *WT; auto *WMI = cast<WitnessMethodInst>(AI.getCallee()); std::tie(F, WT) = AI.getModule().lookUpFunctionInWitnessTable(WMI->getConformance(), WMI->getMember()); if (!F) return false; if (AI.getFunction()->isSerialized()) { // function_ref inside fragile function cannot reference a private or // hidden symbol. if (!F->hasValidLinkageForFragileRef()) return false; } return true; }
static SubstitutionMap getWitnessMethodSubstitutions(SILModule &Module, ApplySite AI, SILFunction *F, ProtocolConformanceRef CRef) { auto witnessFnTy = F->getLoweredFunctionType(); assert(witnessFnTy->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod); auto requirementSig = AI.getOrigCalleeType()->getGenericSignature(); auto witnessThunkSig = witnessFnTy->getGenericSignature(); SubstitutionMap origSubs = AI.getSubstitutionMap(); auto *mod = Module.getSwiftModule(); bool isDefaultWitness = (witnessFnTy->getDefaultWitnessMethodProtocol() == CRef.getRequirement()); auto *classWitness = witnessFnTy->getWitnessMethodClass(*mod); return getWitnessMethodSubstitutions( mod, CRef, requirementSig, witnessThunkSig, origSubs, isDefaultWitness, classWitness); }
/// 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. DevirtualizationResult swift::tryDevirtualizeWitnessMethod(ApplySite AI) { SILFunction *F; SILWitnessTable *WT; auto *WMI = cast<WitnessMethodInst>(AI.getCallee()); std::tie(F, WT) = AI.getModule().lookUpFunctionInWitnessTable(WMI->getConformance(), WMI->getMember()); if (!F) return std::make_pair(nullptr, FullApplySite()); if (AI.getFunction()->isFragile()) { // function_ref inside fragile function cannot reference a private or // hidden symbol. if (!F->hasValidLinkageForFragileRef()) return std::make_pair(nullptr, FullApplySite()); } auto Result = devirtualizeWitnessMethod(AI, F, WMI->getConformance()); return std::make_pair(Result.getInstruction(), Result); }
/// Helper for visitApplyAccesses that visits address-type call arguments, /// including arguments to @noescape functions that are passed as closures to /// the current call. static void visitApplyAccesses(ApplySite apply, llvm::function_ref<void(Operand *)> visitor) { for (Operand &oper : apply.getArgumentOperands()) { // Consider any address-type operand an access. Whether it is read or modify // depends on the argument convention. if (oper.get()->getType().isAddress()) { visitor(&oper); continue; } auto fnType = oper.get()->getType().getAs<SILFunctionType>(); if (!fnType || !fnType->isNoEscape()) continue; // When @noescape function closures are passed as arguments, their // arguments are considered accessed at the call site. TinyPtrVector<PartialApplyInst *> partialApplies; findClosuresForFunctionValue(oper.get(), partialApplies); // Recursively visit @noescape function closure arguments. for (auto *PAI : partialApplies) visitApplyAccesses(PAI, visitor); } }
/// Helper for visitApplyAccesses that visits address-type call arguments, /// including arguments to @noescape functions that are passed as closures to /// the current call. static void visitApplyAccesses(ApplySite apply, llvm::function_ref<void(Operand *)> visitor) { for (Operand &oper : apply.getArgumentOperands()) { // Consider any address-type operand an access. Whether it is read or modify // depends on the argument convention. if (oper.get()->getType().isAddress()) { visitor(&oper); continue; } auto fnType = oper.get()->getType().getAs<SILFunctionType>(); if (!fnType || !fnType->isNoEscape()) continue; // When @noescape function closures are passed as arguments, their // arguments are considered accessed at the call site. FindClosureResult result = findClosureForAppliedArg(oper.get()); if (!result.PAI) continue; // Recursively visit @noescape function closure arguments. visitApplyAccesses(result.PAI, visitor); } }
bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) { DeadInstructionSet DeadApplies; llvm::SmallSetVector<SILInstruction *, 8> Applies; bool Changed = false; for (auto &BB : F) { // Collect the applies for this block in reverse order so that we // can pop them off the end of our vector and process them in // forward order. for (auto It = BB.rbegin(), End = BB.rend(); It != End; ++It) { auto *I = &*It; // Skip non-apply instructions, apply instructions with no // substitutions, apply instructions where we do not statically // know the called function, and apply instructions where we do // not have the body of the called function. ApplySite Apply = ApplySite::isa(I); if (!Apply || !Apply.hasSubstitutions()) continue; auto *Callee = Apply.getReferencedFunction(); if (!Callee || !Callee->isDefinition()) continue; Applies.insert(Apply.getInstruction()); } // Attempt to specialize each apply we collected, deleting any // that we do specialize (along with other instructions we clone // in the process of doing so). We pop from the end of the list to // avoid tricky iterator invalidation issues. while (!Applies.empty()) { auto *I = Applies.pop_back_val(); auto Apply = ApplySite::isa(I); assert(Apply && "Expected an apply!"); SILFunction *Callee = Apply.getReferencedFunction(); assert(Callee && "Expected to have a known callee"); // We have a call that can potentially be specialized, so // attempt to do so. llvm::SmallVector<SILFunction *, 2> NewFunctions; trySpecializeApplyOfGeneric(Apply, DeadApplies, NewFunctions); // Remove all the now-dead applies. We must do this immediately // rather than defer it in order to avoid problems with cloning // dead instructions when doing recursive specialization. while (!DeadApplies.empty()) { auto *AI = DeadApplies.pop_back_val(); // Remove any applies we are deleting so that we don't attempt // to specialize them. Applies.remove(AI); recursivelyDeleteTriviallyDeadInstructions(AI, true); Changed = true; } // If calling the specialization utility resulted in new functions // (as opposed to returning a previous specialization), we need to notify // the pass manager so that the new functions get optimized. for (SILFunction *NewF : reverse(NewFunctions)) { notifyPassManagerOfFunction(NewF, Callee); } } } return Changed; }
/// 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 ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F, ProtocolConformanceRef C) { // 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, 4> NewSubs; getWitnessMethodSubstitutions(AI, F, C, NewSubs); // 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, NewSubs); // Bail if some of the arguments cannot be converted into // types required by the found devirtualized method. if (!canPassOrConvertAllArguments(AI, SubstCalleeCanType)) return ApplySite(); // Collect arguments from the apply instruction. auto Arguments = SmallVector<SILValue, 4>(); // Iterate over the non self arguments and add them to the // new argument list, upcasting when required. SILBuilderWithScope B(AI.getInstruction()); for (unsigned ArgN = 0, ArgE = AI.getNumArguments(); ArgN != ArgE; ++ArgN) { SILValue A = AI.getArgument(ArgN); auto ParamType = SubstCalleeCanType->getSILArgumentType( SubstCalleeCanType->getNumSILArguments() - AI.getNumArguments() + ArgN); if (A->getType() != ParamType) A = B.createUpcast(AI.getLoc(), A, ParamType); Arguments.push_back(A); } // Replace old apply instruction by a new apply instruction that invokes // the witness thunk. SILBuilderWithScope Builder(AI.getInstruction()); SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); auto SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeCanType); auto ResultSILType = SubstCalleeCanType->getSILResult(); ApplySite SAI; if (auto *A = dyn_cast<ApplyInst>(AI)) SAI = Builder.createApply(Loc, FRI, SubstCalleeSILType, ResultSILType, NewSubs, Arguments, A->isNonThrowing()); if (auto *TAI = dyn_cast<TryApplyInst>(AI)) SAI = Builder.createTryApply(Loc, FRI, SubstCalleeSILType, NewSubs, Arguments, TAI->getNormalBB(), TAI->getErrorBB()); if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) SAI = Builder.createPartialApply(Loc, FRI, SubstCalleeSILType, NewSubs, Arguments, PAI->getType()); NumWitnessDevirt++; return SAI; }
/// \brief Devirtualize an apply of a class method. /// /// \p AI is the apply to devirtualize. /// \p ClassOrMetatype is a class value or metatype value that is the /// self argument of the apply we will devirtualize. /// return the result value of the new ApplyInst if created one or null. FullApplySite swift::devirtualizeClassMethod(FullApplySite AI, SILValue ClassOrMetatype, OptRemark::Emitter *ORE) { LLVM_DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI.getInstruction()); SILModule &Mod = AI.getModule(); auto *MI = cast<MethodInst>(AI.getCallee()); auto ClassOrMetatypeType = ClassOrMetatype->getType(); auto *F = getTargetClassMethod(Mod, ClassOrMetatypeType, MI); CanSILFunctionType GenCalleeType = F->getLoweredFunctionType(); SubstitutionMap Subs = getSubstitutionsForCallee(Mod, GenCalleeType, ClassOrMetatypeType.getASTType(), AI); CanSILFunctionType SubstCalleeType = GenCalleeType; if (GenCalleeType->isPolymorphic()) SubstCalleeType = GenCalleeType->substGenericArgs(Mod, Subs); SILFunctionConventions substConv(SubstCalleeType, Mod); SILBuilderWithScope B(AI.getInstruction()); SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = B.createFunctionRef(Loc, F); // Create the argument list for the new apply, casting when needed // in order to handle covariant indirect return types and // contravariant argument types. llvm::SmallVector<SILValue, 8> NewArgs; auto IndirectResultArgIter = AI.getIndirectSILResults().begin(); for (auto ResultTy : substConv.getIndirectSILResultTypes()) { NewArgs.push_back( castValueToABICompatibleType(&B, Loc, *IndirectResultArgIter, IndirectResultArgIter->getType(), ResultTy)); ++IndirectResultArgIter; } auto ParamArgIter = AI.getArgumentsWithoutIndirectResults().begin(); // Skip the last parameter, which is `self`. Add it below. for (auto param : substConv.getParameters().drop_back()) { auto paramType = substConv.getSILType(param); NewArgs.push_back( castValueToABICompatibleType(&B, Loc, *ParamArgIter, ParamArgIter->getType(), paramType)); ++ParamArgIter; } // Add the self argument, upcasting if required because we're // calling a base class's method. auto SelfParamTy = substConv.getSILType(SubstCalleeType->getSelfParameter()); NewArgs.push_back(castValueToABICompatibleType(&B, Loc, ClassOrMetatype, ClassOrMetatypeType, SelfParamTy)); ApplySite NewAS = replaceApplySite(B, Loc, AI, FRI, Subs, NewArgs, substConv); FullApplySite NewAI = FullApplySite::isa(NewAS.getInstruction()); assert(NewAI); LLVM_DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n"); if (ORE) ORE->emit([&]() { using namespace OptRemark; return RemarkPassed("ClassMethodDevirtualized", *AI.getInstruction()) << "Devirtualized call to class method " << NV("Method", F); }); NumClassDevirt++; return NewAI; }
/// Delete an apply site that's been successfully devirtualized. void swift::deleteDevirtualizedApply(ApplySite Old) { auto *OldApply = Old.getInstruction(); recursivelyDeleteTriviallyDeadInstructions(OldApply, true); }
/// Attempt to devirtualize the given apply if possible, and return a /// new instruction in that case, or nullptr otherwise. ApplySite swift::tryDevirtualizeApply(ApplySite AI, ClassHierarchyAnalysis *CHA, OptRemark::Emitter *ORE) { LLVM_DEBUG(llvm::dbgs() << " Trying to devirtualize: " << *AI.getInstruction()); // 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 (isa<WitnessMethodInst>(AI.getCallee())) return tryDevirtualizeWitnessMethod(AI, ORE); // TODO: check if we can also de-virtualize partial applies of class methods. FullApplySite FAS = FullApplySite::isa(AI.getInstruction()); if (!FAS) return ApplySite(); /// 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 : $@convention(method)... /// \endcode /// /// or /// /// %XX = metatype $... /// %YY = class_method %XX : ... /// /// into /// /// %YY = function_ref @... if (auto *CMI = dyn_cast<ClassMethodInst>(FAS.getCallee())) { auto &M = FAS.getModule(); auto Instance = stripUpCasts(CMI->getOperand()); auto ClassType = Instance->getType(); if (ClassType.is<MetatypeType>()) ClassType = ClassType.getMetatypeInstanceType(M); auto *CD = ClassType.getClassOrBoundGenericClass(); if (isEffectivelyFinalMethod(FAS, ClassType, CD, CHA)) return tryDevirtualizeClassMethod(FAS, Instance, ORE, true /*isEffectivelyFinalMethod*/); // Try to check if the exact dynamic type of the instance is statically // known. if (auto Instance = getInstanceWithExactDynamicType(CMI->getOperand(), CMI->getModule(), CHA)) return tryDevirtualizeClassMethod(FAS, Instance, ORE); if (auto ExactTy = getExactDynamicType(CMI->getOperand(), CMI->getModule(), CHA)) { if (ExactTy == CMI->getOperand()->getType()) return tryDevirtualizeClassMethod(FAS, CMI->getOperand(), ORE); } } if (isa<SuperMethodInst>(FAS.getCallee())) { if (FAS.hasSelfArgument()) { return tryDevirtualizeClassMethod(FAS, FAS.getSelfArgument(), ORE); } // It is an invocation of a class method. // Last operand is the metatype that should be used for dispatching. return tryDevirtualizeClassMethod(FAS, FAS.getArguments().back(), ORE); } return ApplySite(); }
bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) { bool Changed = false; llvm::SmallVector<SILInstruction *, 8> DeadApplies; for (auto &BB : F) { for (auto It = BB.begin(), End = BB.end(); It != End;) { auto &I = *It++; // Skip non-apply instructions, apply instructions with no // substitutions, apply instructions where we do not statically // know the called function, and apply instructions where we do // not have the body of the called function. ApplySite Apply = ApplySite::isa(&I); if (!Apply || !Apply.hasSubstitutions()) continue; auto *Callee = Apply.getCalleeFunction(); if (!Callee || !Callee->isDefinition()) continue; // We have a call that can potentially be specialized, so // attempt to do so. // The specializer helper function currently expects a collector // argument, but we aren't going to make use of the results so // we'll have our filter always return false; auto Filter = [](SILInstruction *I) -> bool { return false; }; CloneCollector Collector(Filter); SILFunction *SpecializedFunction; auto Specialized = trySpecializeApplyOfGeneric(Apply, SpecializedFunction, Collector); if (Specialized) { Changed = true; // If calling the specialization utility resulted in a new // function (as opposed to returning a previous // specialization), we need to notify the pass manager so that // the new function gets optimized. if (SpecializedFunction) notifyPassManagerOfFunction(SpecializedFunction); auto *AI = Apply.getInstruction(); if (!isa<TryApplyInst>(AI)) AI->replaceAllUsesWith(Specialized.getInstruction()); DeadApplies.push_back(AI); } } } // Remove all the now-dead applies. while (!DeadApplies.empty()) { auto *AI = DeadApplies.pop_back_val(); recursivelyDeleteTriviallyDeadInstructions(AI, true); } return Changed; }
/// 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 ApplySite devirtualizeWitnessMethod(ApplySite 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()); if (auto generics = AI.getOrigCalleeType()->getGenericSignature()) { ArrayRef<Substitution> origSubs = AI.getSubstitutions(); for (auto genericParam : generics->getAllDependentTypes()) { auto origSub = origSubs.front(); origSubs = origSubs.slice(1); // Ignore generic parameters derived from 'self', the generic // parameter at depth 0, index 0. auto type = genericParam->getCanonicalType(); while (auto memberType = dyn_cast<DependentMemberType>(type)) { type = memberType.getBase(); } auto paramType = cast<GenericTypeParamType>(type); if (paramType->getDepth() == 0) { // There shouldn't be any other parameters at this depth. assert(paramType->getIndex() == 0); continue; } // Okay, remember this substitution. NewSubstList.push_back(origSub); } assert(origSubs.empty() && "subs not parallel to dependent types"); } // 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>(); // Iterate over the non self arguments and add them to the // new argument list, upcasting when required. SILBuilderWithScope B(AI.getInstruction()); for (unsigned ArgN = 0, ArgE = AI.getNumArguments(); ArgN != ArgE; ++ArgN) { SILValue A = AI.getArgument(ArgN); auto ParamType = SubstCalleeCanType->getSILArgumentType( SubstCalleeCanType->getNumSILArguments() - AI.getNumArguments() + ArgN); if (A->getType() != ParamType) A = B.createUpcast(AI.getLoc(), A, ParamType); Arguments.push_back(A); } // Replace old apply instruction by a new apply instruction that invokes // the witness thunk. SILBuilderWithScope Builder(AI.getInstruction()); SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); auto SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeCanType); auto ResultSILType = SubstCalleeCanType->getSILResult(); ApplySite SAI; if (auto *A = dyn_cast<ApplyInst>(AI)) SAI = Builder.createApply(Loc, FRI, SubstCalleeSILType, ResultSILType, NewSubstList, Arguments, A->isNonThrowing()); if (auto *TAI = dyn_cast<TryApplyInst>(AI)) SAI = Builder.createTryApply(Loc, FRI, SubstCalleeSILType, NewSubstList, Arguments, TAI->getNormalBB(), TAI->getErrorBB()); if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) SAI = Builder.createPartialApply(Loc, FRI, SubstCalleeSILType, NewSubstList, Arguments, PAI->getType()); NumWitnessDevirt++; return SAI; }
/// 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 ApplySite devirtualizeWitnessMethod(ApplySite 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. ArrayRef<Substitution> SubstList; SubstList = AI.getSubstitutionsWithoutSelfSubstitution(); for (auto &origSub : SubstList) 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(); // Iterate over the non self arguments and add them to the // new argument list, upcasting when required. SILBuilderWithScope B(AI.getInstruction()); for (unsigned ArgN = 0, ArgE = AI.getNumArguments(); ArgN != ArgE; ++ArgN) { SILValue A = AI.getArgument(ArgN); auto ParamType = ParamTypes[ParamTypes.size() - AI.getNumArguments() + ArgN]; if (A.getType() != ParamType) A = B.createUpcast(AI.getLoc(), A, ParamType); Arguments.push_back(A); } // Replace old apply instruction by a new apply instruction that invokes // the witness thunk. SILBuilderWithScope Builder(AI.getInstruction()); SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); auto SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeCanType); auto ResultSILType = SubstCalleeCanType->getSILResult(); ApplySite SAI; if (auto *A = dyn_cast<ApplyInst>(AI)) SAI = Builder.createApply(Loc, FRI, SubstCalleeSILType, ResultSILType, NewSubstList, Arguments, A->isNonThrowing()); if (auto *TAI = dyn_cast<TryApplyInst>(AI)) SAI = Builder.createTryApply(Loc, FRI, SubstCalleeSILType, NewSubstList, Arguments, TAI->getNormalBB(), TAI->getErrorBB()); if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) SAI = Builder.createPartialApply(Loc, FRI, SubstCalleeSILType, NewSubstList, Arguments, PAI->getType()); NumWitnessDevirt++; return SAI; }
/// 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 ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F, ProtocolConformanceRef C) { // 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, 4> NewSubs; getWitnessMethodSubstitutions(AI, F, C, NewSubs); // 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, NewSubs); // Collect arguments from the apply instruction. auto Arguments = SmallVector<SILValue, 4>(); // Iterate over the non self arguments and add them to the // new argument list, upcasting when required. SILBuilderWithScope B(AI.getInstruction()); SILFunctionConventions substConv(SubstCalleeCanType, Module); unsigned substArgIdx = AI.getCalleeArgIndexOfFirstAppliedArg(); for (auto arg : AI.getArguments()) { auto paramType = substConv.getSILArgumentType(substArgIdx++); if (arg->getType() != paramType) arg = castValueToABICompatibleType(&B, AI.getLoc(), arg, arg->getType(), paramType); Arguments.push_back(arg); } assert(substArgIdx == substConv.getNumSILArguments()); // Replace old apply instruction by a new apply instruction that invokes // the witness thunk. SILBuilderWithScope Builder(AI.getInstruction()); SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); auto SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeCanType); auto ResultSILType = substConv.getSILResultType(); ApplySite SAI; if (auto *A = dyn_cast<ApplyInst>(AI)) SAI = Builder.createApply(Loc, FRI, SubstCalleeSILType, ResultSILType, NewSubs, Arguments, A->isNonThrowing()); if (auto *TAI = dyn_cast<TryApplyInst>(AI)) SAI = Builder.createTryApply(Loc, FRI, SubstCalleeSILType, NewSubs, Arguments, TAI->getNormalBB(), TAI->getErrorBB()); if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) SAI = Builder.createPartialApply(Loc, FRI, SubstCalleeSILType, NewSubs, Arguments, PAI->getType()); NumWitnessDevirt++; return SAI; }