diff --git a/include/swift/AST/SILOptions.h b/include/swift/AST/SILOptions.h index da8e88772a59d..46132b8483d1d 100644 --- a/include/swift/AST/SILOptions.h +++ b/include/swift/AST/SILOptions.h @@ -108,10 +108,6 @@ class SILOptions { /// purposes. bool EnableOSSAOptimizations = true; - /// Controls whether to turn on speculative devirtualization. - /// It is turned off by default. - bool EnableSpeculativeDevirtualization = false; - /// Controls whether to emit actor data-race checks. bool EnableActorDataRaceChecks = false; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 38e4975ba3dab..55cf939ea9a47 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -578,7 +578,7 @@ def disable_sil_partial_apply : Flag<["-"], "disable-sil-partial-apply">, HelpText<"Disable use of partial_apply in SIL generation">; def enable_spec_devirt : Flag<["-"], "enable-spec-devirt">, - HelpText<"Enable speculative devirtualization pass.">; + HelpText<"Deprecated, has no effect">; def enable_async_demotion : Flag<["-"], "enable-experimental-async-demotion">, HelpText<"Enables an optimization pass to demote async functions.">; diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 062a3f1b9dc5d..d9e6a2678c118 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -428,8 +428,6 @@ LEGACY_PASS(SimplifyBBArgs, "simplify-bb-args", "SIL Block Argument Simplification") LEGACY_PASS(SimplifyCFG, "simplify-cfg", "SIL CFG Simplification") -LEGACY_PASS(SpeculativeDevirtualization, "specdevirt", - "Speculative Devirtualization via Guarded Calls") LEGACY_PASS(SplitAllCriticalEdges, "split-critical-edges", "Split all Critical Edges in the SIL CFG") LEGACY_PASS(SplitNonCondBrCriticalEdges, "split-non-cond_br-critical-edges", diff --git a/lib/DriverTool/sil_opt_main.cpp b/lib/DriverTool/sil_opt_main.cpp index aa4f7a5278313..5b04453e83701 100644 --- a/lib/DriverTool/sil_opt_main.cpp +++ b/lib/DriverTool/sil_opt_main.cpp @@ -296,10 +296,6 @@ struct SILOptOptions { VerifyExclusivity = llvm::cl::opt("enable-verify-exclusivity", llvm::cl::desc("Verify the access markers used to enforce exclusivity.")); - llvm::cl::opt - EnableSpeculativeDevirtualization = llvm::cl::opt("enable-spec-devirt", - llvm::cl::desc("Enable Speculative Devirtualization pass.")); - llvm::cl::opt EnableAsyncDemotion = llvm::cl::opt("enable-async-demotion", llvm::cl::desc("Enables an optimization pass to demote async functions.")); @@ -891,7 +887,6 @@ int sil_opt_main(ArrayRef argv, void *MainAddr) { SILOpts.EmitVerboseSIL |= options.EmitVerboseSIL; SILOpts.EmitSortedSIL |= options.EmitSortedSIL; - SILOpts.EnableSpeculativeDevirtualization = options.EnableSpeculativeDevirtualization; SILOpts.EnableAsyncDemotion = options.EnableAsyncDemotion; SILOpts.EnableThrowsPrediction = options.EnableThrowsPrediction; SILOpts.EnableNoReturnCold = options.EnableNoReturnCold; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 37f878acc536b..e8d54541e472c 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -3081,7 +3081,6 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args, Opts.EnableOSSAOptimizations &= !Args.hasArg(OPT_disable_ossa_opts); Opts.EnableSILOpaqueValues = Args.hasFlag( OPT_enable_sil_opaque_values, OPT_disable_sil_opaque_values, false); - Opts.EnableSpeculativeDevirtualization |= Args.hasArg(OPT_enable_spec_devirt); Opts.EnableAsyncDemotion |= Args.hasArg(OPT_enable_async_demotion); Opts.EnableThrowsPrediction = Args.hasFlag( OPT_enable_throws_prediction, OPT_disable_throws_prediction, diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 3d08efc8bf489..edccafab44611 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -795,11 +795,6 @@ static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) { // Do the second stack promotion on low-level SIL. P.addStackPromotion(); - // Speculate virtual call targets. - if (P.getOptions().EnableSpeculativeDevirtualization) { - P.addSpeculativeDevirtualization(); - } - // There should be at least one SILCombine+SimplifyCFG between the // ClosureSpecializer, etc. and the last inliner. Cleaning up after these // passes can expose more inlining opportunities. diff --git a/lib/SILOptimizer/Transforms/CMakeLists.txt b/lib/SILOptimizer/Transforms/CMakeLists.txt index 9728f59996728..01755e1eab34d 100644 --- a/lib/SILOptimizer/Transforms/CMakeLists.txt +++ b/lib/SILOptimizer/Transforms/CMakeLists.txt @@ -31,6 +31,5 @@ target_sources(swiftSILOptimizer PRIVATE DestroyAddrHoisting.cpp SimplifyCFG.cpp Sink.cpp - SpeculativeDevirtualizer.cpp StringOptimization.cpp) diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp deleted file mode 100644 index a156c5e283527..0000000000000 --- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp +++ /dev/null @@ -1,642 +0,0 @@ -//===--- SpeculativeDevirtualizer.cpp - Speculatively devirtualize calls --===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -// -// Speculatively devirtualizes witness- and class-method calls into direct -// calls. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "sil-speculative-devirtualizer" - -#include "swift/SIL/BasicBlockUtils.h" -#include "swift/SIL/SILArgument.h" -#include "swift/SIL/SILBuilder.h" -#include "swift/SIL/SILFunction.h" -#include "swift/SIL/SILInstruction.h" -#include "swift/SIL/SILModule.h" -#include "swift/SIL/InstructionUtils.h" -#include "swift/SIL/OptimizationRemark.h" -#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h" -#include "swift/SILOptimizer/Utils/Generics.h" -#include "swift/SILOptimizer/PassManager/Passes.h" -#include "swift/SILOptimizer/PassManager/PassManager.h" -#include "swift/SILOptimizer/PassManager/Transforms.h" -#include "swift/SILOptimizer/Utils/Devirtualize.h" -#include "swift/SILOptimizer/Utils/SILInliner.h" -#include "swift/AST/ASTContext.h" -#include "swift/Basic/Assertions.h" -#include "llvm/ADT/MapVector.h" -#include "llvm/ADT/PointerIntPair.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/ADT/StringSet.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/CommandLine.h" - -using namespace swift; - -// This is the limit for the number of subclasses (jump targets) that the -// speculative devirtualizer will try to predict. -static const int MaxNumSpeculativeTargets = 6; - -STATISTIC(NumTargetsPredicted, "Number of monomorphic functions predicted"); - -/// We want to form a second edge to the given block, but we know -/// that'll form a critical edge. Return a basic block to which we can -/// create an edge essentially like the original edge. -static SILBasicBlock *cloneEdge(TermInst *TI, unsigned SuccIndex) { -#ifndef NDEBUG - auto origDestBB = TI->getSuccessors()[SuccIndex].getBB(); -#endif - - // Split the edge twice. The first split will become our cloned - // and temporarily-unused edge. The second split will remain in place - // as the original edge. - auto clonedEdgeBB = splitEdge(TI, SuccIndex); - auto replacementEdgeBB = splitEdge(TI, SuccIndex); - - // Extract the terminators. - auto clonedEdgeBranch = - cast(clonedEdgeBB->getTerminator()); - auto replacementEdgeBranch = - cast(replacementEdgeBB->getTerminator()); - - assert(TI->getSuccessors()[SuccIndex].getBB() == replacementEdgeBB); - assert(replacementEdgeBranch->getDestBB() == clonedEdgeBB); - assert(clonedEdgeBranch->getDestBB() == origDestBB); - - // Change the replacement branch to point to the original destination. - // This will leave the cloned edge unused, which is how we wanted it. - replacementEdgeBranch->getSuccessors()[0] = clonedEdgeBranch->getDestBB(); - assert(clonedEdgeBB->pred_empty()); - - return clonedEdgeBB; -} - -// A utility function for cloning the apply instruction. -static FullApplySite CloneApply(FullApplySite AI, SILValue SelfArg, - SILBuilder &Builder) { - // Clone the Apply. - Builder.setCurrentDebugScope(AI.getDebugScope()); - auto Args = AI.getArguments(); - SmallVector Ret(Args.size()); - for (unsigned i = 0, e = Args.size(); i != e; ++i) { - if (i == e - 1 && SelfArg) { - Ret[i] = SelfArg; - } else { - Ret[i] = Args[i]; - } - } - - FullApplySite NAI; - - switch (AI.getInstruction()->getKind()) { - case SILInstructionKind::ApplyInst: - NAI = Builder.createApply(AI.getLoc(), AI.getCallee(), - AI.getSubstitutionMap(), - Ret, AI.getApplyOptions()); - break; - case SILInstructionKind::TryApplyInst: { - auto *TryApplyI = cast(AI.getInstruction()); - auto NormalBB = cloneEdge(TryApplyI, TryApplyInst::NormalIdx); - auto ErrorBB = cloneEdge(TryApplyI, TryApplyInst::ErrorIdx); - NAI = Builder.createTryApply(AI.getLoc(), AI.getCallee(), - AI.getSubstitutionMap(), - Ret, NormalBB, ErrorBB, - AI.getApplyOptions()); - break; - } - default: - llvm_unreachable("Trying to clone an unsupported apply instruction"); - } - - return NAI; -} - -/// Insert monomorphic inline caches for a specific class or metatype -/// type \p SubClassTy. -static FullApplySite speculateMonomorphicTarget(SILPassManager *pm, FullApplySite AI, - CanType SubType, ClassDecl *CD, - CanType ClassType, - CheckedCastBranchInst *&CCBI) { - if (SubType->hasDynamicSelfType()) - return FullApplySite(); - - CCBI = nullptr; - // Bail if this class_method cannot be devirtualized. - if (!canDevirtualizeClassMethod(AI, CD, ClassType)) - return FullApplySite(); - - // Can't speculate begin_apply yet. - if (isa(AI)) - 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(); - - ClassMethodInst *CMI = cast(AI.getCallee()); - - // 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(SILType::getPrimitiveObjectType(SubType), - CMI->getOperand()->getOwnershipKind()); - - 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. - - CCBI = Builder.createCheckedCastBranch(AI.getLoc(), /*exact*/ true, - CheckedCastInstOptions(), - CMI->getOperand(), - CMI->getOperand()->getType().getASTType(), - SILType::getPrimitiveObjectType(SubType), - 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, DownCastedClassInstance, IdenBuilder); - FullApplySite VirtAI = CloneApply(AI, SILValue(), 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(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(), OwnershipKind::Owned); - if (!isa(AI)) { - if (AI.getSubstCalleeType()->isNoReturnFunction( - F->getModule(), AI.getFunction()->getTypeExpansionContext())) { - IdenBuilder.createUnreachable(AI.getLoc()); - VirtBuilder.createUnreachable(AI.getLoc()); - } else { - IdenBuilder.createBranch(AI.getLoc(), Continue, - { cast(IdenAI) }); - VirtBuilder.createBranch(AI.getLoc(), Continue, - { cast(VirtAI) }); - } - } - - // Remove the old Apply instruction. - assert(AI.getInstruction() == &Continue->front() && - "AI should be the first instruction in the split Continue block"); - if (isa(AI)) { - AI.getInstruction()->eraseFromParent(); - assert(Continue->empty() && - "There should not be an instruction after try_apply"); - Continue->eraseFromParent(); - } else { - auto apply = cast(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 NewInst = devirtualizeClassMethod(pm, IdenAI, DownCastedClassInstance, CD, - ClassType, nullptr) - .first; - assert(NewInst && "Expected to be able to devirtualize apply!"); - (void)NewInst; - - deleteDevirtualizedApply(IdenAI); - - // Split critical edges resulting from VirtAI. - if (auto *TAI = dyn_cast(VirtAI)) { - auto *ErrorBB = TAI->getFunction()->createBasicBlock(); - SILArgument *ErrorArg = nullptr; - if (TAI->getErrorBB()->getNumArguments() == 1) { - ErrorArg = TAI->getErrorBB()->getArgument(0); - ErrorBB->createPhiArgument(ErrorArg->getType(), OwnershipKind::Owned); - } - Builder.setInsertionPoint(ErrorBB); - - if (ErrorArg) { - Builder.createBranch(TAI->getLoc(), TAI->getErrorBB(), - {ErrorBB->getArgument(0)}); - } else { - Builder.createBranch(TAI->getLoc(), TAI->getErrorBB()); - } - - auto *NormalBB = TAI->getFunction()->createBasicBlock(); - NormalBB->createPhiArgument(TAI->getNormalBB()->getArgument(0)->getType(), - OwnershipKind::Owned); - Builder.setInsertionPoint(NormalBB); - Builder.createBranch(TAI->getLoc(), TAI->getNormalBB(), - {NormalBB->getArgument(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.getSubstitutionMap(), - Args, NormalBB, ErrorBB, - VirtAI.getApplyOptions()); - VirtAI.getInstruction()->eraseFromParent(); - VirtAI = NewVirtAI; - } - - return VirtAI; -} - -/// Returns true, if a method implementation to be called by the -/// default case handler of a speculative devirtualization is statically -/// known. This happens if it can be proven that generated -/// checked_cast_br instructions cover all other possible cases. -/// -/// \p CHA class hierarchy analysis to be used -/// \p AI invocation instruction -/// \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, - FullApplySite AI, - ClassDecl *CD, - ClassHierarchyAnalysis::ClassList &Subs) { - ClassMethodInst *CMI = cast(AI.getCallee()); - auto *Method = CMI->getMember().getAbstractFunctionDecl(); - assert(Method && "not a function"); - - if (CD->isFinal()) - return true; - - // If the class has an @objc ancestry it can be dynamically subclassed and we - // can't therefore statically know the default case. - if (CD->checkAncestry(AncestryFlags::ObjC)) - return false; - - // Only handle classes defined within the SILModule's associated context. - if (!CD->isChildContextOf(AI.getModule().getAssociatedContext())) - return false; - - if (!CD->hasAccess()) - return false; - - // Only consider 'private' members, unless we are in whole-module compilation. - switch (CD->getEffectiveAccess()) { - case AccessLevel::Open: - return false; - case AccessLevel::Public: - case AccessLevel::Package: - case AccessLevel::Internal: - if (!AI.getModule().isWholeModule()) - return false; - break; - case AccessLevel::FilePrivate: - case AccessLevel::Private: - break; - } - - // This is a private or a module internal class. - // - // We can analyze the class hierarchy rooted at it and - // eventually devirtualize a method call more efficiently. - - // First, analyze all direct subclasses. - // We know that a dedicated checked_cast_br check is - // generated for each direct subclass by tryToSpeculateTarget. - for (auto S : Subs) { - // Check if the subclass overrides a method - auto *FD = S->findOverridingDecl(Method); - if (!FD) - continue; - if (CHA->hasKnownDirectSubclasses(S)) { - // This subclass has its own subclasses and - // they will use this implementation or provide - // their own. In either case it is not covered by - // checked_cast_br instructions generated by - // tryToSpeculateTarget. Therefore it increases - // the number of remaining cases to be handled - // by the default case handler. - return false; - } - } - - // Then, analyze indirect subclasses. - - // Set of indirect subclasses for the class. - auto &IndirectSubs = CHA->getIndirectSubClasses(CD); - - // Check if any indirect subclasses use an implementation - // of the method different from the implementation in - // the current class. If this is the case, then such - // an indirect subclass would need a dedicated - // checked_cast_br check to be devirtualized. But this is - // not done by tryToSpeculateTarget yet and therefore - // such a subclass should be handled by the "default" - // case handler, which essentially means that "default" - // case cannot be devirtualized since it covers more - // then one alternative. - for (auto S : IndirectSubs) { - auto *ImplFD = S->findImplementingMethod(Method); - if (ImplFD != Method) { - // Different implementation is used by a subclass. - // Therefore, the default case is not known. - return false; - } - } - - return true; -} - -/// Try to speculate the call target for the call \p AI. This function -/// returns true if a change was made. -static bool tryToSpeculateTarget(SILPassManager *pm, FullApplySite AI, ClassHierarchyAnalysis *CHA, - OptRemark::Emitter &ORE) { - ClassMethodInst *CMI = cast(AI.getCallee()); - - // Strip any upcasts off of our 'self' value, potentially leaving us - // with a value whose type is closer (in the class hierarchy) to the - // actual dynamic type. - auto SubTypeValue = stripUpCasts(CMI->getOperand()); - CanType SubType = SubTypeValue->getType().getASTType(); - - // Bail if any generic types parameters of the class instance type are - // unbound. - // We cannot devirtualize unbound generic calls yet. - if (SubType->hasArchetype()) - return false; - - auto *F = CMI->getFunction(); - auto &M = F->getModule(); - - CheckedCastBranchInst *LastCCBI = nullptr; - - auto ClassType = getSelfInstanceType(SubType); - ClassDecl *CD = ClassType.getClassOrBoundGenericClass(); - assert(CD && "Expected decl for class type!"); - - if (!CHA->hasKnownDirectSubclasses(CD)) { - // If there is only one possible alternative for this method, - // try to devirtualize it completely. - ClassHierarchyAnalysis::ClassList Subs; - if (isDefaultCaseKnown(CHA, AI, CD, Subs)) { - auto NewInst = - tryDevirtualizeClassMethod(pm, AI, SubTypeValue, CD, ClassType, &ORE) - .first; - if (NewInst) - deleteDevirtualizedApply(AI); - return bool(NewInst); - } - - LLVM_DEBUG(llvm::dbgs() << "Inserting monomorphic speculative call for " - "class " << CD->getName() << "\n"); - return !!speculateMonomorphicTarget(pm, AI, SubType, CD, ClassType, LastCCBI); - } - - // True if any instructions were changed or generated. - bool Changed = false; - - SmallVector Subs; - getAllSubclasses(CHA, CD, ClassType, M, Subs); - - // Number of subclasses which cannot be handled by checked_cast_br checks. - int NotHandledSubsNum = 0; - if (Subs.size() > MaxNumSpeculativeTargets) { - LLVM_DEBUG(llvm::dbgs() << "Class " << CD->getName() << " has too many (" - << Subs.size() << ") subclasses. Performing " - "speculative devirtualization only for the first " - << MaxNumSpeculativeTargets << " of them.\n"); - - NotHandledSubsNum += (Subs.size() - MaxNumSpeculativeTargets); - Subs.erase(&Subs[MaxNumSpeculativeTargets], Subs.end()); - } - - LLVM_DEBUG(llvm::dbgs() << "Class " << CD->getName() << " is a superclass. " - "Inserting polymorphic speculative call.\n"); - - // Try to devirtualize the static class of instance - // if it is possible. - if (auto F = getTargetClassMethod(M, AI, CD, ClassType, CMI)) { - // Do not devirtualize if a method in the base class is marked - // as non-optimizable. This way it is easy to disable the - // devirtualization of this method in the base class and - // any classes derived from it. - if (!F->shouldOptimize()) - return false; - } - - auto FirstAI = - speculateMonomorphicTarget(pm, AI, SubType, CD, ClassType, LastCCBI); - if (FirstAI) { - Changed = true; - AI = FirstAI; - } - - // Perform a speculative devirtualization of a method invocation. - // It replaces an indirect class_method-based call by a code to perform - // a direct call of the method implementation based on the dynamic class - // of the instance. - // - // The code is generated according to the following principles: - // - // - For each direct subclass, a dedicated checked_cast_br instruction - // is generated to check if a dynamic class of the instance is exactly - // this subclass. - // - // - If this check succeeds, then it jumps to the code which performs a - // direct call of a method implementation specific to this subclass. - // - // - If this check fails, then a different subclass is checked by means of - // checked_cast_br in a similar way. - // - // - Finally, if the instance does not exactly match any of the direct - // subclasses, the "default" case code is generated, which should handle - // all remaining alternatives, i.e. it should be able to dispatch to any - // possible remaining method implementations. Typically this is achieved by - // using a class_method instruction, which performs an indirect invocation. - // But if it can be proven that only one specific implementation of - // a method will be always invoked by this code, then a class_method-based - // call can be devirtualized and replaced by a more efficient direct - // invocation of this specific method implementation. - // - // Remark: With the current implementation of a speculative devirtualization, - // if devirtualization of the "default" case is possible, then it would - // by construction directly invoke the implementation of the method - // corresponding to the static type of the instance. This may change - // in the future, if we start using PGO for ordering of checked_cast_br - // checks. - - // TODO: The ordering of checks may benefit from using a PGO, because - // the most probable alternatives could be checked first. - - for (auto S : Subs) { - LLVM_DEBUG(llvm::dbgs() << "Inserting a speculative call for class " - << CD->getName() << " and subclass " << S->getName() << "\n"); - - // FIXME: Add support for generic subclasses. - if (S->isGenericContext()) { - ++NotHandledSubsNum; - continue; - } - - CanType CanClassType = S->getDeclaredInterfaceType()->getCanonicalType(); - - auto ClassOrMetatypeType = CanClassType; - if (auto MT = dyn_cast(SubType)) { - ClassOrMetatypeType = CanMetatypeType::get(CanClassType, - MT->getRepresentation()); - } - - // Pass the metatype of the subclass. - auto NewAI = speculateMonomorphicTarget(pm, AI, ClassOrMetatypeType, S, - CanClassType, LastCCBI); - if (!NewAI) { - ++NotHandledSubsNum; - continue; - } - AI = NewAI; - Changed = true; - } - - using namespace OptRemark; - // Check if there is only a single statically known implementation - // of the method which can be called by the default case handler. - if (NotHandledSubsNum || !isDefaultCaseKnown(CHA, AI, CD, Subs)) { - // Devirtualization of remaining cases is not possible, - // because more than one implementation of the method - // needs to be handled here. Thus, an indirect call through - // the class_method cannot be eliminated completely. - // - if (Changed) - ORE.emit([&]() { - RemarkPassed R("PartialSpecDevirt", *AI.getInstruction()); - R << "Partially devirtualized call with run-time checks for " - << NV("NumSubTypesChecked", Subs.size()) << " subclasses of " - << NV("ClassType", ClassType); - if (NotHandledSubsNum) - R << ", number of subclasses not devirtualized: " - << NV("NotHandledSubsNum", NotHandledSubsNum); - if (!isDefaultCaseKnown(CHA, AI, CD, Subs)) - R << ", not all subclasses are known"; - return R; - }); - return Changed; - } - - auto RB = [&]() { - return RemarkPassed("SpecDevirt", *AI.getInstruction()) - << "Devirtualized call with run-time checks for the derived classes " - "of " << NV("ClassType", ClassType); - }; - - // At this point it is known that there is only one remaining method - // implementation which is not covered by checked_cast_br checks yet. - // So, it is safe to replace a class_method invocation by - // a direct call of this remaining implementation. - if (LastCCBI && SubTypeValue == LastCCBI->getOperand()) { - // Remove last checked_cast_br, because it will always succeed. - SILBuilderWithScope B(LastCCBI); - auto CastedValue = B.createUncheckedReinterpretCast( - LastCCBI->getLoc(), LastCCBI->getOperand(), - LastCCBI->getTargetLoweredType()); - B.createBranch(LastCCBI->getLoc(), LastCCBI->getSuccessBB(), {CastedValue}); - LastCCBI->eraseFromParent(); - ORE.emit(RB); - return true; - } - auto NewInst = - tryDevirtualizeClassMethod(pm, AI, SubTypeValue, CD, ClassType, nullptr) - .first; - if (NewInst) { - ORE.emit(RB); - deleteDevirtualizedApply(AI); - return true; - } - - if (Changed) - ORE.emit(RB); - return Changed; -} - -namespace { - /// Speculate the targets of virtual calls by assuming that the requested - /// class is at the bottom of the class hierarchy. - class SpeculativeDevirtualization : public SILFunctionTransform { - public: - ~SpeculativeDevirtualization() override {} - - void run() override { - - auto &CurFn = *getFunction(); - - // Don't perform speculative devirtualization at -Os. - if (CurFn.optimizeForSize()) - return; - - // Don't speculatively devirtualize calls inside thunks. - if (CurFn.isThunk()) - return; - - ClassHierarchyAnalysis *CHA = PM->getAnalysis(); - - bool Changed = false; - - // Collect virtual calls that may be specialized. - SmallVector ToSpecialize; - for (auto &BB : *getFunction()) { - for (auto II = BB.begin(), IE = BB.end(); II != IE; ++II) { - FullApplySite AI = FullApplySite::isa(&*II); - if (AI && isa(AI.getCallee())) - ToSpecialize.push_back(AI); - } - } - - OptRemark::Emitter ORE(DEBUG_TYPE, CurFn); - // Go over the collected calls and try to insert speculative calls. - for (auto AI : ToSpecialize) - Changed |= tryToSpeculateTarget(getPassManager(), AI, CHA, ORE); - - if (Changed) { - CurFn.getModule().linkFunction(&CurFn, SILModule::LinkingMode::LinkAll); - - invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); - } - } - - }; - -} // end anonymous namespace - -SILTransform *swift::createSpeculativeDevirtualization() { - return new SpeculativeDevirtualization(); -} diff --git a/test/SILOptimizer/devirt_base_class.swift b/test/SILOptimizer/devirt_base_class.swift index 76239f4259366..75db2df02ba2f 100644 --- a/test/SILOptimizer/devirt_base_class.swift +++ b/test/SILOptimizer/devirt_base_class.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -enable-spec-devirt -O -emit-sil %s | %FileCheck %s +// RUN: %target-swift-frontend -O -emit-sil %s | %FileCheck %s public class Base1 { @inline(never) func f() -> Int { return 0 } } @@ -24,11 +24,8 @@ private func foo(_ a: A) -> Int { // // CHECK-LABEL: sil private [noinline] @{{.*}}foo -// CHECK-NOT: class_method -// CHECK: checked_cast_br -// CHECK: function_ref -// CHECK: checked_cast_br -// CHECK: function_ref +// This used to check speculative-devirtualization, which we don't have anymore. +// CHECK: class_method return a.f() } diff --git a/test/SILOptimizer/devirt_covariant_return.swift b/test/SILOptimizer/devirt_covariant_return.swift index 1deee904801c3..7b5c33a779db7 100644 --- a/test/SILOptimizer/devirt_covariant_return.swift +++ b/test/SILOptimizer/devirt_covariant_return.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -module-name devirt_covariant_return -Xllvm -sil-full-demangle -enable-spec-devirt -O -Xllvm -disable-sil-cm-rr-cm=0 -Xllvm -sil-inline-generics=false -primary-file %s -Xllvm -sil-print-types -emit-sil -sil-inline-threshold 1000 -Xllvm -sil-disable-pass=ObjectOutliner -sil-verify-all | %FileCheck %s +// RUN: %target-swift-frontend -module-name devirt_covariant_return -Xllvm -sil-full-demangle -O -Xllvm -disable-sil-cm-rr-cm=0 -Xllvm -sil-inline-generics=false -primary-file %s -Xllvm -sil-print-types -emit-sil -sil-inline-threshold 1000 -Xllvm -sil-disable-pass=ObjectOutliner -sil-verify-all | %FileCheck %s // REQUIRES: swift_in_compiler @@ -12,7 +12,6 @@ // CHECK-LABEL: sil hidden @$s23devirt_covariant_return6driveryyF : $@convention(thin) () -> () { // CHECK: bb0 -// CHECK-NOT: alloc_ref // CHECK: function_ref @unknown1a : $@convention(thin) () -> () // CHECK: apply // CHECK: function_ref @defenestrate : $@convention(thin) () -> () @@ -112,8 +111,7 @@ public class Bear { // Check that devirtualizer can handle convenience initializers, which have covariant optional // return types. // CHECK-LABEL: sil @$s23devirt_covariant_return4BearC{{[_0-9a-zA-Z]*}}fC - // CHECK: checked_cast_br [exact] @thick Bear.Type in %{{.*}} : $@thick Bear.Type to @thick GummyBear.Type - // CHECK: upcast %{{.*}} : $Optional to $Optional + // This used to check speculative-devirtualization, which we don't have anymore. // CHECK: } public convenience init?(delegateFailure: Bool, failAfter: Bool) { self.init(fail: delegateFailure) @@ -193,11 +191,7 @@ func driver1(_ c: C1) -> Int32 { // gets properly unwrapped into a Payload object and then further // devirtualized. // CHECK-LABEL: sil hidden [noinline] @$s23devirt_covariant_return7driver3ys5Int32VAA1CCF -// CHECK: bb{{[0-9]+}}(%{{[0-9]+}} : $C2): -// CHECK-NOT: bb{{.*}}: -// check that for C2, we convert the non-optional result into an optional and then cast. -// CHECK: enum $Optional -// CHECK-NEXT: upcast +// This used to check speculative-devirtualization, which we don't have anymore. // CHECK: return @inline(never) func driver3(_ c: C) -> Int32 { @@ -235,12 +229,7 @@ public class D2: D1 { // that D2.foo() is inlined thanks to this. // CHECK-LABEL: sil hidden [noinline] @$s23devirt_covariant_return7driver2ys5Int32VAA2D2CF // CHECK-NOT: class_method -// CHECK: checked_cast_br [exact] D2 in -// CHECK: bb2 -// CHECK: global_addr -// CHECK: load -// CHECK: ref_element_addr -// CHECK: bb3 +// This used to check speculative-devirtualization, which we don't have anymore. // CHECK: class_method // CHECK: } @inline(never) @@ -279,9 +268,7 @@ class EEE : CCC { // Check that c.foo() is devirtualized, because the optimizer can handle the casting the return type // correctly, i.e. it can cast (BBB, BBB) into (AAA, AAA) // CHECK-LABEL: sil hidden [noinline] @$s23devirt_covariant_return37testDevirtOfMethodReturningTupleTypes_1bAA2AAC_AEtAA3CCCC_AA2BBCtF -// CHECK: checked_cast_br [exact] CCC in %{{.*}} : $CCC to CCC -// CHECK: checked_cast_br [exact] CCC in %{{.*}} : $CCC to DDD -// CHECK: checked_cast_br [exact] CCC in %{{.*}} : $CCC to EEE +// This used to check speculative-devirtualization, which we don't have anymore. // CHECK: class_method // CHECK: } @inline(never) @@ -319,11 +306,7 @@ class DDDD : CCCC { // Check devirtualization of methods with optional results, where // optional results need to be casted. // CHECK-LABEL: sil [noinline] @{{.*}}testOverridingMethodWithOptionalResult -// CHECK: checked_cast_br [exact] F in %{{.*}} : $F to F -// CHECK: checked_cast_br [exact] F in %{{.*}} : $F to G -// CHECK: switch_enum -// CHECK: checked_cast_br [exact] F in %{{.*}} : $F to H -// CHECK: switch_enum +// This used to check speculative-devirtualization, which we don't have anymore. @inline(never) public func testOverridingMethodWithOptionalResult(_ f: F) -> (F?, Int)? { return f.foo() diff --git a/test/SILOptimizer/devirt_default_case.swift b/test/SILOptimizer/devirt_default_case.swift index b7232e805bb8d..ebefaa967f538 100644 --- a/test/SILOptimizer/devirt_default_case.swift +++ b/test/SILOptimizer/devirt_default_case.swift @@ -1,6 +1,6 @@ -// RUN: %target-swift-frontend -enable-spec-devirt -O -module-name devirt_default_case -Xllvm -sil-print-types -emit-sil %s | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-NORMAL %s -// RUN: %target-swift-frontend -enable-spec-devirt -O -module-name devirt_default_case -Xllvm -sil-print-types -emit-sil -enable-testing %s | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-TESTABLE %s +// RUN: %target-swift-frontend -O -module-name devirt_default_case -Xllvm -sil-print-types -emit-sil %s | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-NORMAL %s +// RUN: %target-swift-frontend -O -module-name devirt_default_case -Xllvm -sil-print-types -emit-sil -enable-testing %s | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-TESTABLE %s @_silgen_name("action") func action(_ n:Int) -> () @@ -42,9 +42,8 @@ private class Derived2 : Base2 { // Check that call to Base2.middle can be devirtualized // // CHECK-LABEL: sil @$s19devirt_default_case9callOuteryS2iF -// CHECK: function_ref @$s19devirt_default_case5Base233_{{.*}}5inner -// CHECK: function_ref @$s19devirt_default_case8Derived233_{{.*}}6middle -// CHECK-NOT: class_method +// This used to check speculative-devirtualization, which we don't have anymore. +// CHECK: class_method // CHECK: } // end sil function '$s19devirt_default_case9callOuteryS2iF' public func callOuter(_ x: Int) -> Int { @@ -68,9 +67,8 @@ class Base3 { // for testing. // // CHECK-LABEL: sil{{( hidden)?}} [noinline] @$s19devirt_default_case5Base3C5outeryyF : $@convention(method) (@guaranteed Base3) -> () { -// CHECK: function_ref @$s19devirt_default_case5Base3C6middleyyF -// CHECK: function_ref @$s19devirt_default_case8Derived333_{{.*}}6middle -// CHECK-NORMAL-NOT: class_method +// This used to check speculative-devirtualization, which we don't have anymore. +// CHECK-NORMAL: class_method // CHECK-TESTABLE: class_method %0 : $Base3, #Base3.middle // CHECK: } // end sil function '$s19devirt_default_case5Base3C5outeryyF' @inline(never) func outer() { @@ -112,9 +110,8 @@ public func testfoo3() -> Int { // Check that call to A3.f() can be devirtualized. // // CHECK-NORMAL: sil hidden [noinline] @$s19devirt_default_case3fooySiAA2A3CF -// CHECK-NORMAL: function_ref @$s19devirt_default_case2B3C1fSiyFTf4d_n -// CHECK-NORMAL: function_ref @$s19devirt_default_case2A3C1fSiyFTf4d_n -// CHECK-NORMAL-NOT: class_method +// This used to check speculative-devirtualization, which we don't have anymore. +// CHECK-NORMAL: class_method // CHECK: } // end sil function '$s19devirt_default_case3fooySiAA2A3CF' class Base4 { @@ -123,9 +120,8 @@ class Base4 { // Check that call to foo() can be devirtualized // // CHECK-LABEL: sil{{( hidden)?}} [noinline] @$s19devirt_default_case5Base4C4testyyF -// CHECK: function_ref @$s19devirt_default_case5Base4C3fooyyFTf4d_n -// CHECK: function_ref @$s19devirt_default_case8Derived4C3fooyyFTf4d_n -// CHECK-NORMAL-NOT: class_method +// This used to check speculative-devirtualization, which we don't have anymore. +// CHECK-NORMAL: class_method // CHECK-TESTABLE: class_method %0 : $Base4, #Base4.foo // CHECK: } // end sil function '$s19devirt_default_case5Base4C4testyyF' foo() @@ -171,8 +167,7 @@ func check_static_class_devirt(_ c: C6) -> Int { // Check that C.bar() and D.bar() are devirtualized. // // CHECK-LABEL: sil{{( hidden)?}} [noinline] @$s19devirt_default_case019check_static_class_A0ySiAA2C6CF -// CHECK: checked_cast_br [exact] C6 in %0 : $C6 to C6 -// CHECK: checked_cast_br [exact] C6 in %0 : $C6 to D6 +// This used to check speculative-devirtualization, which we don't have anymore. // CHECK: class_method // CHECK: } // end sil function '$s19devirt_default_case019check_static_class_A0ySiAA2C6CF' return c.bar() diff --git a/test/SILOptimizer/devirt_protocol_method_invocations.swift b/test/SILOptimizer/devirt_protocol_method_invocations.swift index 25a9162be3c90..f1dd7be845b8b 100644 --- a/test/SILOptimizer/devirt_protocol_method_invocations.swift +++ b/test/SILOptimizer/devirt_protocol_method_invocations.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -module-name devirt_protocol_method_invocations -enable-spec-devirt -O -Xllvm -sil-disable-pass=ExistentialSpecializer -Xllvm -sil-print-types -emit-sil %s | %FileCheck %s +// RUN: %target-swift-frontend -module-name devirt_protocol_method_invocations -O -Xllvm -sil-disable-pass=ExistentialSpecializer -Xllvm -sil-print-types -emit-sil %s | %FileCheck %s // REQUIRES: swift_in_compiler @@ -100,23 +100,10 @@ public func test_devirt_protocol_extension_method_invocation_with_self_return_ty // apply instructions. // CHECK-LABEL: sil [noinline] @$s34devirt_protocol_method_invocations05test_a1_b1_C11_invocationySiAA1CCF -// CHECK-NOT: witness_method -// CHECK: checked_cast -// CHECK-NOT: checked_cast -// CHECK: bb1( -// CHECK-NOT: checked_cast -// CHECK: return -// CHECK: bb2( -// CHECK-NOT: checked_cast -// CHECK: function_ref -// CHECK: apply -// CHECK: apply -// CHECK: br bb1( -// CHECK: bb3 -// CHECK-NOT: checked_cast +// This used to check speculative-devirtualization, which we don't have anymore. +// CHECK: class_method // CHECK: apply // CHECK: apply -// CHECK: br bb1( // Check that calls of a method boo() from the protocol extension // get devirtualized and are not invoked via the expensive witness_method instruction diff --git a/test/SILOptimizer/devirt_speculate.swift b/test/SILOptimizer/devirt_speculate.swift deleted file mode 100644 index d81c9e7cb678a..0000000000000 --- a/test/SILOptimizer/devirt_speculate.swift +++ /dev/null @@ -1,119 +0,0 @@ -// RUN: %target-swift-frontend %/s -parse-as-library -enable-spec-devirt -O -Xllvm -sil-print-types -emit-sil -save-optimization-record-path %t.opt.yaml | %FileCheck %s -// RUN: %FileCheck -check-prefix=YAML -input-file=%t.opt.yaml %s -// RUN: %target-swift-frontend %/s -parse-as-library -Osize -Xllvm -sil-print-types -emit-sil | %FileCheck %s --check-prefix=OSIZE -// -// Test speculative devirtualization. - -// Test MaxNumSpeculativeTargets. -// rdar:23228386 -public class Base { - public init() {} - public func foo() {} -} - -@_optimize(none) -func blackHole(_: T) {} - -class Sub1 : Base { - override func foo() { blackHole(self) } -} -class Sub2 : Base { - override func foo() { blackHole(self) } -} -class Sub3 : Base { - override func foo() { blackHole(self) } -} -class Sub4 : Base { - override func foo() { blackHole(self) } -} -class Sub5 : Base { - override func foo() { blackHole(self) } -} -class Sub6 : Base { - override func foo() { blackHole(self) } -} -class Sub7 : Base { - override func foo() { blackHole(self) } -} -// CHECK: @$s16devirt_speculate28testMaxNumSpeculativeTargetsyyAA4BaseCF -// CHECK: bb0(%0 : $Base): -// CHECK: checked_cast_br [exact] Base in %0 : $Base to Base, bb2, bb3 - -// CHECK: bb2([[CASTED:%.*]]): -// CHECK: br bb1 - -// CHECK: bb3: -// CHECK: checked_cast_br [exact] Base in %0 : $Base to Sub1, bb4, bb5 - -// CHECK: bb4([[CASTED:%.*]] : $Sub1): -// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () -// CHECK: apply [[FN]]([[CASTED]]) -// CHECK: br bb1 - -// CHECK: bb5: -// CHECK: checked_cast_br [exact] Base in %0 : $Base to Sub2, bb6, bb7 - -// CHECK: bb6([[CASTED:%.*]] : $Sub2): -// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () -// CHECK: apply [[FN]]([[CASTED]]) -// CHECK: br bb1 - -// CHECK: bb7: -// CHECK: checked_cast_br [exact] Base in %0 : $Base to Sub3, bb8, bb9 - -// CHECK: bb8([[CASTED:%.*]] : $Sub3): -// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () -// CHECK: apply [[FN]]([[CASTED]]) -// CHECK: br bb1 - -// CHECK: bb9: -// CHECK: checked_cast_br [exact] Base in %0 : $Base to Sub4, bb10, bb11 - -// CHECK: bb10([[CASTED:%.*]] : $Sub4): -// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () -// CHECK: apply [[FN]]([[CASTED]]) -// CHECK: br bb1 - -// CHECK: bb11: -// CHECK: checked_cast_br [exact] Base in %0 : $Base to Sub5, bb12, bb13 - -// CHECK: bb12([[CASTED:%.*]] : $Sub5): -// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () -// CHECK: apply [[FN]]([[CASTED]]) -// CHECK: br bb1 - -// CHECK: bb13: -// CHECK: checked_cast_br [exact] Base in %0 : $Base to Sub6, bb14, bb15 - -// CHECK: bb14([[CASTED:%.*]] : $Sub6): -// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () -// CHECK: apply [[FN]]([[CASTED]]) -// CHECK: br bb1 - -// CHECK: bb15: -// CHECK-NOT: checked_cast_br -// CHECK: %[[CM:[0-9]+]] = class_method %0 : $Base, #Base.foo : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () -// CHECK: apply %[[CM]](%0) : $@convention(method) (@guaranteed Base) -> () - -// YAML: Pass: sil-speculative-devirtualizer -// YAML-NEXT: Name: sil.PartialSpecDevirt -// YAML-NEXT: DebugLoc: -// YAML: File: {{.*}}/devirt_speculate.swift -// YAML: Line: 118 -// YAML: Column: 5 -// YAML-NEXT: Function: '$s16devirt_speculate28testMaxNumSpeculativeTargetsyyAA4BaseCF' -// YAML-NEXT: Args: -// YAML-NEXT: - String: 'Partially devirtualized call with run-time checks for ' -// YAML-NEXT: - NumSubTypesChecked: '6' -// YAML-NEXT: - String: ' subclasses of ' -// YAML-NEXT: - ClassType: Base -// YAML-NEXT: - String: ', number of subclasses not devirtualized: ' -// YAML-NEXT: - NotHandledSubsNum: '1' -// YAML-NEXT: ... - -// OSIZE: @$s16devirt_speculate28testMaxNumSpeculativeTargetsyyAA4BaseCF -// OSIZE-NOT: checked_cast_br [exact] Base in %0 : $Base to Base -// OSIZE-NOT: checked_cast_br [exact] Base in %0 : $Base to Sub -public func testMaxNumSpeculativeTargets(_ b: Base) { - b.foo() -} diff --git a/test/SILOptimizer/devirt_speculative.sil b/test/SILOptimizer/devirt_speculative.sil deleted file mode 100644 index e4a8b0e7a71c6..0000000000000 --- a/test/SILOptimizer/devirt_speculative.sil +++ /dev/null @@ -1,199 +0,0 @@ -// RUN: %target-sil-opt -sil-print-types -enable-objc-interop -module-name devirt_speculative -enable-sil-verify-all %s -specdevirt | %FileCheck %s - -sil_stage canonical - -import Builtin -import Swift - -@objc class MyNSObject {} -private class Base : MyNSObject { - override init() - @inline(never) func foo() - @inline(never) func exit() -> Never -} - -private class Sub : Base { - override init() - @inline(never) override func foo() - @inline(never) override func exit() -> Never -} - -sil private [noinline] @_TBaseFooFun : $@convention(method) (@guaranteed Base) -> () { -bb0(%0 : $Base): - %1 = tuple() - return %1 : $() -} - -sil private [noinline] @_TSubFooFun : $@convention(method) (@guaranteed Sub) -> () { -bb0(%0 : $Sub): - %1 = tuple() - return %1 : $() -} - - -sil @Base_exit : $@convention(method) (@guaranteed Base) -> Never - -sil @Sub_exit : $@convention(method) (@guaranteed Sub) -> Never - -sil_vtable Base { - #Base.foo: @_TBaseFooFun - #Base.exit: @Base_exit -} - -sil_vtable Sub { - #Base.foo: @_TSubFooFun [override] - #Base.exit: @Sub_exit [override] -} - -sil @test_objc_ancestry : $@convention(thin) (@guaranteed Base) -> () { -bb0(%0: $Base): - %1 = class_method %0 : $Base, #Base.foo : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () - %2 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> () - %3 = tuple() - return %3 : $() -} - -// Make sure we leave the generic method call because an objc derived class can be extended at runtime. - -// CHECK-LABEL: sil @test_objc_ancestry -// CHECK: bb0 -// CHECK: [[METH:%.*]] = class_method %0 : $Base, #Base.foo : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () -// CHECK: checked_cast_br [exact] Base in %0 : $Base to Base, bb{{.*}}, bb[[CHECK2:[0-9]+]] -// CHECK: bb[[CHECK2]]{{.*}}: -// CHECK: checked_cast_br [exact] Base in %0 : $Base to Sub, bb{{.*}}, bb[[GENCALL:[0-9]+]] -// CHECK: bb[[GENCALL]]{{.*}}: -// CHECK: apply [[METH]] - -struct MyValue {} - -private class Generic : MyNSObject { - override init() -} - -private class Base2 : Generic { - override init() - @inline(never) func foo() -} - -private class Sub2 : Base2 { - override init() - @inline(never) override func foo() -} - -sil private [noinline] @_TBase2FooFun : $@convention(method) (@guaranteed Base2) -> () { -bb0(%0 : $Base2): - %1 = tuple() - return %1 : $() -} - -sil private [noinline] @_TSub2FooFun : $@convention(method) (@guaranteed Sub2) -> () { -bb0(%0 : $Sub2): - %1 = tuple() - return %1 : $() -} - -sil_vtable Base2 { - #Base2.foo: @_TBase2FooFun -} - -sil_vtable Sub2 { - #Base2.foo: @_TSub2FooFun [override] -} - -sil @test_objc_ancestry2 : $@convention(thin) (@guaranteed Base2) -> () { -bb0(%0: $Base2): - %1 = class_method %0 : $Base2, #Base2.foo : (Base2) -> () -> (), $@convention(method) (@guaranteed Base2) -> () - %2 = apply %1(%0) : $@convention(method) (@guaranteed Base2) -> () - %3 = tuple() - return %3 : $() -} - -// CHECK-LABEL: sil @test_objc_ancestry2 -// CHECK: bb0 -// CHECK: [[METH:%.*]] = class_method %0 : $Base2, #Base2.foo : (Base2) -> () -> (), $@convention(method) (@guaranteed Base2) -> () -// CHECK: checked_cast_br [exact] Base2 in %0 : $Base2 to Base2, bb{{.*}}, bb[[CHECK2:[0-9]+]] -// CHECK: bb[[CHECK2]]{{.*}}: -// CHECK: checked_cast_br [exact] Base2 in %0 : $Base2 to Sub2, bb{{.*}}, bb[[GENCALL:[0-9]+]] -// CHECK: bb[[GENCALL]]{{.*}}: -// CHECK: apply [[METH]] - -// Check that NoReturn functions are devirtualized properly. -// The new apply should be followed by an unreachable instruction -// instead of a branch instruction. -// CHECK-LABEL: sil{{.*}}test_devirt_of_noreturn_function -// CHECK: checked_cast_br -// CHECK: function_ref @Base_exit -// CHECK-NEXT: apply -// CHECK-NEXT: unreachable -// CHECK: checked_cast_br -// CHECK: function_ref @Sub_exit -// CHECK-NEXT: apply -// CHECK-NEXT: unreachable -sil hidden [noinline] @test_devirt_of_noreturn_function : $@convention(thin) (@owned Base) -> () { -bb0(%0 : $Base): - %2 = class_method %0 : $Base, #Base.exit : (Base) -> () -> Never, $@convention(method) (@guaranteed Base) -> Never - %3 = apply %2(%0) : $@convention(method) (@guaranteed Base) -> Never - unreachable -} - -class Throw { - func mayThrow() throws -} - -class S1 : Throw { - override func mayThrow() -} - -class S2 : Throw { - override func mayThrow() throws -} - -sil hidden [thunk] @$S4main4ThrowC8mayThrowyyKF : $@convention(method) (@guaranteed Throw) -> @error any Error { -bb0(%0 : $Throw): - %1 = tuple () - return %1 : $() -} - -sil hidden @$S4main2S1C8mayThrowyyF : $@convention(method) (@guaranteed S1) -> () { -bb0(%0 : $S1): - %1 = tuple () - return %1 : $() -} - -sil hidden [thunk] [heuristic_always_inline] @$S4main2S2C8mayThrowyyKF : $@convention(method) (@guaranteed S2) -> @error any Error { -bb0(%0 : $S2): - %1 = tuple () - return %1 : $() -} - -// CHECK-LABEL: sil{{.*}} @test_devirt_of_throw_without_error -// CHECK-NOT: checked_cast_br [exact] Throw in %0 : $Throw to S1 -// CHECK: return - -sil hidden [noinline] @test_devirt_of_throw_without_error : $@convention(thin) (@owned Throw) -> () { -bb0(%0 : $Throw): - %80 = class_method %0 : $Throw, #Throw.mayThrow : (Throw) -> () throws -> (), $@convention(method) (@guaranteed Throw) -> @error any Error // user: %81 - try_apply %80(%0) : $@convention(method) (@guaranteed Throw) -> @error any Error, normal bb7, error bb6 - -bb6(%82 : $Error): - br bb1 - -bb7(%84 : $()): - br bb1 - -bb1: - %3 = tuple () - return %3 : $() -} - -sil_vtable Throw { - #Throw.mayThrow: (Throw) -> () throws -> () : @$S4main4ThrowC8mayThrowyyKF -} - -sil_vtable S1 { - #Throw.mayThrow: (Throw) -> () throws -> () : @$S4main2S1C8mayThrowyyF [override] -} - -sil_vtable S2 { - #Throw.mayThrow: (Throw) -> () throws -> () : @$S4main2S2C8mayThrowyyKF [override] -} diff --git a/test/SILOptimizer/devirt_speculative_init.swift b/test/SILOptimizer/devirt_speculative_init.swift deleted file mode 100644 index 8e18d95bf49e0..0000000000000 --- a/test/SILOptimizer/devirt_speculative_init.swift +++ /dev/null @@ -1,36 +0,0 @@ - -// RUN: %target-swift-frontend %s -parse-as-library -enable-spec-devirt -O -Xllvm -sil-print-types -emit-sil | %FileCheck %s -// RUN: %target-swift-frontend %s -parse-as-library -Osize -Xllvm -sil-print-types -emit-sil -// -// Test speculative devirtualization. - -// REQUIRES: swift_in_compiler - -public class Cat { - var cats: Int - - required init(cats: Int) { - self.cats = cats - } -} - -public class BigCat : Cat { - required init(cats: Int) { - super.init(cats: cats) - } -} - -public func make(type: Cat.Type, cats: Int) -> Cat { - return type.init(cats: cats) -} - -// CHECK-LABEL: sil @$s23devirt_speculative_init4make4type4catsAA3CatCAFm_SitF : $@convention(thin) (@thick Cat.Type, Int) -> @owned Cat { -// CHECK: checked_cast_br [exact] @thick Cat.Type in %0 : $@thick Cat.Type to @thick Cat.Type, bb2, bb3 -// CHECK: bb1{{.*}}: -// CHECK: return -// CHECK: bb2({{%.*}} : $@thick Cat.Type): -// CHECK: alloc_ref $Cat -// CHECK: br bb1 -// CHECK: bb3: -// CHECK: alloc_ref $BigCat -// CHECK: br bb1 diff --git a/test/SILOptimizer/devirt_speculative_nested.swift b/test/SILOptimizer/devirt_speculative_nested.swift deleted file mode 100644 index 6af1263fe9579..0000000000000 --- a/test/SILOptimizer/devirt_speculative_nested.swift +++ /dev/null @@ -1,32 +0,0 @@ - -// RUN: %target-swift-frontend -module-name devirt_speculative_nested %s -parse-as-library -enable-spec-devirt -O -Xllvm -sil-print-types -emit-sil | %FileCheck %s -// RUN: %target-swift-frontend -module-name devirt_speculative_nested %s -parse-as-library -Osize -Xllvm -sil-print-types -emit-sil -// -// Test speculative devirtualization. - -public class Outer { - final class Inner : Base { - @inline(never) override func update() { - } - } -} - -public class Base { - @inline(never) func update() { } -} - -// FIXME: We don't speculate to the override Outer.Inner.update() here, -// because we cannot express the cast -- the cast "returns" a new archetype -// T, much like an opened existential. -// -// But at least, we shouldn't crash. - -// CHECK-LABEL: sil @$s25devirt_speculative_nested3foo1xyAA4BaseC_tF : $@convention(thin) (@guaranteed Base) -> () -// CHECK: checked_cast_br [exact] Base in %0 : $Base to Base -// CHECK: function_ref @$s25devirt_speculative_nested4BaseC6updateyyF -// CHECK: class_method %0 : $Base, #Base.update -// CHECK: return - -public func foo(x: Base) { - x.update() -} diff --git a/test/SILOptimizer/devirt_try_apply.sil b/test/SILOptimizer/devirt_try_apply.sil index f61484894ae42..ac998124d0ffe 100644 --- a/test/SILOptimizer/devirt_try_apply.sil +++ b/test/SILOptimizer/devirt_try_apply.sil @@ -1,5 +1,4 @@ // RUN: %target-sil-opt -enable-sil-verify-all %s -devirtualizer -inline | %FileCheck %s --check-prefix=CHECK-DEVIRT -// RUN: %target-sil-opt -enable-sil-verify-all %s -specdevirt | %FileCheck %s --check-prefix=CHECK-SPECDEVIRT // RUN: %target-sil-opt -enable-sil-verify-all %s -mandatory-inlining | %FileCheck %s --check-prefix=CHECK-MANDATORY-INLINING sil_stage canonical @@ -444,11 +443,6 @@ bb0(%0 : $@thick CP2.Type): } -// Check that -specdevirt can devirtualize it -// CHECK-SPECDEVIRT-LABEL: sil [noinline] @_TF16devirt_try_apply19testTryApplyDevirt1FCS_4BaseGSqVs5Int32_ -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - sil [noinline] @_TF16devirt_try_apply19testTryApplyDevirt1FCS_4BaseGSqVs5Int32_ : $@convention(thin) (@owned Base) -> Optional { bb0(%0 : $Base): %1 = alloc_stack $Optional @@ -484,11 +478,6 @@ bb4(%20 : $Error): sil [transparent] [serialized] @_TFSqCurfMGSqq__FT10nilLiteralT__GSqq__ : $@convention(thin) (@thin Optional.Type) -> @out Optional -// Check that -specdevirt can devirtualize it -// CHECK-SPECDEVIRT-LABEL: sil [noinline] @_TF16devirt_try_apply19testTryApplyDevirt2FCS_4BaseGSqS0__ -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - sil [noinline] @_TF16devirt_try_apply19testTryApplyDevirt2FCS_4BaseGSqS0__ : $@convention(thin) (@owned Base) -> @owned Optional { bb0(%0 : $Base): %1 = alloc_stack $Optional @@ -525,11 +514,6 @@ bb4(%24 : $Error): } -// Check that -specdevirt can devirtualize it -// CHECK-SPECDEVIRT-LABEL: sil [noinline] @_TF16devirt_try_apply19testTryApplyDevirt3FCS_4BaseGSqS0__ -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - sil [noinline] @_TF16devirt_try_apply19testTryApplyDevirt3FCS_4BaseGSqS0__ : $@convention(thin) (@owned Base) -> @owned Optional { bb0(%0 : $Base): %1 = alloc_stack $Optional @@ -609,11 +593,6 @@ bb0: } -// Check that -specdevirt can devirtualize it -// CHECK-SPECDEVIRT-LABEL: sil @_TF16devirt_try_apply5test4FT_Vs5Int32 -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - // CHECK-DEVIRT-LABEL: sil @_TF16devirt_try_apply5test4FT_Vs5Int32 // CHECK-DEVIRT-NOT: checked_cast_br [exact] // CHECK-DEVIRT-NOT: class_method @@ -695,10 +674,6 @@ bb4(%23 : $Error): // Check that devirtualization happens even at -inline, without an explicit invocation of the -devirtualize. -// CHECK-SPECDEVIRT-LABEL: sil @_TF16devirt_try_apply5test6FT_GSqVs5Int32_ -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - // CHECK-DEVIRT-LABEL: sil @_TF16devirt_try_apply5test6FT_GSqVs5Int32_ // CHECK-DEVIRT-NOT: class_method // CHECK-DEVIRT: } @@ -736,11 +711,6 @@ bb4(%20 : $Error): br bb3 } -// Check that -specdevirt can devirtualize it -// CHECK-SPECDEVIRT-LABEL: sil @_TF16devirt_try_apply5test7FT_Vs5Int32 -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - // CHECK-DEVIRT-LABEL: sil @_TF16devirt_try_apply5test7FT_Vs5Int32 // CHECK-DEVIRT-NOT: checked_cast_br [exact] // CHECK-DEVIRT-NOT: class_method diff --git a/test/SILOptimizer/devirt_try_apply_ownership.sil b/test/SILOptimizer/devirt_try_apply_ownership.sil index f06a003fd3d3a..444137304005c 100644 --- a/test/SILOptimizer/devirt_try_apply_ownership.sil +++ b/test/SILOptimizer/devirt_try_apply_ownership.sil @@ -418,11 +418,6 @@ bb0(%0 : $@thick CP2.Type): } -// Check that -specdevirt can devirtualize it -// CHECK-SPECDEVIRT-LABEL: sil [noinline] [ossa] @_TF16devirt_try_apply19testTryApplyDevirt1FCS_4BaseGSqVs5Int32_ -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - sil [noinline] [ossa] @_TF16devirt_try_apply19testTryApplyDevirt1FCS_4BaseGSqVs5Int32_ : $@convention(thin) (@owned Base) -> Optional { bb0(%0 : @owned $Base): %1 = alloc_stack $Optional @@ -456,11 +451,6 @@ bb4(%20 : @owned $Error): sil [transparent] [serialized] [ossa] @_TFSqCurfMGSqq__FT10nilLiteralT__GSqq__ : $@convention(thin) (@thin Optional.Type) -> @out Optional -// Check that -specdevirt can devirtualize it -// CHECK-SPECDEVIRT-LABEL: sil [noinline] [ossa] @_TF16devirt_try_apply19testTryApplyDevirt2FCS_4BaseGSqS0__ -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - sil [noinline] [ossa] @_TF16devirt_try_apply19testTryApplyDevirt2FCS_4BaseGSqS0__ : $@convention(thin) (@owned Base) -> @owned Optional { bb0(%0 : @owned $Base): %1 = alloc_stack $Optional @@ -493,11 +483,6 @@ bb4(%24 : @owned $Error): } -// Check that -specdevirt can devirtualize it -// CHECK-SPECDEVIRT-LABEL: sil [noinline] [ossa] @_TF16devirt_try_apply19testTryApplyDevirt3FCS_4BaseGSqS0__ -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - sil [noinline] [ossa] @_TF16devirt_try_apply19testTryApplyDevirt3FCS_4BaseGSqS0__ : $@convention(thin) (@owned Base) -> @owned Optional { bb0(%0 : @owned $Base): %1 = alloc_stack $Optional @@ -567,11 +552,6 @@ bb0: } -// Check that -specdevirt can devirtualize it -// CHECK-SPECDEVIRT-LABEL: sil [ossa] @_TF16devirt_try_apply5test4FT_Vs5Int32 -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - // CHECK-DEVIRT-LABEL: sil [ossa] @_TF16devirt_try_apply5test4FT_Vs5Int32 // CHECK-DEVIRT-NOT: checked_cast_br [exact] // CHECK-DEVIRT-NOT: class_method @@ -653,10 +633,6 @@ bb4(%23 : @owned $Error): // Check that devirtualization happens even at -inline, without an explicit invocation of the -devirtualize. -// CHECK-SPECDEVIRT-LABEL: sil [ossa] @_TF16devirt_try_apply5test6FT_GSqVs5Int32_ -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - // CHECK-DEVIRT-LABEL: sil [ossa] @_TF16devirt_try_apply5test6FT_GSqVs5Int32_ // CHECK-DEVIRT-NOT: class_method // CHECK-DEVIRT: } @@ -692,11 +668,6 @@ bb4(%20 : @owned $Error): br bb3 } -// Check that -specdevirt can devirtualize it -// CHECK-SPECDEVIRT-LABEL: sil [ossa] @_TF16devirt_try_apply5test7FT_Vs5Int32 -// CHECK-SPECDEVIRT: checked_cast_br [exact] -// CHECK-SPECDEVIRT: } - // CHECK-DEVIRT-LABEL: sil [ossa] @_TF16devirt_try_apply5test7FT_Vs5Int32 // CHECK-DEVIRT-NOT: checked_cast_br [exact] // CHECK-DEVIRT-NOT: class_method diff --git a/test/SILOptimizer/devirt_unbound_generic.swift b/test/SILOptimizer/devirt_unbound_generic.swift index 67ee48aa5d0be..c6d5a7142134c 100644 --- a/test/SILOptimizer/devirt_unbound_generic.swift +++ b/test/SILOptimizer/devirt_unbound_generic.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -Xllvm -sil-inline-generics -emit-sorted-sil -Xllvm -sil-print-types -emit-sil -enable-spec-devirt -O %s | %FileCheck %s +// RUN: %target-swift-frontend -Xllvm -sil-inline-generics -emit-sorted-sil -Xllvm -sil-print-types -emit-sil -O %s | %FileCheck %s // We used to crash on this when trying to devirtualize t.boo(a, 1), // because it is an "apply" with replacement types that contain @@ -44,7 +44,7 @@ class Derived : Base { // Check that testDevirt is specialized and uses speculative devirtualization. // CHECK-LABEL: sil shared [noinline] @{{.*}}testDevirt -// CHECK: checked_cast_br [exact] CC in %{{.*}} : $CC to CC +// This used to check speculative-devirtualization, which we don't have anymore. // CHECK: class_method // CHECK: } @inline(never) @@ -193,9 +193,8 @@ public func doTest6() { } // CHECK-LABEL: sil private [noinline] @{{.*}}test7 -// CHECK-NOT: class_method -// CHECK: checked_cast_br -// CHECK-NOT: class_method +// This used to check speculative-devirtualization, which we don't have anymore. +// CHECK: class_method // CHECK: } @inline(never) private func test7(_ c: CP) -> Int32 { diff --git a/test/SILOptimizer/devirt_value_metatypes.swift b/test/SILOptimizer/devirt_value_metatypes.swift index 8c450502b7e95..c7d65fb642ace 100644 --- a/test/SILOptimizer/devirt_value_metatypes.swift +++ b/test/SILOptimizer/devirt_value_metatypes.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -module-name devirt_value_metatypes -emit-sil -enable-spec-devirt -O %s | %FileCheck %s +// RUN: %target-swift-frontend -module-name devirt_value_metatypes -emit-sil -O %s | %FileCheck %s open class A { @inline(never) @@ -15,8 +15,7 @@ class B: A { // CHECK-LABEL: sil {{.*}}@$s22devirt_value_metatypes17testValueMetatypeyyAA1ACF // CHECK: value_metatype $@thick A.Type -// CHECK: checked_cast_br -// CHECK: checked_cast_br +// This used to check speculative-devirtualization, which we don't have anymore. // CHECK: class_method // CHECK: } public func testValueMetatype(_ x:A) { diff --git a/test/SILOptimizer/inline_cache_and_arc.swift b/test/SILOptimizer/inline_cache_and_arc.swift deleted file mode 100644 index f251ee07fd56f..0000000000000 --- a/test/SILOptimizer/inline_cache_and_arc.swift +++ /dev/null @@ -1,37 +0,0 @@ -// RUN: %target-swift-frontend -parse-as-library -enable-spec-devirt -O -emit-sil %s | %FileCheck %s -// REQUIRES: swift_stdlib_no_asserts,optimized_stdlib,CPU=x86_64 - -// Test inline cache with a global class. Make sure the retain|release pair -// for the fast path is removed in the loop. - -open class A -{ - open func f(_ a: Int) -> Int - { - return a + 1 - } -} - -var x = 0 -public var a = A() - -public func testit() { -// CHECK-LABEL: sil @{{.*}}testityyF -// CHECK: bb0: -// CHECK-NOT: {{.*(retain|release).*}} -// CHECK: bb1{{.*}}: -// CHECK-NOT: {{.*(retain|release).*}} -// CHECK: return -// CHECK: bb2{{.*}}: -// CHECK-NOT: {{.*(retain|release|apply).*}} -// CHECK: br bb1 -// CHECK: bb3{{.*}}: -// CHECK-NEXT: class_method -// CHECK-NEXT: strong_retain -// CHECK-NEXT: apply -// CHECK-NEXT: strong_release -// CHECK-NEXT: br bb1 - - x = a.f(x) -} - diff --git a/test/SILOptimizer/inline_caches.sil b/test/SILOptimizer/inline_caches.sil deleted file mode 100644 index 2c2d5b5060ec6..0000000000000 --- a/test/SILOptimizer/inline_caches.sil +++ /dev/null @@ -1,74 +0,0 @@ -// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -specdevirt -code-sinking | %FileCheck %s - -sil_stage canonical - -import Builtin -import Swift - -// Make sure we convert a class_method into a diamond control flow -// where one of the sides is a direct call to FOO::ping. -class Foo { - func ping() -} - -sil @_TFC8testcase3Foo4pingfS0_FT_T_ : $@convention(method) (@guaranteed Foo) -> () -sil @_TFC8testcase3FoocfMS0_FT_S0_ : $@convention(method) (@owned Foo) -> @owned Foo - -sil @_TF8testcase7my_mainFCS_3FooT_ : $@convention(thin) (@owned Foo) -> () { -bb0(%0 : $Foo): - -//CHECK: checked_cast_br [exact] Foo in %0 : $Foo to Foo - -//CHECK: function_ref @_TFC8testcase3Foo4pingfS0_FT_T_ -//CHECK: apply - -//CHECK: class_method -//CHECK-NEXT: apply - %1 = class_method %0 : $Foo, #Foo.ping : (Foo) -> () -> (), $@convention(method) (@guaranteed Foo) -> () // user: %2 - %2 = apply %1(%0) : $@convention(method) (@guaranteed Foo) -> () - %3 = tuple () // user: %4 - return %3 : $() // id: %4 -} - -sil_vtable Foo { - #Foo.ping: @_TFC8testcase3Foo4pingfS0_FT_T_ // testcase.Foo.ping (testcase.Foo)() -> () - #Foo.init!initializer: @_TFC8testcase3FoocfMS0_FT_S0_ // testcase.Foo.init (testcase.Foo.Type)() -> testcase.Foo -} - -class C { - class func foo() - class func bar() -} - -// CHECK-LABEL: sil @_TZFC4spec1C3foofMS0_FT_T_ -sil @_TZFC4spec1C3foofMS0_FT_T_ : $@convention(method) (@thick C.Type) -> () { -bb0(%0 : $@thick C.Type): - %2 = tuple () - return %2 : $() -} - -// CHECK-LABEL: sil @_TZFC4spec1C3barfMS0_FT_T_ -sil @_TZFC4spec1C3barfMS0_FT_T_ : $@convention(method) (@thick C.Type) -> () { - // CHECK: bb0 -bb0(%0 : $@thick C.Type): - // CHECK-NOT: class_method - // CHECK: checked_cast_br [exact] @thick C.Type in %0 : $@thick C.Type to @thick C.Type, bb2, bb3 - // CHECK: bb1 - // CHECK: return - // CHECK: bb2 - // CHECK: [[REF:%.*]] = function_ref @_TZFC4spec1C3foofMS0_FT_T_ - // CHECK: apply [[REF]] - // CHECK: br bb1 - // CHECK: bb3 - // CHECK: [[METHOD:%.*]] = class_method %0 : $@thick C.Type, #C.foo : (C.Type) -> () -> (), $@convention(method) (@thick C.Type) -> () - // CHECK: apply [[METHOD]] - %2 = class_method %0 : $@thick C.Type, #C.foo : (C.Type) -> () -> (), $@convention(method) (@thick C.Type) -> () - %3 = apply %2(%0) : $@convention(method) (@thick C.Type) -> () - %4 = tuple () - return %4 : $() -} - -sil_vtable C { - #C.foo: @_TZFC4spec1C3foofMS0_FT_T_ - #C.bar: @_TZFC4spec1C3barfMS0_FT_T_ -} diff --git a/test/SILOptimizer/inlinecaches_arc.sil b/test/SILOptimizer/inlinecaches_arc.sil deleted file mode 100644 index d81dce9a38930..0000000000000 --- a/test/SILOptimizer/inlinecaches_arc.sil +++ /dev/null @@ -1,44 +0,0 @@ -// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -specdevirt -code-sinking -emit-sorted-sil | %FileCheck %s - -sil_stage canonical - -import Builtin -import Swift - -class Foo { - func ping() -> Int -} - -sil @_TFC4main3Foo4pingfS0_FT_Si : $@convention(method) (@guaranteed Foo) -> Int -sil @_TFC4main3FoocfMS0_FT_S0_ : $@convention(method) (@owned Foo) -> @owned Foo - -// CHECK-LABEL: _TF4main7my_mainFCS_3FooSi -// CHECK: bb0 -// CHECK-NOT: strong_retain -// CHECK: checked_cast_br [exact] Foo in %0 : $Foo to Foo - -// CHECK: bb1 -// CHECK: class_method -// CHECK-NEXT: apply -// CHECK: strong_release - -// CHECK: bb2 -// CHECK: function_ref -// CHECK: apply -// CHECK: strong_release - -// CHECK: bb3 -// CHECK-NOT: strong_release -sil @_TF4main7my_mainFCS_3FooSi : $@convention(thin) (@owned Foo) -> Int { -bb0(%0 : $Foo): - debug_value %0 : $Foo, let, name "x" // id: %1 - %3 = class_method %0 : $Foo, #Foo.ping : (Foo) -> () -> Int, $@convention(method) (@guaranteed Foo) -> Int // user: %4 - %4 = apply %3(%0) : $@convention(method) (@guaranteed Foo) -> Int // user: %6 - strong_release %0 : $Foo // id: %5 - return %4 : $Int // id: %6 -} - -sil_vtable Foo { - #Foo.ping: @_TFC4main3Foo4pingfS0_FT_Si // main.Foo.ping (main.Foo)() -> Swift.Int - #Foo.init!initializer: @_TFC4main3FoocfMS0_FT_S0_ // main.Foo.init (main.Foo.Type)() -> main.Foo -} diff --git a/test/SILOptimizer/inlinecaches_invalidate_failure.sil b/test/SILOptimizer/inlinecaches_invalidate_failure.sil deleted file mode 100644 index 1ace5ed2147a9..0000000000000 --- a/test/SILOptimizer/inlinecaches_invalidate_failure.sil +++ /dev/null @@ -1,108 +0,0 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -inline -specdevirt -code-sinking -sil-verify-without-invalidation - -sil_stage canonical - -import Builtin -import Swift - -public class Foo { - deinit - func doSomething() -> Foo - init() -} - -public class Foo2 : Foo { - deinit - override func doSomething() -> Foo - override init() -} - -// test.Foo.doSomething (test.Foo)() -> test.Foo -sil hidden_external @_TFC4test3Foo11doSomethingfS0_FT_S0_ : $@convention(method) (@guaranteed Foo) -> @owned Foo - -// test.Foo.init (test.Foo.Type)() -> test.Foo -sil hidden_external @_TFC4test3FoocfMS0_FT_S0_ : $@convention(method) (@owned Foo) -> @owned Foo - -// test.Foo2.doSomething (test.Foo2)() -> test.Foo -sil hidden @_TFC4test4Foo211doSomethingfS0_FT_CS_3Foo : $@convention(method) (@guaranteed Foo2) -> @owned Foo { -bb0(%0 : $Foo2): - debug_value %0 : $Foo2 // id: %1 - strong_retain %0 : $Foo2 // id: %2 - %3 = upcast %0 : $Foo2 to $Foo // user: %4 - return %3 : $Foo // id: %4 -} - -// test.Foo2.__deallocating_deinit -sil @_TFC4test4Foo2D : $@convention(method) (@owned Foo2) -> () { -bb0(%0 : $Foo2): - debug_value %0 : $Foo2 // id: %1 - %2 = upcast %0 : $Foo2 to $Foo // user: %4 - // function_ref test.Foo.deinit - %3 = function_ref @_TFC4test3Food : $@convention(method) (@guaranteed Foo) -> @owned Builtin.NativeObject // user: %4 - %4 = apply %3(%2) : $@convention(method) (@guaranteed Foo) -> @owned Builtin.NativeObject // user: %5 - %5 = unchecked_ref_cast %4 : $Builtin.NativeObject to $Foo2 // user: %6 - dealloc_ref %5 : $Foo2 // id: %6 - %7 = tuple () // user: %8 - return %7 : $() // id: %8 -} - -// test.Foo2.deinit -sil @_TFC4test4Foo2d : $@convention(method) (@guaranteed Foo2) -> @owned Builtin.NativeObject { -bb0(%0 : $Foo2): - debug_value %0 : $Foo2 // id: %1 - %2 = upcast %0 : $Foo2 to $Foo // user: %4 - // function_ref test.Foo.deinit - %3 = function_ref @_TFC4test3Food : $@convention(method) (@guaranteed Foo) -> @owned Builtin.NativeObject // user: %4 - %4 = apply %3(%2) : $@convention(method) (@guaranteed Foo) -> @owned Builtin.NativeObject // user: %5 - return %4 : $Builtin.NativeObject // id: %5 -} - -// test.Foo.deinit -sil @_TFC4test3Food : $@convention(method) (@guaranteed Foo) -> @owned Builtin.NativeObject - -// test.Foo2.init (test.Foo2.Type)() -> test.Foo2 -sil hidden @_TFC4test4Foo2cfMS0_FT_S0_ : $@convention(method) (@owned Foo2) -> @owned Foo2 { -bb0(%0 : $Foo2): - %1 = alloc_stack $Foo2 // users: %2, %6, %9, %10 - store %0 to %1 : $*Foo2 // id: %2 - %3 = upcast %0 : $Foo2 to $Foo // user: %7 - // function_ref test.Foo.init (test.Foo.Type)() -> test.Foo - %4 = function_ref @_TFC4test3FoocfMS0_FT_S0_ : $@convention(method) (@owned Foo) -> @owned Foo // user: %7 - %7 = apply %4(%3) : $@convention(method) (@owned Foo) -> @owned Foo // user: %8 - %8 = unchecked_ref_cast %7 : $Foo to $Foo2 // users: %9, %11 - store %8 to %1 : $*Foo2 // id: %9 - dealloc_stack %1 : $*Foo2 // id: %10 - return %8 : $Foo2 // id: %11 -} - -// test.Foo2.__allocating_init (test.Foo2.Type)() -> test.Foo2 -sil hidden @_TFC4test4Foo2CfMS0_FT_S0_ : $@convention(thin) (@thick Foo2.Type) -> @owned Foo2 { -bb0(%0 : $@thick Foo2.Type): - %1 = alloc_ref $Foo2 // users: %3, %4 - %2 = alloc_stack $Foo2 // users: %3, %7, %10, %11 - store %1 to %2 : $*Foo2 // id: %3 - %4 = upcast %1 : $Foo2 to $Foo // user: %8 - // function_ref test.Foo.init (test.Foo.Type)() -> test.Foo - %5 = function_ref @_TFC4test3FoocfMS0_FT_S0_ : $@convention(method) (@owned Foo) -> @owned Foo // user: %8 - %8 = apply %5(%4) : $@convention(method) (@owned Foo) -> @owned Foo // user: %9 - %9 = unchecked_ref_cast %8 : $Foo to $Foo2 // users: %10, %12 - store %9 to %2 : $*Foo2 // id: %10 - dealloc_stack %2 : $*Foo2 // id: %11 - return %9 : $Foo2 // id: %12 -} - -// test.main (test.Foo) -> test.Foo -sil hidden @_TF4test4mainFCS_3FooS0_ : $@convention(thin) (@owned Foo) -> @owned Foo { -bb0(%0 : $Foo): - debug_value %0 : $Foo // id: %1 - %2 = class_method %0 : $Foo, #Foo.doSomething : (Foo) -> () -> Foo, $@convention(method) (@guaranteed Foo) -> @owned Foo // user: %3 - %3 = apply %2(%0) : $@convention(method) (@guaranteed Foo) -> @owned Foo // user: %5 - strong_release %0 : $Foo // id: %4 - return %3 : $Foo // id: %5 -} - -sil_vtable Foo2 { - #Foo.doSomething: @_TFC4test4Foo211doSomethingfS0_FT_CS_3Foo // test.Foo2.doSomething (test.Foo2)() -> test.Foo - #Foo.init!initializer: @_TFC4test4Foo2cfMS0_FT_S0_ // test.Foo2.init (test.Foo2.Type)() -> test.Foo2 - #Foo2.deinit!deallocator: @_TFC4test4Foo2D // test.Foo2.__deallocating_deinit -} diff --git a/test/SILOptimizer/inlinecaches_objc.sil b/test/SILOptimizer/inlinecaches_objc.sil deleted file mode 100644 index 5172116065e23..0000000000000 --- a/test/SILOptimizer/inlinecaches_objc.sil +++ /dev/null @@ -1,36 +0,0 @@ -// RUN: %target-sil-opt -enable-objc-interop -enable-sil-verify-all %s -specdevirt -code-sinking | %FileCheck %s - -sil_stage canonical - -import Builtin -import Swift - -@objc class X { - @objc func ping() -} - -sil @_TFC4main1X4pingfS0_FT_T_ : $@convention(method) (@guaranteed X) -> () -sil @_TFC4main1XcfMS0_FT_S0_ : $@convention(method) (@owned X) -> @owned X - -// CHECK-LABEL: _TFC4main1X4pingfS0_FT_T_ -// CHECK-NOT: checked_cast_br identical -// CHECK: return -sil @_TF4main1fFCS_1XT_ : $@convention(thin) (@owned X) -> () { -bb0(%0 : $X): - debug_value %0 : $X, let, name "x" // id: %1 - strong_retain %0 : $X // id: %2 - %3 = objc_method %0 : $X, #X.ping!foreign : (X) -> () -> () , $@convention(objc_method) (X) -> () // user: %4 - %4 = apply %3(%0) : $@convention(objc_method) (X) -> () - strong_release %0 : $X // id: %5 - strong_release %0 : $X // id: %6 - %7 = tuple () // user: %8 - return %7 : $() // id: %8 -} - -sil_vtable X { - #X.ping: @_TFC4main1X4pingfS0_FT_T_ // main.X.ping (main.X)() -> () - #X.init!initializer: @_TFC4main1XcfMS0_FT_S0_ // main.X.init (main.X.Type)() -> main.X -} - - - diff --git a/test/SILOptimizer/mm_inlinecaches_multiple.sil b/test/SILOptimizer/mm_inlinecaches_multiple.sil deleted file mode 100644 index 8aacdd2168e5d..0000000000000 --- a/test/SILOptimizer/mm_inlinecaches_multiple.sil +++ /dev/null @@ -1,58 +0,0 @@ -// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -specdevirt -code-sinking | %FileCheck %s -// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -specdevirt -code-sinking -wmo | %FileCheck --check-prefix=CHECK-WMO %s - -sil_stage canonical - -import Builtin -import Swift - -// Make sure we convert a class_method into a diamond control flow -// where one of the sides is a direct call to FOO::ping. -class Foo { - func ping() -} - -class Bar : Foo { -} - -sil @_TFC8testcase3Foo4pingfS0_FT_T_ : $@convention(method) (@guaranteed Foo) -> () { -bb0(%0 : $Foo): - %2 = tuple () // user: %3 - return %2 : $() // id: %3 -} - -sil @_TFC8testcase3FoocfMS0_FT_S0_ : $@convention(method) (@owned Foo) -> @owned Foo { -bb0(%0 : $Foo): - return %0 : $Foo // id: %1 -} - - -//CHECK-LABEL: @_TF8testcase7my_mainFCS_3FooT_ -//CHECK: checked_cast_br [exact] Foo in %0 : $Foo to Foo -//CHECK: return -//CHECK: checked_cast_br [exact] Foo in %0 : $Foo to Bar -//CHECK: class_method - -//CHECK-WMO-LABEL: @_TF8testcase7my_mainFCS_3FooT_ -//CHECK-WMO: checked_cast_br [exact] Foo in %0 : $Foo to Foo -//CHECK-WMO: return -//CHECK-WMO: unchecked_ref_cast %0 : $Foo to $Bar -//CHECK-WMO: class_method - -sil @_TF8testcase7my_mainFCS_3FooT_ : $@convention(thin) (@owned Foo) -> () { -bb0(%0 : $Foo): - %1 = class_method %0 : $Foo, #Foo.ping : (Foo) -> () -> () , $@convention(method) (@guaranteed Foo) -> () // user: %2 - %2 = apply %1(%0) : $@convention(method) (@guaranteed Foo) -> () - %3 = tuple () // user: %4 - return %3 : $() // id: %4 -} - -sil_vtable Foo { - #Foo.ping: @_TFC8testcase3Foo4pingfS0_FT_T_ // testcase.Foo.ping (testcase.Foo)() -> () - #Foo.init!initializer: @_TFC8testcase3FoocfMS0_FT_S0_ // testcase.Foo.init (testcase.Foo.Type)() -> testcase.Foo -} - -sil_vtable Bar { - #Foo.ping: @_TFC8testcase3Foo4pingfS0_FT_T_ [inherited] - #Foo.init!initializer: @_TFC8testcase3FoocfMS0_FT_S0_ [inherited] -} diff --git a/test/SILOptimizer/optimize_never.sil b/test/SILOptimizer/optimize_never.sil index 8649dfef05315..eaee8f658e9c3 100644 --- a/test/SILOptimizer/optimize_never.sil +++ b/test/SILOptimizer/optimize_never.sil @@ -1,5 +1,5 @@ // RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all -inline -generic-specializer -inline %s > %t.sil -// RUN: %target-sil-opt -sil-print-types -sil-full-demangle -enable-sil-verify-all -specdevirt %t.sil | %FileCheck %s +// RUN: %target-sil-opt -sil-print-types -sil-full-demangle -enable-sil-verify-all %t.sil | %FileCheck %s // Check that the @_optimize(none) annotation prevents // any optimizations of the annotated function: @@ -324,9 +324,6 @@ bb0(%0 : $*T, %1 : $C): // CHECK-LABEL: sil @$s14optimize_never4boo3ys5Int32VAA1CCF // CHECK: bb0 // CHECK-NOT: function_ref @$s14optimize_never4foo3ys5Int32Vx_AA1CCtlF -// Presence of checked_cast_br indicates that the speculative devirtualization -// was performed. And this is only possible, if the original call is inlined. -// CHECK: checked_cast_br [exact] C in {{%.*}} : $C to C, bb2, bb3 // CHECK: return sil @$s14optimize_never4boo3ys5Int32VAA1CCF : $@convention(thin) (@owned C) -> Int32 { bb0(%0 : $C): @@ -374,9 +371,6 @@ bb0(%0 : $Int32, %1 : $C): // CHECK-LABEL: sil @$s14optimize_never4boo4ys5Int32VAA1CCF // CHECK: bb0 // CHECK-NOT: function_ref @$s14optimize_never4foo4ys5Int32VAD_AA1CCtF -// Presence of checked_cast_br indicates that the speculative devirtualization -// was performed. And this is only possible, if the original call is inlined. -// CHECK: checked_cast_br [exact] C in {{%.*}} : $C to C, bb2, bb3 // CHECK: return sil @$s14optimize_never4boo4ys5Int32VAA1CCF : $@convention(thin) (@owned C) -> Int32 { bb0(%0 : $C): @@ -454,10 +448,8 @@ bb0(%0 : $Base): // // CHECK-LABEL: sil @$s14optimize_never23testDoNotDevirtDerived11os5Int32VAA4BaseC_tF // CHECK: bb0 +// This used to check speculative-devirtualization, which we don't have anymore. // CHECK: class_method {{%[0-9]+}} : $Base, #Base.boo : (Base) -> () -> Int32 -// CHECK: checked_cast_br [exact] Base in {{%[0-9]+}} : $Base to Base -// CHECK: checked_cast_br [exact] Base in {{%[0-9]+}} : $Base to Derived2 -// CHECK-NOT: class_method // CHECK: } sil @$s14optimize_never23testDoNotDevirtDerived11os5Int32VAA4BaseC_tF : $@convention(thin) (@owned Base) -> Int32 { bb0(%0 : $Base): diff --git a/test/SILOptimizer/polymorphic_inline_caches.sil b/test/SILOptimizer/polymorphic_inline_caches.sil deleted file mode 100644 index 029a67a1e6576..0000000000000 --- a/test/SILOptimizer/polymorphic_inline_caches.sil +++ /dev/null @@ -1,231 +0,0 @@ -// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -specdevirt -code-sinking -dce -early-codemotion -disable-sil-cm-rr-cm=0 -retain-sinking | %FileCheck %s - -sil_stage canonical - -import Builtin -import Swift - -class A { - func ping(x: Builtin.Int32) -> Builtin.Int32 -} - -class B : A { - override func ping(x: Builtin.Int32) -> Builtin.Int32 -} - -class C : A { - override func ping(x: Builtin.Int32) -> Builtin.Int32 -} - -class D : C { - override func ping(x: Builtin.Int32) -> Builtin.Int32 -} - -sil @external_func : $@convention(thin) (A) -> Builtin.Int32 - -sil @_TFC25polymorphic_inline_caches1A4pingfS0_FBi32_Bi32_ : $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 -sil @_TFC25polymorphic_inline_caches1B4pingfS0_FBi32_Bi32_ : $@convention(method) (Builtin.Int32, @guaranteed B) -> Builtin.Int32 -sil @_TFC25polymorphic_inline_caches1C4pingfS0_FBi32_Bi32_ : $@convention(method) (Builtin.Int32, @guaranteed C) -> Builtin.Int32 -sil @_TFC25polymorphic_inline_caches1D4pingfS0_FBi32_Bi32_ : $@convention(method) (Builtin.Int32, @guaranteed D) -> Builtin.Int32 - -// CHECK-LABEL: sil @_TF25polymorphic_inline_caches3fooFTCS_1ABi32__Bi32_ -sil @_TF25polymorphic_inline_caches3fooFTCS_1ABi32__Bi32_ : $@convention(thin) (@owned A, Builtin.Int32) -> Builtin.Int32 { -// CHECK: bb0 -bb0(%0 : $A, %1 : $Builtin.Int32): -// CHECK-NOT: strong_retain %0 - strong_retain %0 : $A -// CHECK-NOT: class_method - %5 = class_method %0 : $A, #A.ping : (A) -> (Builtin.Int32) -> Builtin.Int32, $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 - // CHECK-NOT: apply - %6 = apply %5(%1, %0) : $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 - // CHECK: checked_cast_br [exact] A in %0 : $A to A, bb2, bb3 - // CHECK: bb1 - strong_release %0 : $A - // CHECK: return - return %6 : $Builtin.Int32 - // CHECK: bb2 - // CHECK: [[APING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1A4pingfS0_FBi32_Bi32_ - // CHECK: apply [[APING]] - // CHECK: bb3 - // CHECK: checked_cast_br [exact] A in %0 : $A to B, bb5, bb6 - // CHECK: bb4 - // CHECK: br bb1 - // CHECK: bb5 - // CHECK: [[BPING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1B4pingfS0_FBi32_Bi32_ - // CHECK: apply [[BPING]] - // CHECK: bb6 - // CHECK: checked_cast_br [exact] A in %0 : $A to C, bb8, bb9 - // CHECK: bb7 - // CHECK: br bb4 - // CHECK: bb8 - // CHECK: [[CPING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1C4pingfS0_FBi32_Bi32_ - // CHECK: apply [[CPING]] - // CHECK: bb9 - // CHECK: [[PING:%.*]] = class_method %0 : $A, #A.ping - // CHECK: apply [[PING]] -} - -// CHECK-LABEL: sil @polymorphic_inline_caches_dont_sink_retain -sil @polymorphic_inline_caches_dont_sink_retain : $@convention(thin) (@owned A, Builtin.Int32) -> Builtin.Int32 { -// CHECK: bb0 -bb0(%0 : $A, %1 : $Builtin.Int32): -// CHECK: strong_retain %0 - %4 = function_ref @external_func : $@convention(thin) (A) -> Builtin.Int32 - %5 = class_method %0 : $A, #A.ping : (A) -> (Builtin.Int32) -> Builtin.Int32, $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 - - // Don't sink this retain, because its argument is used by %6 (apply of @external_func) - strong_retain %0 : $A -// CHECK-NOT: class_method - // CHECK: apply - // CHECK-NOT: apply - %6 = apply %4(%0) : $@convention(thin) (A) -> Builtin.Int32 - %7 = apply %5(%1, %0) : $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 - // CHECK: checked_cast_br [exact] A in %0 : $A to A, bb2, bb3 - // CHECK: bb1 - strong_release %0 : $A - // CHECK: return - return %6 : $Builtin.Int32 - // CHECK: bb2 - // CHECK: [[APING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1A4pingfS0_FBi32_Bi32_ - // CHECK: apply [[APING]] - // CHECK: bb3 - // CHECK: checked_cast_br [exact] A in %0 : $A to B, bb5, bb6 - // CHECK: bb4 - // CHECK: br bb1 - // CHECK: bb5 - // CHECK: [[BPING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1B4pingfS0_FBi32_Bi32_ - // CHECK: apply [[BPING]] - // CHECK: bb6 - // CHECK: checked_cast_br [exact] A in %0 : $A to C, bb8, bb9 - // CHECK: bb7 - // CHECK: br bb4 - // CHECK: bb8 - // CHECK: [[CPING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1C4pingfS0_FBi32_Bi32_ - // CHECK: apply [[CPING]] - // CHECK: bb9 - // CHECK: [[PING:%.*]] = class_method %0 : $A, #A.ping - // CHECK: apply [[PING]] -} - -sil_vtable A { - #A.ping: @_TFC25polymorphic_inline_caches1A4pingfS0_FBi32_Bi32_ -} - -sil_vtable B { - #A.ping: @_TFC25polymorphic_inline_caches1B4pingfS0_FBi32_Bi32_ [override] -} - -sil_vtable C { - #A.ping: @_TFC25polymorphic_inline_caches1C4pingfS0_FBi32_Bi32_ [override] -} - -sil_vtable D { - #A.ping: @_TFC25polymorphic_inline_caches1D4pingfS0_FBi32_Bi32_ [override] -} - -protocol P { - func foo() -} - -public class E : P { - @inline(never) func foo() - deinit - init() -} - -public class F : E { - @inline(never) override func foo() - deinit - override init() -} - -public final class G : E { - @inline(never) override final func foo() - deinit - override init() -} - -sil [noinline] @_TFC5casts1E3foofS0_FT_T_ : $@convention(method) (@guaranteed E) -> () -sil [noinline] @_TFC5casts1F3foofS0_FT_T_ : $@convention(method) (@guaranteed F) -> () -sil [noinline] @_TFC5casts1G3foofS0_FT_T_ : $@convention(method) (@guaranteed G) -> () - -// CHECK-LABEL: sil [noinline] @_TF5casts18unchecked_ref_castFBoT_ -sil [noinline] @_TF5casts18unchecked_ref_castFBoT_ : $@convention(thin) (@owned Builtin.NativeObject) -> () { - // CHECK: bb0 -bb0(%0 : $Builtin.NativeObject): - strong_retain %0 : $Builtin.NativeObject - // CHECK: [[CAST:%.*]] = unchecked_ref_cast %0 : $Builtin.NativeObject to $E - %3 = unchecked_ref_cast %0 : $Builtin.NativeObject to $E - // CHECK-NOT: class_method - %4 = class_method %3 : $E, #E.foo : (E) -> () -> (), $@convention(method) (@guaranteed E) -> () - %5 = apply %4(%3) : $@convention(method) (@guaranteed E) -> () - strong_release %0 : $Builtin.NativeObject - %7 = tuple () - return %7 : $() - // CHECK: checked_cast_br [exact] E in [[CAST]] : $E to E, bb2, bb3 - // CHECK: bb1 - // CHECK: strong_release %0 - // CHECK: return - // CHECK: bb2 - // CHECK: [[EFOO:%.*]] = function_ref @_TFC5casts1E3foofS0_FT_T_ - // CHECK: strong_retain %0 - // CHECK: apply [[EFOO]] - // CHECK: bb3 - // CHECK: checked_cast_br [exact] E in [[CAST]] : $E to F, bb5, bb6 - // CHECK: bb4 - // CHECK: br bb1 - // CHECK: bb5 - // CHECK: [[FFOO:%.*]] = function_ref @_TFC5casts1F3foofS0_FT_T_ - // CHECK: strong_retain %0 - // CHECK: apply [[FFOO]] - // CHECK: bb6 - // CHECK: checked_cast_br [exact] E in [[CAST]] : $E to G, bb8, bb9 - // CHECK: bb7 - // CHECK: bb8 - // CHECK: [[GFOO:%.*]] = function_ref @_TFC5casts1G3foofS0_FT_T_ - // CHECK: strong_retain %0 - // CHECK: apply [[GFOO]] - // CHECK: bb9 - // CHECK: [[DYNAMIC:%.*]] = class_method [[CAST]] : $E, #E.foo - // CHECK: strong_retain %0 - // CHECK: apply [[DYNAMIC]] -} - -// CHECK-LABEL: sil [noinline] @_TF5casts22unchecked_ref_castFCS_1ET_ -sil [noinline] @_TF5casts22unchecked_ref_castFCS_1ET_ : $@convention(thin) (@owned E) -> () { -// CHECK: bb0 -bb0(%0 : $E): - strong_retain %0 : $E - // CHECK: [[CAST:%.*]] = unchecked_ref_cast %0 : $E to $F - %3 = unchecked_ref_cast %0 : $E to $F - // CHECK-NOT: class_method - %4 = class_method %3 : $F, #F.foo : (F) -> () -> (), $@convention(method) (@guaranteed F) -> () - %5 = apply %4(%3) : $@convention(method) (@guaranteed F) -> () - strong_release %0 : $E - %7 = tuple () - return %7 : $() - // CHECK: checked_cast_br [exact] F in [[CAST]] : $F to F, bb2, bb3 - // CHECK: bb1 - // CHECK: strong_release %0 - // CHECK: return - // CHECK: bb2 - // CHECK: [[FFOO:%.*]] = function_ref @_TFC5casts1F3foofS0_FT_T_ - // CHECK: strong_retain %0 - // CHECK: apply [[FFOO]] - // CHECK: bb3 - // CHECK: [[DYNAMIC:%.*]] = class_method [[CAST]] : $F, #F.foo - // CHECK: strong_retain %0 - // CHECK: apply [[DYNAMIC]] -} - -sil_vtable E { - #E.foo: @_TFC5casts1E3foofS0_FT_T_ -} - -sil_vtable F { - #E.foo: @_TFC5casts1F3foofS0_FT_T_ [override] -} - -sil_vtable G { - #E.foo: @_TFC5casts1G3foofS0_FT_T_ [override] -}