From 696da80ca0b51c35a935d2e2e436ef79f5497d4e Mon Sep 17 00:00:00 2001 From: Roman Levenstein Date: Tue, 28 Jul 2015 00:11:38 +0000 Subject: [PATCH] [sil-devirtualizer] Support devirtualization of try_apply instructions. rdar://21909405 Swift SVN r30710 --- include/swift/SIL/SILInstruction.h | 16 ++ include/swift/SILPasses/Utils/Devirtualize.h | 8 +- lib/SILPasses/Devirtualizer.cpp | 129 +++++++++---- lib/SILPasses/Utils/Devirtualize.cpp | 181 ++++++++++++------- lib/SILPasses/Utils/Local.cpp | 3 +- test/SILPasses/devirt_try_apply.swift | 144 +++++++++++++++ 6 files changed, 374 insertions(+), 107 deletions(-) create mode 100644 test/SILPasses/devirt_try_apply.swift diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 96bcabb34ec..5d150105c35 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -3902,6 +3902,11 @@ public: FOREACH_IMPL_RETURN(getCallee()); } + /// Return the type. + SILType getType() const { + FOREACH_IMPL_RETURN(getSubstCalleeType()->getResult().getSILType()); + } + /// Get the type of the callee without the applied substitutions. CanSILFunctionType getOrigCalleeType() const { return getCallee().getType().castTo(); @@ -3939,6 +3944,17 @@ public: FOREACH_IMPL_RETURN(getSubstitutions()); } + ArrayRef getSubstitutionsWithoutSelfSubstitution() const { + switch (Inst->getKind()) { + case ValueKind::ApplyInst: + return cast(Inst)->getSubstitutionsWithoutSelfSubstitution(); + case ValueKind::TryApplyInst: + return cast(Inst)->getSubstitutionsWithoutSelfSubstitution(); + default: + llvm_unreachable("not implemented for this instruction!"); + } + } + /// The arguments passed to this instruction. MutableArrayRef getArgumentOperands() const { FOREACH_IMPL_RETURN(getArgumentOperands()); diff --git a/include/swift/SILPasses/Utils/Devirtualize.h b/include/swift/SILPasses/Utils/Devirtualize.h index aeec87caf65..0624978bf6c 100644 --- a/include/swift/SILPasses/Utils/Devirtualize.h +++ b/include/swift/SILPasses/Utils/Devirtualize.h @@ -31,11 +31,11 @@ namespace swift { -SILInstruction *tryDevirtualizeApply(ApplyInst *AI); +SILInstruction *tryDevirtualizeApply(FullApplySite AI); bool isClassWithUnboundGenericParameters(SILType C, SILModule &M); -bool canDevirtualizeClassMethod(ApplyInst *AI, SILType ClassInstanceType); -SILInstruction *devirtualizeClassMethod(ApplyInst *AI, SILValue ClassInstance); -SILInstruction *tryDevirtualizeClassMethod(ApplyInst *AI, +bool canDevirtualizeClassMethod(FullApplySite AI, SILType ClassInstanceType); +SILInstruction *devirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance); +SILInstruction *tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance); } diff --git a/lib/SILPasses/Devirtualizer.cpp b/lib/SILPasses/Devirtualizer.cpp index 0aace225b55..3670c0b7780 100644 --- a/lib/SILPasses/Devirtualizer.cpp +++ b/lib/SILPasses/Devirtualizer.cpp @@ -71,7 +71,7 @@ public: << "\n"); for (auto &BB : F) { for (auto II = BB.begin(), IE = BB.end(); II != IE;) { - ApplyInst *AI = dyn_cast(&*II); + FullApplySite AI = FullApplySite::isa(&*II); ++II; if (!AI) @@ -109,37 +109,56 @@ SILTransform *swift::createDevirtualizer() { } // A utility function for cloning the apply instruction. -static ApplyInst *CloneApply(ApplyInst *AI, SILBuilder &Builder) { +static FullApplySite CloneApply(FullApplySite AI, SILBuilder &Builder) { // Clone the Apply. - auto Args = AI->getArguments(); + auto Args = AI.getArguments(); SmallVector Ret(Args.size()); for (unsigned i = 0, e = Args.size(); i != e; ++i) Ret[i] = Args[i]; - auto NAI = Builder.createApply(AI->getLoc(), AI->getCallee(), - AI->getSubstCalleeSILType(), - AI->getType(), - AI->getSubstitutions(), - Ret); - NAI->setDebugScope(AI->getDebugScope()); + FullApplySite NAI; + + switch (AI.getInstruction()->getKind()) { + case ValueKind::ApplyInst: + NAI = Builder.createApply(AI.getLoc(), AI.getCallee(), + AI.getSubstCalleeSILType(), + AI.getType(), + AI.getSubstitutions(), + Ret); + break; + case ValueKind::TryApplyInst: { + auto *TryApplyI = cast(AI.getInstruction()); + NAI = Builder.createTryApply(AI.getLoc(), AI.getCallee(), + AI.getSubstCalleeSILType(), + AI.getSubstitutions(), + Ret, + TryApplyI->getNormalBB(), + TryApplyI->getErrorBB()); + } + break; + default: + llvm_unreachable("Trying to clone an unsupported apply instruction"); + } + + NAI.getInstruction()->setDebugScope(AI.getDebugScope()); return NAI; } /// Insert monomorphic inline caches for a specific class or metatype /// type \p SubClassTy. -static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI, - SILType SubType) { +static FullApplySite speculateMonomorphicTarget(FullApplySite AI, + SILType SubType) { // Bail if this class_method cannot be devirtualized. if (!canDevirtualizeClassMethod(AI, SubType)) - return nullptr; + 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. - SILBasicBlock::iterator It = AI; - SILFunction *F = AI->getFunction(); - SILBasicBlock *Entry = AI->getParent(); + SILBasicBlock::iterator It = AI.getInstruction(); + SILFunction *F = AI.getFunction(); + SILBasicBlock *Entry = AI.getParent(); // Iden is the basic block containing the direct call. SILBasicBlock *Iden = F->createBasicBlock(); @@ -149,13 +168,13 @@ static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI, SILBasicBlock *Continue = Entry->splitBasicBlock(It); - SILBuilderWithScope<> Builder(Entry, AI->getDebugScope()); + SILBuilderWithScope<> Builder(Entry, AI.getDebugScope()); // Create the checked_cast_branch instruction that checks at runtime if the // class instance is identical to the SILType. - ClassMethodInst *CMI = cast(AI->getCallee()); + ClassMethodInst *CMI = cast(AI.getCallee()); - It = Builder.createCheckedCastBranch(AI->getLoc(), /*exact*/ true, + It = Builder.createCheckedCastBranch(AI.getLoc(), /*exact*/ true, CMI->getOperand(), SubType, Iden, Virt); @@ -181,8 +200,8 @@ static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI, } // Copy the two apply instructions into the two blocks. - ApplyInst *IdenAI = CloneApply(AI, IdenBuilder); - ApplyInst *VirtAI = CloneApply(AI, VirtBuilder); + 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. @@ -200,15 +219,23 @@ static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI, // Create a PHInode for returning the return value from both apply // instructions. - SILArgument *Arg = Continue->createBBArg(AI->getType()); - IdenBuilder.createBranch(AI->getLoc(), Continue, ArrayRef(IdenAI)) - ->setDebugScope(AI->getDebugScope()); - VirtBuilder.createBranch(AI->getLoc(), Continue, ArrayRef(VirtAI)) - ->setDebugScope(AI->getDebugScope()); + SILArgument *Arg = Continue->createBBArg(AI.getType()); + if (!isa(AI)) { + IdenBuilder.createBranch(AI.getLoc(), Continue, + ArrayRef(IdenAI.getInstruction()))->setDebugScope( + AI.getDebugScope()); + VirtBuilder.createBranch(AI.getLoc(), Continue, + ArrayRef(VirtAI.getInstruction()))->setDebugScope( + AI.getDebugScope()); + } // Remove the old Apply instruction. - AI->replaceAllUsesWith(Arg); - AI->eraseFromParent(); + if (!isa(AI)) + AI.getInstruction()->replaceAllUsesWith(Arg); + auto *OriginalBB = AI.getParent(); + AI.getInstruction()->eraseFromParent(); + if (OriginalBB->empty()) + OriginalBB->removeFromParent(); // Update the stats. NumTargetsPredicted++; @@ -222,6 +249,32 @@ static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI, if (CMI->hasOneUse()) CMI->moveBefore(CMI->use_begin()->getUser()); + // Split critical edges resulting from VirtAI. + if (auto *TAI = dyn_cast(VirtAI)) { + auto *ErrorBB = TAI->getFunction()->createBasicBlock(); + ErrorBB->createBBArg(TAI->getErrorBB()->getBBArg(0)->getType()); + Builder.setInsertionPoint(ErrorBB); + Builder.createBranch(TAI->getLoc(), TAI->getErrorBB(), + {ErrorBB->getBBArg(0)}); + + auto *NormalBB = TAI->getFunction()->createBasicBlock(); + NormalBB->createBBArg(TAI->getNormalBB()->getBBArg(0)->getType()); + Builder.setInsertionPoint(NormalBB); + Builder.createBranch(TAI->getLoc(), TAI->getNormalBB(), + {NormalBB->getBBArg(0) }); + + Builder.setInsertionPoint(VirtAI.getInstruction()); + SmallVector Args; + for (auto Arg : VirtAI.getArguments()) { + Args.push_back(Arg); + } + FullApplySite NewVirtAI = Builder.createTryApply(VirtAI.getLoc(), VirtAI.getCallee(), + VirtAI.getSubstCalleeSILType(), VirtAI.getSubstitutions(), + Args, NormalBB, ErrorBB); + VirtAI.getInstruction()->eraseFromParent(); + VirtAI = NewVirtAI; + } + return VirtAI; } @@ -235,12 +288,12 @@ static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI, /// \p CD static class of the instance whose method is being invoked /// \p Subs set of direct subclasses of this class static bool isDefaultCaseKnown(ClassHierarchyAnalysis *CHA, - ApplyInst *AI, + FullApplySite AI, ClassDecl *CD, ClassHierarchyAnalysis::ClassList &Subs) { - ClassMethodInst *CMI = cast(AI->getCallee()); + ClassMethodInst *CMI = cast(AI.getCallee()); auto *Method = CMI->getMember().getFuncDecl(); - const DeclContext *DC = AI->getModule().getAssociatedContext(); + const DeclContext *DC = AI.getModule().getAssociatedContext(); if (CD->isFinal()) return true; @@ -262,7 +315,7 @@ static bool isDefaultCaseKnown(ClassHierarchyAnalysis *CHA, case Accessibility::Public: return false; case Accessibility::Internal: - if (!AI->getModule().isWholeModule()) + if (!AI.getModule().isWholeModule()) return false; break; case Accessibility::Private: @@ -323,9 +376,9 @@ static bool isDefaultCaseKnown(ClassHierarchyAnalysis *CHA, /// \brief Try to speculate the call target for the call \p AI. This function /// returns true if a change was made. -static bool tryToSpeculateTarget(ApplyInst *AI, +static bool tryToSpeculateTarget(FullApplySite AI, ClassHierarchyAnalysis *CHA) { - ClassMethodInst *CMI = cast(AI->getCallee()); + ClassMethodInst *CMI = cast(AI.getCallee()); // We cannot devirtualize in cases where dynamic calls are // semantically required. @@ -341,7 +394,7 @@ static bool tryToSpeculateTarget(ApplyInst *AI, // Bail if any generic types parameters of the class instance type are // unbound. // We cannot devirtualize unbound generic calls yet. - if (isClassWithUnboundGenericParameters(SubType, AI->getModule())) + if (isClassWithUnboundGenericParameters(SubType, AI.getModule())) return false; auto &M = CMI->getModule(); @@ -365,7 +418,7 @@ static bool tryToSpeculateTarget(ApplyInst *AI, DEBUG(llvm::dbgs() << "Inserting monomorphic speculative call for class " << CD->getName() << "\n"); - return speculateMonomorphicTarget(AI, SubType); + return !!speculateMonomorphicTarget(AI, SubType); } // Collect the direct subclasses for the class. @@ -511,11 +564,11 @@ namespace { bool Changed = false; // Collect virtual calls that may be specialized. - SmallVector ToSpecialize; + SmallVector ToSpecialize; for (auto &BB : *getFunction()) { for (auto II = BB.begin(), IE = BB.end(); II != IE; ++II) { - auto *AI = dyn_cast(&*II); - if (AI && isa(AI->getCallee())) + FullApplySite AI = FullApplySite::isa(&*II); + if (AI && isa(AI.getCallee())) ToSpecialize.push_back(AI); } } diff --git a/lib/SILPasses/Utils/Devirtualize.cpp b/lib/SILPasses/Utils/Devirtualize.cpp index b82fd16568c..1826a3f5fa7 100644 --- a/lib/SILPasses/Utils/Devirtualize.cpp +++ b/lib/SILPasses/Utils/Devirtualize.cpp @@ -95,7 +95,7 @@ bool swift::isClassWithUnboundGenericParameters(SILType C, SILModule &M) { // to invoke the method. static ArrayRef getSubstitutionsForCallee(SILModule &M, CanSILFunctionType GenCalleeType, - SILType ClassInstanceType, ApplyInst *AI) { + SILType ClassInstanceType, FullApplySite AI) { // *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 @@ -163,12 +163,12 @@ getSubstitutionsForCallee(SILModule &M, CanSILFunctionType GenCalleeType, } if (ClassSubs.empty()) - return AI->getSubstitutions(); + return AI.getSubstitutions(); - auto AISubs = AI->getSubstitutions(); + auto AISubs = AI.getSubstitutions(); CanSILFunctionType AIGenCalleeType = - AI->getCallee().getType().castTo(); + AI.getCallee().getType().castTo(); CanType AISelfClass = AIGenCalleeType->getSelfParameter().getType(); @@ -228,11 +228,11 @@ static SILFunction *getTargetClassMethod(SILModule &M, /// \p ClassOrMetatypeType is the class type or metatype type we are /// devirtualizing for. /// return true if it is possible to devirtualize, false - otherwise. -bool swift::canDevirtualizeClassMethod(ApplyInst *AI, +bool swift::canDevirtualizeClassMethod(FullApplySite AI, SILType ClassOrMetatypeType) { - DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI); + DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI.getInstruction()); - SILModule &Mod = AI->getModule(); + SILModule &Mod = AI.getModule(); // Bail if any generic types parameters of the class instance type are // unbound. @@ -244,7 +244,7 @@ bool swift::canDevirtualizeClassMethod(ApplyInst *AI, // either be a metatype or an alloc_ref. DEBUG(llvm::dbgs() << " Origin Type: " << ClassOrMetatypeType); - auto *CMI = cast(AI->getCallee()); + auto *CMI = cast(AI.getCallee()); // Find the implementation of the member which should be invoked. auto *F = getTargetClassMethod(Mod, ClassOrMetatypeType, CMI->getMember()); @@ -257,7 +257,7 @@ bool swift::canDevirtualizeClassMethod(ApplyInst *AI, return false; } - if (AI->getFunction()->isFragile()) { + if (AI.getFunction()->isFragile()) { // function_ref inside fragile function cannot reference a private or // hidden symbol. if (!(F->isFragile() || isValidLinkageForFragileRef(F->getLinkage()) || @@ -313,15 +313,15 @@ static SILValue conditionallyCastAddr(SILBuilderWithScope<16> &B, /// Insert instructions to cast the tuple return type into appropiate /// tuple type expected by the original apply_inst. -static SILInstruction *castTupleReturnType(ApplyInst *AI, ApplyInst *NewAI, +static SILInstruction *castTupleReturnType(FullApplySite AI, SILValue Value, CanTypeWrapper ResultTupleTy, SILFunction *F, SILBuilder& B) { - auto AITupleTy = cast(AI->getType().getSwiftRValueType()); + auto AITupleTy = cast(AI.getType().getSwiftRValueType()); SmallVector TupleElements; auto TupleElementTypes = ResultTupleTy.getElementTypes(); unsigned NumElements = ResultTupleTy->getElements().size(); for (unsigned i = 0; i < NumElements; ++i) { auto EltTy = TupleElementTypes[i]; - auto ExtractedElt = B.createTupleExtract(AI->getLoc(), NewAI, i); + auto ExtractedElt = B.createTupleExtract(AI.getLoc(), Value, i); OptionalTypeKind OTK; auto OptionalEltTy = EltTy.getCanonicalTypeOrNull()->getAnyOptionalObjectType(OTK); @@ -334,25 +334,25 @@ static SILInstruction *castTupleReturnType(ApplyInst *AI, ApplyInst *NewAI, // Dereference the optional value auto *SomeDecl = B.getASTContext().getOptionalSomeDecl(OTK); - auto FuncPtr = B.createUncheckedEnumData(AI->getLoc(), ExtractedElt, + auto FuncPtr = B.createUncheckedEnumData(AI.getLoc(), ExtractedElt, SomeDecl); auto AIOptionalEltTy = AITupleTy.getElementType(i).getCanonicalTypeOrNull()->getAnyOptionalObjectType(); - auto SILAIOptionalEltTy = AI->getModule().Types.getLoweredType( + auto SILAIOptionalEltTy = AI.getModule().Types.getLoweredType( AIOptionalEltTy); - auto ConvertedFuncPtr = B.createConvertFunction(AI->getLoc(), FuncPtr, + auto ConvertedFuncPtr = B.createConvertFunction(AI.getLoc(), FuncPtr, SILAIOptionalEltTy); TupleElements.push_back( - B.createOptionalSome(AI->getLoc(), ConvertedFuncPtr, OTK, + B.createOptionalSome(AI.getLoc(), ConvertedFuncPtr, OTK, SILType::getPrimitiveObjectType(AITupleTy.getElementType(i)))); } // Now create a new tuple DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n"); NumClassDevirt++; - return B.createTuple(AI->getLoc(), AI->getType(), TupleElements); + return B.createTuple(AI.getLoc(), AI.getType(), TupleElements); } /// \brief Devirtualize an apply of a class method. @@ -361,12 +361,12 @@ static SILInstruction *castTupleReturnType(ApplyInst *AI, ApplyInst *NewAI, /// \p ClassOrMetatype is a class value or metatype value that is the /// self argument of the apply we will devirtualize. /// return the new ApplyInst if created one or null. -SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI, +SILInstruction *swift::devirtualizeClassMethod(FullApplySite AI, SILValue ClassOrMetatype) { - DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI); + DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI.getInstruction()); - SILModule &Mod = AI->getModule(); - auto *CMI = cast(AI->getCallee()); + SILModule &Mod = AI.getModule(); + auto *CMI = cast(AI.getCallee()); auto ClassOrMetatypeType = ClassOrMetatype.getType(); auto *F = getTargetClassMethod(Mod, ClassOrMetatypeType, CMI->getMember()); @@ -378,18 +378,18 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI, if (GenCalleeType->isPolymorphic()) SubstCalleeType = GenCalleeType->substGenericArgs(Mod, Mod.getSwiftModule(), Subs); - SILBuilderWithScope<16> B(AI); - FunctionRefInst *FRI = B.createFunctionRef(AI->getLoc(), F); + SILBuilderWithScope<16> B(AI.getInstruction()); + FunctionRefInst *FRI = B.createFunctionRef(AI.getLoc(), 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 NewArgs; - auto Args = AI->getArguments(); + auto Args = AI.getArguments(); auto ParamTypes = SubstCalleeType->getParameterSILTypes(); for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) - NewArgs.push_back(conditionallyCastAddr(B, AI->getLoc(), Args[i], + NewArgs.push_back(conditionallyCastAddr(B, AI.getLoc(), Args[i], ParamTypes[i])); // Add the self argument, upcasting if required because we're @@ -398,32 +398,66 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI, if (ClassOrMetatypeType == SelfParamTy) NewArgs.push_back(ClassOrMetatype); else - NewArgs.push_back(B.createUpcast(AI->getLoc(), ClassOrMetatype, + NewArgs.push_back(B.createUpcast(AI.getLoc(), ClassOrMetatype, SelfParamTy)); // 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(); + SILType ReturnType = AI.getType(); if (!SubstCalleeType->hasIndirectResult()) { ReturnType = SubstCalleeType->getSILResult(); } SILType SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeType); - SILInstruction *NewAI = - B.createApply(AI->getLoc(), FRI, SubstCalleeSILType, ReturnType, - Subs, NewArgs); + FullApplySite NewAI; - if (ReturnType == AI->getType()) { + SILBasicBlock *ResultBB = nullptr; + SILBasicBlock *NormalBB = nullptr; + SILValue ResultValue; + + if (!isa(AI)) { + NewAI = B.createApply(AI.getLoc(), FRI, SubstCalleeSILType, ReturnType, + Subs, NewArgs); + ResultValue = SILValue(NewAI.getInstruction(), 0); + } else { + auto *TAI = cast(AI); + // Always create a new BB for normal and error BBs. + // This avoids creation of critical edges. + ResultBB = B.getFunction().createBasicBlock(); + ResultBB->createBBArg(ReturnType); + NormalBB = TAI->getNormalBB(); + + auto *ErrorBB = B.getFunction().createBasicBlock(); + ErrorBB->createBBArg(TAI->getErrorBB()->getBBArg(0)->getType()); + + NewAI = B.createTryApply(AI.getLoc(), FRI, SubstCalleeSILType, + Subs, NewArgs, + ResultBB, ErrorBB); + // The result value is passed as a parameter to the normal block. + ResultValue = ResultBB->getBBArg(0); + B.setInsertionPoint(ErrorBB); + B.createBranch(TAI->getLoc(), TAI->getErrorBB(), + {ErrorBB->getBBArg(0)}); + B.setInsertionPoint(ResultBB); + } + + SILInstruction *CastedReturnValue = NewAI.getInstruction(); + + if (ReturnType == AI.getType()) { DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n"); NumClassDevirt++; - return NewAI; + if (NormalBB) { + B.createBranch(NewAI.getLoc(), NormalBB, { ResultBB->getBBArg(0) }); + } + return CastedReturnValue; } // 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 + // Accessors could return a tuple where one of the elements is of a function // type, which may refer to a subclass instead of a superclass in its // signature. @@ -432,9 +466,14 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI, // the function types and reconstruct the tuple. if (auto *FD = dyn_cast(CMI->getMember().getDecl())) { if (FD->isAccessor()) { - if (auto ResultTupleTy = dyn_cast(ReturnType.getSwiftRValueType())) - return castTupleReturnType(AI, dyn_cast(NewAI), - ResultTupleTy, F, B); + if (auto ResultTupleTy = dyn_cast(ReturnType.getSwiftRValueType())) { + assert(isa(NewAI) && "should be an apply_inst"); + CastedReturnValue = castTupleReturnType(AI, ResultValue, ResultTupleTy, F, B); + if (NormalBB) { + B.createBranch(NewAI.getLoc(), NormalBB, { CastedReturnValue }); + } + return CastedReturnValue; + } } } @@ -449,7 +488,7 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI, auto OptionalReturnType = ReturnType.getSwiftRValueType() .getAnyOptionalObjectType(OTK); - auto OptionalAIType = AI->getType().getSwiftRValueType() + auto OptionalAIType = AI.getType().getSwiftRValueType() .getAnyOptionalObjectType(AI_OTK); // Return type if not an optional, but the expected type is an optional @@ -462,15 +501,19 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI, auto OptType = OptionalType::get(AI_OTK, ReturnType.getSwiftRValueType()). getCanonicalTypeOrNull(); - NewAI = B.createOptionalSome(AI->getLoc(), NewAI, - AI_OTK, - SILType::getPrimitiveObjectType(OptType)); + ResultValue = B.createOptionalSome(AI.getLoc(), ResultValue, + AI_OTK, + SILType::getPrimitiveObjectType(OptType)); OptionalReturnType = ReturnType.getSwiftRValueType(); if (OptionalAIType == OptionalReturnType) { DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n"); NumClassDevirt++; - return NewAI; + CastedReturnValue = dyn_cast(ResultValue.getDef()); + if (NormalBB) { + B.createBranch(NewAI.getLoc(), NormalBB, { CastedReturnValue }); + } + return CastedReturnValue; } } @@ -481,10 +524,14 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI, // Both types are optional and one of them is the superclass of the other. DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n"); NumClassDevirt++; - return B.createUpcast(AI->getLoc(), SILValue(NewAI, 0), AI->getType()); + CastedReturnValue = B.createUpcast(AI.getLoc(), ResultValue, AI.getType()); + if (NormalBB) { + B.createBranch(NewAI.getLoc(), NormalBB, { CastedReturnValue }); + } + return CastedReturnValue; } - if (OptionalReturnType == AI->getType().getSwiftRValueType()) { + if (OptionalReturnType == AI.getType().getSwiftRValueType()) { UnwrapOptionalResult = true; } @@ -498,28 +545,34 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI, "Only addresses and refs can have their types changed due to " "covariant return types or contravariant argument types."); - SILInstruction *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); + CastedReturnValue = B.createUncheckedEnumData(AI.getLoc(), + ResultValue, SomeDecl); } else if (WrapOptionalResult) { // The devirtualized method returns a non-optional result. // We need to wrap it into an optional. - CastedAI = B.createOptionalSome(AI->getLoc(), NewAI, AI_OTK, AI->getType()); + CastedReturnValue = B.createOptionalSome(AI.getLoc(), ResultValue, + AI_OTK, AI.getType()); } else if (ReturnType.isAddress()) { - CastedAI = B.createUncheckedAddrCast(AI->getLoc(), NewAI, AI->getType()); + CastedReturnValue = B.createUncheckedAddrCast(AI.getLoc(), + ResultValue, AI.getType()); } else { - CastedAI = B.createUncheckedRefCast(AI->getLoc(), NewAI, AI->getType()); + CastedReturnValue = B.createUncheckedRefCast(AI.getLoc(), + ResultValue, AI.getType()); } DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n"); NumClassDevirt++; - return CastedAI; + if (NormalBB) { + B.createBranch(NewAI.getLoc(), NormalBB, { CastedReturnValue }); + } + return CastedReturnValue; } -SILInstruction *swift::tryDevirtualizeClassMethod(ApplyInst *AI, +SILInstruction *swift::tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance) { if (!canDevirtualizeClassMethod(AI, ClassInstance.getType())) return nullptr; @@ -534,11 +587,11 @@ SILInstruction *swift::tryDevirtualizeClassMethod(ApplyInst *AI, /// 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 *devirtualizeWitnessMethod(ApplyInst *AI, SILFunction *F, +static ApplyInst *devirtualizeWitnessMethod(FullApplySite AI, SILFunction *F, ArrayRef 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(); + auto &Module = AI.getModule(); // Collect all the required substitutions. // @@ -548,7 +601,7 @@ static ApplyInst *devirtualizeWitnessMethod(ApplyInst *AI, SILFunction *F, SmallVector NewSubstList(Subs.begin(), Subs.end()); // Add the non-self-derived substitutions from the original application. - for (auto &origSub : AI->getSubstitutionsWithoutSelfSubstitution()) + for (auto &origSub : AI.getSubstitutionsWithoutSelfSubstitution()) if (!origSub.getArchetype()->isSelfDerived()) NewSubstList.push_back(origSub); @@ -567,10 +620,10 @@ static ApplyInst *devirtualizeWitnessMethod(ApplyInst *AI, SILFunction *F, // 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()) { + SILBuilderWithScope<8> B(AI.getInstruction()); + for (SILValue A : AI.getArguments()) { if (A.getType() != *ParamType) - A = B.createUpcast(AI->getLoc(), A, *ParamType); + A = B.createUpcast(AI.getLoc(), A, *ParamType); Arguments.push_back(A); ++ParamType; @@ -578,8 +631,8 @@ static ApplyInst *devirtualizeWitnessMethod(ApplyInst *AI, SILFunction *F, // Replace old apply instruction by a new apply instruction that invokes // the witness thunk. - SILBuilderWithScope<2> Builder(AI); - SILLocation Loc = AI->getLoc(); + SILBuilderWithScope<2> Builder(AI.getInstruction()); + SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); auto SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeCanType); @@ -594,15 +647,15 @@ static ApplyInst *devirtualizeWitnessMethod(ApplyInst *AI, SILFunction *F, /// 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 *tryDevirtualizeWitnessMethod(ApplyInst *AI) { +static ApplyInst *tryDevirtualizeWitnessMethod(FullApplySite AI) { SILFunction *F; ArrayRef Subs; SILWitnessTable *WT; - auto *WMI = cast(AI->getCallee()); + auto *WMI = cast(AI.getCallee()); std::tie(F, WT, Subs) = - AI->getModule().lookUpFunctionInWitnessTable(WMI->getConformance(), + AI.getModule().lookUpFunctionInWitnessTable(WMI->getConformance(), WMI->getMember()); if (!F) @@ -652,15 +705,15 @@ static bool isKnownFinal(SILModule &M, SILDeclRef Member) { /// Attempt to devirtualize the given apply if possible, and return a /// new instruction in that case, or nullptr otherwise. -SILInstruction *swift::tryDevirtualizeApply(ApplyInst *AI) { - DEBUG(llvm::dbgs() << " Trying to devirtualize: " << *AI); +SILInstruction *swift::tryDevirtualizeApply(FullApplySite AI) { + DEBUG(llvm::dbgs() << " Trying to devirtualize: " << *AI.getInstruction()); // Devirtualize apply instructions that call witness_method instructions: // // %8 = witness_method $Optional, #LogicValue.boolValue!getter.1 // %9 = apply %8(%6#1) : ... // - if (isa(AI->getCallee())) + if (isa(AI.getCallee())) return tryDevirtualizeWitnessMethod(AI); /// Optimize a class_method and alloc_ref pair into a direct function @@ -679,7 +732,7 @@ SILInstruction *swift::tryDevirtualizeApply(ApplyInst *AI) { /// into /// /// %YY = function_ref @... - if (auto *CMI = dyn_cast(AI->getCallee())) { + if (auto *CMI = dyn_cast(AI.getCallee())) { // Check if the class member is known to be final. if (isKnownFinal(CMI->getModule(), CMI->getMember())) return tryDevirtualizeClassMethod(AI, CMI->getOperand()); diff --git a/lib/SILPasses/Utils/Local.cpp b/lib/SILPasses/Utils/Local.cpp index a20edbe5020..348a8cb5a3e 100644 --- a/lib/SILPasses/Utils/Local.cpp +++ b/lib/SILPasses/Utils/Local.cpp @@ -220,7 +220,8 @@ ApplyInst *swift::findApplyFromDevirtualizedResult(SILInstruction *I) { // value, and delete the old apply. void swift::replaceDeadApply(FullApplySite Old, SILInstruction *New) { auto *OldApply = Old.getInstruction(); - OldApply->replaceAllUsesWith(New); + if (!isa(OldApply)) + OldApply->replaceAllUsesWith(New); recursivelyDeleteTriviallyDeadInstructions(OldApply, true); } diff --git a/test/SILPasses/devirt_try_apply.swift b/test/SILPasses/devirt_try_apply.swift new file mode 100644 index 00000000000..1e0b7c41e2f --- /dev/null +++ b/test/SILPasses/devirt_try_apply.swift @@ -0,0 +1,144 @@ +// RUN: %target-swift-frontend -O -primary-file %s -emit-sil -sil-inline-threshold 1000 -sil-verify-all | FileCheck %s + +private class Base { + @inline(never) + func foo() throws -> Int32? { + print("Base") + return 0 + } + + @inline(never) + func boo1() throws -> Base { + return self + } + + @inline(never) + func boo2() throws -> Base? { + return self + } +} + + +private class Derived1: Base { + @inline(never) + override func foo() throws -> Int32? { + print("Derived1") + return 1 + } + + @inline(never) + override func boo1() throws -> Derived1 { + return self + } + + @inline(never) + override func boo2() throws -> Derived1? { + return self + } +} + +private class Derived2: Base { + @inline(never) + override func foo() throws -> Int32 { + print("Derived2") + return 2 + } + + @inline(never) + override func boo1() throws -> Derived2 { + return self + } + + @inline(never) + override func boo2() throws -> Derived2 { + return self + } +} + +// CHECK-LABEL: sil private [noinline] @_TTSf4g___TF16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A219testTryApplyDevirt1 +// CHECK-NOT: class_method +// CHECK-NOT: } +// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived13foofS0_FzT_GSqVSs5Int32_ : $@convention(method) (@guaranteed Derived1) -> (Optional, @error ErrorType) +// CHECK: try_apply +// CHECK-NOT: class_method +// CHECK-NOT: } +// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived23foofS0_FzT_VSs5Int32 : $@convention(method) (@guaranteed Derived2) -> (Int32, @error ErrorType) +// CHECK: try_apply +// CHECK-NOT: class_method +// CHECK-NOT: } +// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A24Base3foofS0_FzT_GSqVSs5Int32_ : $@convention(method) (@guaranteed Base) -> (Optional, @error ErrorType) +// CHECK: try_apply +// CHECK-NOT: class_method +// CHECK: } +@inline(never) +private func testTryApplyDevirt1(b: Base) -> Int32? { + var result: Int32? = nil + do { + result = try b.foo() + } catch _ { + } + return result +} + +// CHECK-LABEL: sil private [noinline] @_TTSf4g___TF16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A219testTryApplyDevirt2 +// CHECK-NOT: class_method +// CHECK-NOT: } +// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived14boo1fS0_FzT_S0_ : $@convention(method) (@guaranteed Derived1) -> (@owned Derived1, @error ErrorType) +// CHECK: try_apply +// CHECK-NOT: class_method +// CHECK-NOT: } +// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived24boo1fS0_FzT_S0_ : $@convention(method) (@guaranteed Derived2) -> (@owned Derived2, @error ErrorType) +// CHECK: try_apply +// CHECK-NOT: class_method +// CHECK-NOT: } +// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A24Base4boo1fS0_FzT_S0_ : $@convention(method) (@guaranteed Base) -> (@owned Base, @error ErrorType) +// CHECK: try_apply +// CHECK-NOT: class_method +// CHECK: } +@inline(never) +private func testTryApplyDevirt2(b: Base) -> Base? { + var result: Base? = nil + do { + result = try b.boo1() + } catch _ { + } + return result +} + +// CHECK-LABEL: sil private [noinline] @_TTSf4g___TF16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A219testTryApplyDevirt3 +// CHECK-NOT: class_method +// CHECK-NOT: } +// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived14boo2fS0_FzT_GSqS0__ : $@convention(method) (@guaranteed Derived1) -> (@owned Optional, @error ErrorType) +// CHECK: try_apply +// CHECK-NOT: class_method +// CHECK-NOT: } +// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived24boo2fS0_FzT_S0_ : $@convention(method) (@guaranteed Derived2) -> (@owned Derived2, @error ErrorType) +// CHECK: try_apply +// CHECK-NOT: class_method +// CHECK-NOT: } +// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A24Base4boo2fS0_FzT_GSqS0__ : $@convention(method) (@guaranteed Base) -> (@owned Optional, @error ErrorType) +// CHECK: try_apply +// CHECK-NOT: class_method +// CHECK: } +@inline(never) +private func testTryApplyDevirt3(b: Base) -> Base? { + var result: Base? = nil + do { + result = try b.boo2() + } catch _ { + } + return result +} + +public func test1() { + testTryApplyDevirt1(Base()) +} + +public func test2() { + testTryApplyDevirt2(Base()) +} + +public func test3() { + testTryApplyDevirt3(Base()) +} +