/// Insert monomorphic inline caches for a specific class or metatype /// type \p SubClassTy. static FullApplySite speculateMonomorphicTarget(FullApplySite AI, SILType SubType, CheckedCastBranchInst *&CCBI) { CCBI = nullptr; // Bail if this class_method cannot be devirtualized. if (!canDevirtualizeClassMethod(AI, SubType)) return FullApplySite(); if (SubType.getSwiftRValueType()->hasDynamicSelfType()) return FullApplySite(); // Create a diamond shaped control flow and a checked_cast_branch // instruction that checks the exact type of the object. // This cast selects between two paths: one that calls the slow dynamic // dispatch and one that calls the specific method. auto It = AI.getInstruction()->getIterator(); SILFunction *F = AI.getFunction(); SILBasicBlock *Entry = AI.getParent(); // Iden is the basic block containing the direct call. SILBasicBlock *Iden = F->createBasicBlock(); // Virt is the block containing the slow virtual call. SILBasicBlock *Virt = F->createBasicBlock(); Iden->createPHIArgument(SubType, ValueOwnershipKind::Owned); SILBasicBlock *Continue = Entry->split(It); SILBuilderWithScope Builder(Entry, AI.getInstruction()); // Create the checked_cast_branch instruction that checks at runtime if the // class instance is identical to the SILType. ClassMethodInst *CMI = cast<ClassMethodInst>(AI.getCallee()); CCBI = Builder.createCheckedCastBranch(AI.getLoc(), /*exact*/ true, CMI->getOperand(), SubType, Iden, Virt); It = CCBI->getIterator(); SILBuilderWithScope VirtBuilder(Virt, AI.getInstruction()); SILBuilderWithScope IdenBuilder(Iden, AI.getInstruction()); // This is the class reference downcasted into subclass SubType. SILValue DownCastedClassInstance = Iden->getArgument(0); // Copy the two apply instructions into the two blocks. FullApplySite IdenAI = CloneApply(AI, IdenBuilder); FullApplySite VirtAI = CloneApply(AI, VirtBuilder); // See if Continue has a release on self as the instruction right after the // apply. If it exists, move it into position in the diamond. SILBasicBlock::iterator next = next_or_end(Continue->begin(), Continue->end()); auto *Release = (next == Continue->end()) ? nullptr : dyn_cast<StrongReleaseInst>(next); if (Release && Release->getOperand() == CMI->getOperand()) { VirtBuilder.createStrongRelease(Release->getLoc(), CMI->getOperand(), Release->getAtomicity()); IdenBuilder.createStrongRelease(Release->getLoc(), DownCastedClassInstance, Release->getAtomicity()); Release->eraseFromParent(); } // Create a PHInode for returning the return value from both apply // instructions. SILArgument *Arg = Continue->createPHIArgument(AI.getType(), ValueOwnershipKind::Owned); if (!isa<TryApplyInst>(AI)) { if (AI.getSubstCalleeType()->isNoReturnFunction()) { IdenBuilder.createUnreachable(AI.getLoc()); VirtBuilder.createUnreachable(AI.getLoc()); } else { IdenBuilder.createBranch(AI.getLoc(), Continue, { cast<ApplyInst>(IdenAI) }); VirtBuilder.createBranch(AI.getLoc(), Continue, { cast<ApplyInst>(VirtAI) }); } } // Remove the old Apply instruction. assert(AI.getInstruction() == &Continue->front() && "AI should be the first instruction in the split Continue block"); if (isa<TryApplyInst>(AI)) { AI.getInstruction()->eraseFromParent(); assert(Continue->empty() && "There should not be an instruction after try_apply"); Continue->eraseFromParent(); } else { auto apply = cast<ApplyInst>(AI); apply->replaceAllUsesWith(Arg); apply->eraseFromParent(); assert(!Continue->empty() && "There should be at least a terminator after AI"); } // Update the stats. NumTargetsPredicted++; // Devirtualize the apply instruction on the identical path. auto NewInstPair = devirtualizeClassMethod(IdenAI, DownCastedClassInstance); assert(NewInstPair.first && "Expected to be able to devirtualize apply!"); replaceDeadApply(IdenAI, NewInstPair.first); // Split critical edges resulting from VirtAI. if (auto *TAI = dyn_cast<TryApplyInst>(VirtAI)) { auto *ErrorBB = TAI->getFunction()->createBasicBlock(); ErrorBB->createPHIArgument(TAI->getErrorBB()->getArgument(0)->getType(), ValueOwnershipKind::Owned); Builder.setInsertionPoint(ErrorBB); Builder.createBranch(TAI->getLoc(), TAI->getErrorBB(), {ErrorBB->getArgument(0)}); auto *NormalBB = TAI->getFunction()->createBasicBlock(); NormalBB->createPHIArgument(TAI->getNormalBB()->getArgument(0)->getType(), ValueOwnershipKind::Owned); Builder.setInsertionPoint(NormalBB); Builder.createBranch(TAI->getLoc(), TAI->getNormalBB(), {NormalBB->getArgument(0)}); Builder.setInsertionPoint(VirtAI.getInstruction()); SmallVector<SILValue, 4> Args; for (auto Arg : VirtAI.getArguments()) { Args.push_back(Arg); } FullApplySite NewVirtAI = Builder.createTryApply(VirtAI.getLoc(), VirtAI.getCallee(), VirtAI.getSubstitutions(), Args, NormalBB, ErrorBB); VirtAI.getInstruction()->eraseFromParent(); VirtAI = NewVirtAI; } return VirtAI; }
// Start with the substitutions from the apply. // Try to propagate them to find out the real substitutions required // to invoke the method. static void getSubstitutionsForCallee(SILModule &M, CanSILFunctionType baseCalleeType, CanType derivedSelfType, FullApplySite AI, SmallVectorImpl<Substitution> &newSubs) { // If the base method is not polymorphic, no substitutions are required, // even if we originally had substitutions for calling the derived method. if (!baseCalleeType->isPolymorphic()) return; auto derivedClass = derivedSelfType; if (auto metatypeType = dyn_cast<MetatypeType>(derivedClass)) derivedClass = CanType(metatypeType->getInstanceType()); SubstitutionMap subMap; if (auto origCalleeSig = AI.getOrigCalleeType()->getGenericSignature()) { auto calleeSelfType = AI.getSubstCalleeType()->getSelfParameter().getType(); if (auto metatypeType = dyn_cast<MetatypeType>(calleeSelfType)) calleeSelfType = CanType(metatypeType->getInstanceType()); auto *calleeClassDecl = calleeSelfType->getClassOrBoundGenericClass(); assert(calleeClassDecl && "self is not a class type"); auto origSubs = AI.getSubstitutions(); // Add generic parameters from the method itself, ignoring any generic // parameters from the derived class. unsigned minDepth = 0; if (auto derivedClassSig = calleeClassDecl->getGenericSignatureOfContext()) minDepth = derivedClassSig->getGenericParams().back()->getDepth() + 1; for (auto depTy : origCalleeSig->getAllDependentTypes()) { // Grab the next substitution. auto sub = origSubs.front(); origSubs = origSubs.slice(1); // If the dependent type doesn't contain any generic parameter with // a depth of at least the minimum, skip this type. auto canTy = depTy->getCanonicalType(); auto hasInnerGenericParameter = [minDepth](Type type) -> bool { if (auto gp = type->getAs<GenericTypeParamType>()) { return gp->getDepth() >= minDepth; } return false; }; if (!Type(canTy.getPointer()).findIf(hasInnerGenericParameter)) continue; // Otherwise, record the replacement and conformances for the mapped // type. subMap.addSubstitution(canTy, sub.getReplacement()); subMap.addConformances(canTy, sub.getConformances()); } assert(origSubs.empty()); } // Add any generic substitutions for the base class. auto baseSelfType = baseCalleeType->getSelfParameter().getType(); if (auto metatypeType = dyn_cast<MetatypeType>(baseSelfType)) baseSelfType = CanType(metatypeType->getInstanceType()); auto *baseClassDecl = baseSelfType.getClassOrBoundGenericClass(); assert(baseClassDecl && "not a class method"); if (auto baseClassSig = baseClassDecl->getGenericSignatureOfContext()) { // Compute the type of the base class, starting from the // derived class type and the type of the method's self // parameter. auto baseClass = derivedClass->getSuperclassForDecl(baseClassDecl, nullptr) ->getCanonicalType(); auto baseClassSubs = baseClass->gatherAllSubstitutions( M.getSwiftModule(), nullptr); // Decompose the base class substitutions, adding them to the same // substitution maps as above. baseClassSig->getSubstitutionMap(baseClassSubs, subMap); } // Build the new substitutions using the base method signature. auto baseCalleeSig = baseCalleeType->getGenericSignature(); baseCalleeSig->getSubstitutions(subMap, newSubs); }