/// 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); }
/// 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 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; }
/// 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); }
/// 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(); }