//===------------- Outliner.cpp - Outlining Transformations ---------------===// // // 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 // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "sil-outliner" #include "swift/AST/ASTMangler.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" #include "swift/Demangling/Demangler.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/SIL/ApplySite.h" #include "swift/SIL/BasicBlockDatastructures.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/DynamicCasts.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/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/OwnershipOptUtils.h" #include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h" #include "llvm/ADT/BitVector.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/raw_ostream.h" using namespace swift; llvm::cl::opt DumpFuncsBeforeOutliner( "sil-dump-functions-before-outliner", llvm::cl::init(""), llvm::cl::desc( "Break before running each function pass on a particular function")); namespace { class OutlinerMangler : public Mangle::ASTMangler { /// The kind of method bridged. enum MethodKind : unsigned { BridgedProperty, BridgedProperty_Consuming, BridgedPropertyAddress, BridgedMethod, }; llvm::BitVector *IsParameterBridged; llvm::BitVector *IsParameterGuaranteed; SILDeclRef MethodDecl; MethodKind Kind; bool IsReturnBridged; public: /// Create an mangler for an outlined bridged method. OutlinerMangler(SILDeclRef Method, llvm::BitVector *ParameterBridged, llvm::BitVector *IsParameterGuaranteed, bool ReturnBridged) : ASTMangler(Method.getASTContext()), IsParameterBridged(ParameterBridged), IsParameterGuaranteed(IsParameterGuaranteed), MethodDecl(Method), Kind(BridgedMethod), IsReturnBridged(ReturnBridged) {} /// Create an mangler for an outlined bridged property. OutlinerMangler(SILDeclRef Method, bool IsAddress, bool ConsumesValue) : ASTMangler(Method.getASTContext()), IsParameterBridged(nullptr), IsParameterGuaranteed(nullptr), MethodDecl(Method), Kind(IsAddress ? BridgedPropertyAddress : (ConsumesValue ? BridgedProperty_Consuming : BridgedProperty)), IsReturnBridged(true) {} std::string mangle(); private: char getMethodKindMangling() { switch (Kind) { case BridgedProperty: return 'p'; case BridgedProperty_Consuming: return 'o'; case BridgedPropertyAddress: return 'a'; case BridgedMethod: return 'm'; } llvm_unreachable("unhandled kind"); } }; } // end anonymous namespace. std::string OutlinerMangler::mangle() { beginManglingWithoutPrefix(); appendOperator(MethodDecl.mangle()); llvm::SmallString<128> Buffer; llvm::raw_svector_ostream Out(Buffer); Out << getMethodKindMangling(); if (IsParameterBridged) { for (unsigned Idx = 0, E = IsParameterBridged->size(); Idx != E; ++Idx) { Out << (IsParameterBridged->test(Idx) ? 'b' : 'n'); // NOTE: We must keep owned as having nothing here to preserve ABI since // mangling is part of ABI. Out << (IsParameterGuaranteed->test(Idx) ? "g" : ""); } } Out << (IsReturnBridged ? 'b' : 'n'); Out << '_'; appendOperator("Te", Buffer); return finalize(); } namespace { class OutlinePattern { protected: SILOptFunctionBuilder &FuncBuilder; InstModCallbacks callbacks; DeadEndBlocks *deBlocks; public: OutlinePattern(SILOptFunctionBuilder &FuncBuilder, InstModCallbacks callbacks, DeadEndBlocks *deBlocks) : FuncBuilder(FuncBuilder), callbacks(callbacks), deBlocks(deBlocks) {} /// Match the instruction sequence. virtual bool matchInstSequence(SILBasicBlock::iterator I) = 0; /// Outline the matched instruction sequence. /// /// If a new outlined function is created return the function. If the outlined /// function already existed return null. /// Returns the last instruction of the matched sequence after the /// replacement. virtual std::pair outline(SILModule &M) = 0; virtual std::string getOutlinedFunctionName() = 0; virtual ~OutlinePattern() {} }; /// Get the bridgeToObjectiveC witness for the type. static SILDeclRef getBridgeToObjectiveC(CanType NativeType) { auto &Ctx = NativeType->getASTContext(); auto Proto = Ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeable); if (!Proto) return SILDeclRef(); auto ConformanceRef = lookupConformance(NativeType, Proto); if (ConformanceRef.isInvalid()) return SILDeclRef(); auto Conformance = ConformanceRef.getConcrete(); // bridgeToObjectiveC DeclName Name(Ctx, Ctx.Id_bridgeToObjectiveC, llvm::ArrayRef()); auto *Requirement = dyn_cast_or_null( Proto->getSingleRequirement(Name)); if (!Requirement) return SILDeclRef(); auto Witness = Conformance->getWitnessDecl(Requirement); return SILDeclRef(Witness); } /// Get the _unconditionallyBridgeFromObjectiveC witness for the type. SILDeclRef getBridgeFromObjectiveC(CanType NativeType) { auto &Ctx = NativeType->getASTContext(); auto Proto = Ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeable); if (!Proto) return SILDeclRef(); auto ConformanceRef = lookupConformance(NativeType, Proto); if (ConformanceRef.isInvalid()) return SILDeclRef(); auto Conformance = ConformanceRef.getConcrete(); // _unconditionallyBridgeFromObjectiveC DeclName Name(Ctx, Ctx.getIdentifier("_unconditionallyBridgeFromObjectiveC"), llvm::ArrayRef(Identifier())); auto *Requirement = dyn_cast_or_null( Proto->getSingleRequirement(Name)); if (!Requirement) return SILDeclRef(); auto Witness = Conformance->getWitnessDecl(Requirement); return SILDeclRef(Witness); } struct SwitchInfo { SwitchEnumInst *SwitchEnum = nullptr; SILBasicBlock *SomeBB = nullptr; SILBasicBlock *NoneBB = nullptr; BranchInst *Br = nullptr; }; /// Pattern for a bridged property call. /// /// bb7: /// %30 = unchecked_take_enum_data_addr %19 : $*Optional, #Optional.some!enumelt /// %31 = load %30 : $*UITextField /// strong_retain %31 : $UITextField /// %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign : (UITextField) -> () -> String?, $@convention(objc_method) (UITextField) -> @autoreleased Optional /// %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional /// switch_enum %34 : $Optional, case #Optional.some!enumelt: bb8, case #Optional.none!enumelt: bb9 /// /// bb8(%36 : $NSString): /// // function_ref static String._unconditionallyBridgeFromObjectiveC(_:) /// %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional, @thin String.Type) -> @owned String /// %38 = enum $Optional, #Optional.some!enumelt, %36 : $NSString /// %39 = metatype $@thin String.Type /// %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional, @thin String.Type) -> @owned String /// %41 = enum $Optional, #Optional.some!enumelt, %40 : $String /// br bb10(%41 : $Optional) /// /// bb9: /// %43 = enum $Optional, #Optional.none!enumelt /// br bb10(%43 : $Optional) /// /// bb10(%45 : $Optional): class BridgedProperty : public OutlinePattern { std::string OutlinedName; SingleValueInstruction *FirstInst; // A load or class_method SILBasicBlock *StartBB; SwitchInfo switchInfo; ObjCMethodInst *ObjCMethod; SILInstruction *Release; ApplyInst *PropApply; SILInstruction *UnpairedRelease; // A release_value | destroy_value following // the apply which isn't paired to // load [copy] | strong_retain first value. public: bool matchInstSequence(SILBasicBlock::iterator I) override; std::pair outline(SILModule &M) override; BridgedProperty(SILOptFunctionBuilder &FuncBuilder, InstModCallbacks callbacks, DeadEndBlocks *deBlocks) : OutlinePattern(FuncBuilder, callbacks, deBlocks) { clearState(); } BridgedProperty(const BridgedProperty&) = delete; BridgedProperty& operator=(const BridgedProperty&) = delete; virtual ~BridgedProperty() {} std::string getOutlinedFunctionName() override; private: bool matchMethodCall(SILBasicBlock::iterator, LoadInst *); CanSILFunctionType getOutlinedFunctionType(SILModule &M); void clearState(); }; } void BridgedProperty::clearState() { FirstInst = nullptr; StartBB = nullptr; switchInfo = SwitchInfo(); ObjCMethod = nullptr; Release = nullptr; PropApply = nullptr; UnpairedRelease = nullptr; OutlinedName.clear(); } std::string BridgedProperty::getOutlinedFunctionName() { if (OutlinedName.empty()) { OutlinerMangler Mangler(ObjCMethod->getMember(), isa(FirstInst), UnpairedRelease); OutlinedName = Mangler.mangle(); } return OutlinedName; } /// Returns the outlined function type. /// /// This depends on the first instruction we matched. Either we matched a load /// or we started the match at the class method instruction. /// /// load %30 : *UITextField: /// (@in_guaranteed InstanceType) -> (@owned Optional) /// objc_method %31 : UITextField /// (@unowned InstanceType) -> (@owned Optional) /// CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) { SmallVector Parameters; if (auto *Load = dyn_cast(FirstInst)) Parameters.push_back( SILParameterInfo(Load->getType().getASTType(), ParameterConvention::Indirect_In_Guaranteed)); else Parameters.push_back(SILParameterInfo( cast(FirstInst)->getOperand()->getType().getASTType(), UnpairedRelease ? ParameterConvention::Direct_Owned : ParameterConvention::Direct_Unowned)); SmallVector Results; Results.push_back(SILResultInfo( switchInfo.Br->getArg(0)->getType().getASTType(), ResultConvention::Owned)); auto ExtInfo = SILFunctionType::ExtInfo::getThin(); auto FunctionType = SILFunctionType::get( nullptr, ExtInfo, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, Parameters, /*yields*/ {}, Results, std::nullopt, SubstitutionMap(), SubstitutionMap(), M.getASTContext()); return FunctionType; } static void eraseBlock(SILBasicBlock *block) { for (SILInstruction &inst : *block) { inst.replaceAllUsesOfAllResultsWithUndef(); } block->eraseFromParent(); } std::pair BridgedProperty::outline(SILModule &M) { // Get the function type. auto FunctionType = getOutlinedFunctionType(M); std::string nameTmp = getOutlinedFunctionName(); auto name = M.allocateCopy(nameTmp); auto *Fun = FuncBuilder.getOrCreateFunction( ObjCMethod->getLoc(), name, SILLinkage::Shared, FunctionType, IsNotBare, IsNotTransparent, IsSerialized, IsNotDynamic, IsNotDistributed, IsNotRuntimeAccessible); bool NeedsDefinition = Fun->empty(); if (Release) { // Move the release after the call. Release->moveBefore(StartBB->getTerminator()); } // [StartBB] // / \ // [NoneBB] [SomeBB] // \ / // [OldMergeBB] // // Split to: // // [StartBB] // | // [OutlinedEntryBB] } // / \ } // [NoneBB] [SomeBB] } outlined // \ / } // [OldMergeBB] } // | // [NewTailBB] // auto *OutlinedEntryBB = StartBB->split(SILBasicBlock::iterator(FirstInst)); auto *OldMergeBB = switchInfo.Br->getDestBB(); auto *NewTailBB = OldMergeBB->split(OldMergeBB->begin()); if (deBlocks) { deBlocks->updateForNewBlock(NewTailBB); } // Call the outlined function. { SILBuilder Builder(StartBB); auto Loc = FirstInst->getLoc(); SILValue FunRef(Builder.createFunctionRef(Loc, Fun)); SILValue Apply( Builder.createApply(Loc, FunRef, SubstitutionMap(), {FirstInst->getOperand(0)})); Builder.createBranch(Loc, NewTailBB); OldMergeBB->getArgument(0)->replaceAllUsesWith(Apply); } if (!NeedsDefinition) { // Delete the outlined instructions/blocks. if (Release) Release->eraseFromParent(); eraseBlock(OutlinedEntryBB); eraseBlock(switchInfo.NoneBB); eraseBlock(switchInfo.SomeBB); eraseBlock(OldMergeBB); return std::make_pair(nullptr, std::prev(StartBB->end())); } if (!OutlinedEntryBB->getParent()->hasOwnership()) Fun->setOwnershipEliminated(); Fun->setInlineStrategy(NoInline); // Move the blocks into the new function. Fun->moveBlockFromOtherFunction(OldMergeBB, Fun->begin()); Fun->moveBlockFromOtherFunction(switchInfo.NoneBB, Fun->begin()); Fun->moveBlockFromOtherFunction(switchInfo.SomeBB, Fun->begin()); Fun->moveBlockFromOtherFunction(OutlinedEntryBB, Fun->begin()); // Create the function argument and return. auto *Load = dyn_cast(FirstInst); SILBuilder Builder(FirstInst); if (Load) { OutlinedEntryBB->createFunctionArgument(Load->getOperand()->getType()); auto *NewLoad = Builder.createLoad(Load->getLoc(), OutlinedEntryBB->getArgument(0), Load->getOwnershipQualifier()); Load->replaceAllUsesWith(NewLoad); Load->eraseFromParent(); } else { OutlinedEntryBB->createFunctionArgument( FirstInst->getOperand(0)->getType()); auto *Arg = OutlinedEntryBB->getArgument(0); FirstInst->setOperand(0, Arg); if (UnpairedRelease) UnpairedRelease->setOperand(0, Arg); PropApply->setArgument(0, Arg); } Builder.setInsertionPoint(OldMergeBB); Builder.createReturn(ObjCMethod->getLoc(), OldMergeBB->getArgument(0)); return std::make_pair(Fun, std::prev(StartBB->end())); } #define ADVANCE_ITERATOR_OR_RETURN_FALSE(It) \ do { \ if (It->getParent()->end() == ++It) \ return false; \ } while (0); static bool matchSwitch(SwitchInfo &SI, SILInstruction *Inst, SILValue SwitchOperand) { auto *SwitchEnum = dyn_cast(Inst); if (!SwitchEnum || SwitchEnum->getNumCases() != 2 || SwitchEnum->getOperand() != SwitchOperand) return false; auto *SwitchBB = SwitchEnum->getParent(); SILBasicBlock *SomeBB = SwitchEnum->getCase(0).second; SILBasicBlock *NoneBB = SwitchEnum->getCase(1).second; if (NoneBB->getSinglePredecessorBlock() != SwitchBB) return false; if (SomeBB->getSinglePredecessorBlock() != SwitchBB) return false; if (NoneBB->args_size() == 1) std::swap(NoneBB, SomeBB); if (SomeBB->args_size() != 1 || NoneBB->args_size() != 0) return false; // bb9: // %43 = enum $Optional, #Optional.none!enumelt auto It = NoneBB->begin(); auto *NoneEnum = dyn_cast(It); if (!NoneEnum || NoneEnum->hasOperand() || !NoneEnum->hasOneUse()) return false; // br bb10(%43 : $Optional) ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Br1 = dyn_cast(It); if (!Br1 || Br1->getNumArgs() != 1 || Br1->getArg(0) != NoneEnum) return false; auto *MergeBB = Br1->getDestBB(); // bb8(%36 : $NSString): It = SomeBB->begin(); auto *SomeBBArg = SomeBB->getArgument(0); if (!SomeBBArg->hasOneUse()) return false; // %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional, @thin String.Type) -> @owned String auto *FunRef = dyn_cast(It); if (!FunRef || !FunRef->hasOneUse()) return false; // %38 = enum $Optional, #Optional.some!enumelt, %36 : $NSString ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *SomeEnum = dyn_cast(It); if (!SomeEnum || !SomeEnum->hasOperand() || SomeEnum->getOperand() != SomeBBArg) return false; size_t numSomeEnumUses = std::distance(SomeEnum->use_begin(), SomeEnum->use_end()); if (numSomeEnumUses > 2) return false; // %39 = metatype $@thin String.Type ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Metatype = dyn_cast(It); if (!Metatype || !Metatype->hasOneUse()) return false; // %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional, @thin String.Type) -> @owned String ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Apply = dyn_cast(It); if (!Apply || !Apply->hasOneUse() || Apply->getCallee() != FunRef || Apply->getNumArguments() != 2 || Apply->getArgument(0) != SomeEnum || Apply->getArgument(1) != Metatype || Apply->getSubstCalleeType()->getNumResults() != 1) return false; if (Apply->getSubstCalleeType()->getSingleResult().getConvention() != ResultConvention::Owned) return false; // Check that we call the _unconditionallyBridgeFromObjectiveC witness. auto NativeType = Apply->getType().getASTType(); auto *BridgeFun = FunRef->getReferencedFunction(); // Not every type conforms to the ObjectiveCBridgeable protocol in such a case // getBridgeFromObjectiveC returns SILDeclRef(). auto bridgeWitness = getBridgeFromObjectiveC(NativeType); if (bridgeWitness == SILDeclRef() || BridgeFun->getName() != bridgeWitness.mangle()) return false; // %41 = enum $Optional, #Optional.some!enumelt, %40 : $String ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Enum3 = dyn_cast(It); if (!Enum3 || !Enum3->hasOneUse() || !Enum3->hasOperand() || Enum3->getOperand() != Apply) return false; if (numSomeEnumUses == 2) { // [release_value | destroy_value] %38 : $Optional ADVANCE_ITERATOR_OR_RETURN_FALSE(It); bool hasOwnership = It->getFunction()->hasOwnership(); if (hasOwnership) { auto *DVI = dyn_cast(It); if (!DVI || DVI->getOperand() != SomeEnum) return false; } else { auto *RVI = dyn_cast(It); if (!RVI || RVI->getOperand() != SomeEnum) return false; } } // br bb10(%41 : $Optional) ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Br = dyn_cast(It); if (!Br || Br->getDestBB() != MergeBB || Br->getNumArgs() != 1 || Br->getArg(0) != Enum3) return false; SI.SwitchEnum = SwitchEnum; SI.SomeBB = SomeBB; SI.NoneBB = NoneBB; SI.Br = Br; return true; } bool BridgedProperty::matchMethodCall(SILBasicBlock::iterator It, LoadInst *Load) { // Matches: // %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign : (UITextField) -> () -> String?, $@convention(objc_method) (UITextField) -> @autoreleased Optional // %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional // switch_enum %34 : $Optional, case #Optional.some!enumelt: bb8, case #Optional.none!enumelt: bb9 // // bb8(%36 : $NSString): // %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional, @thin String.Type) -> @owned String // %38 = enum $Optional, #Optional.some!enumelt, %36 : $NSString // %39 = metatype $@thin String.Type // %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional, @thin String.Type) -> @owned String // %41 = enum $Optional, #Optional.some!enumelt, %40 : $String // br bb10(%41 : $Optional) // // bb9: // %43 = enum $Optional, #Optional.none!enumelt // br bb10(%43 : $Optional) // // bb10(%45 : $Optional): // // %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign ObjCMethod = dyn_cast(It); SILValue Instance = FirstInst != ObjCMethod ? FirstInst : ObjCMethod->getOperand(); if (!ObjCMethod || !ObjCMethod->hasOneUse() || ObjCMethod->getOperand() != Instance || ObjCMethod->getFunction()->getLoweredFunctionType()->isPolymorphic() || ObjCMethod->getType().castTo()->isPolymorphic() || ObjCMethod->getType().castTo()->hasOpenedExistential()) return false; // %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional ADVANCE_ITERATOR_OR_RETURN_FALSE(It); PropApply = dyn_cast(It); if (!PropApply || PropApply->getCallee() != ObjCMethod || PropApply->getNumArguments() != 1 || PropApply->getArgument(0) != Instance || !PropApply->hasOneUse()) return false; if (Load) { // In OSSA, there will be a destroy_value matching the earlier load [copy]. // In non-ossa, there will be a release matching the earlier retain. The // only user of the retained value is the unowned objective-c method // consumer. unsigned NumUses = 0; Release = nullptr; bool hasOwnership = Load->getFunction()->hasOwnership(); for (auto *Use : Load->getUses()) { ++NumUses; SILInstruction *R; if (hasOwnership) { R = dyn_cast(Use->getUser()); } else { R = dyn_cast(Use->getUser()); } if (R) { if (!Release) { Release = R; } else { Release = nullptr; break; } } } if (!Release) return false; if (hasOwnership) { if (NumUses != 3) return false; } else { if (NumUses != 4) return false; } ADVANCE_ITERATOR_OR_RETURN_FALSE(It); if (Release != &*It) return false; } ADVANCE_ITERATOR_OR_RETURN_FALSE(It); if (auto *dvi = dyn_cast(*&It)) { if (Load) return false; if (dvi->getOperand() != Instance) return false; UnpairedRelease = dvi; ADVANCE_ITERATOR_OR_RETURN_FALSE(It); } // Don't outline in the outlined function. if (ObjCMethod->getFunction()->getName() == getOutlinedFunctionName()) return false; // switch_enum %34 : $Optional, case #Optional.some!enumelt: bb8, // case #Optional.none!enumelt: bb9 return matchSwitch(switchInfo, &*It, PropApply); } bool BridgedProperty::matchInstSequence(SILBasicBlock::iterator It) { // Matches: // [ optionally: // %31 = load %30 : $*UITextField or %31 = load [copy] %30 : $*UITextField // strong_retain %31 : $UITextField // ] // %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign : (UITextField) -> () -> String?, $@convention(objc_method) (UITextField) -> @autoreleased Optional // %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional // switch_enum %34 : $Optional, case #Optional.some!enumelt: bb8, case #Optional.none!enumelt: bb9 // // bb8(%36 : $NSString): // %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional, @thin String.Type) -> @owned String // %38 = enum $Optional, #Optional.some!enumelt, %36 : $NSString // %39 = metatype $@thin String.Type // %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional, @thin String.Type) -> @owned String // %41 = enum $Optional, #Optional.some!enumelt, %40 : $String // br bb10(%41 : $Optional) // // bb9: // %43 = enum $Optional, #Optional.none!enumelt // br bb10(%43 : $Optional) // // bb10(%45 : $Optional): clearState(); // %31 = load %30 : $*UITextField auto *Load = dyn_cast(It); // Otherwise, trying matching from the method call. if (!Load) { // Try to match without the load/strong_retain prefix. auto *CMI = dyn_cast(It); if (!CMI || CMI->getFunction()->getLoweredFunctionType()->isPolymorphic() || CMI->getType().castTo()->isPolymorphic() || CMI->getType().castTo()->hasOpenedExistential()) return false; FirstInst = CMI; } else FirstInst = Load; StartBB = FirstInst->getParent(); if (Load) { if (Load->getFunction()->hasOwnership()) { if (Load->getOwnershipQualifier() != LoadOwnershipQualifier::Copy) return false; ADVANCE_ITERATOR_OR_RETURN_FALSE(It); } else { // strong_retain %31 : $UITextField ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Retain = dyn_cast(It); if (!Retain || Retain->getOperand() != Load) return false; ADVANCE_ITERATOR_OR_RETURN_FALSE(It); } } if (!matchMethodCall(It, Load)) return false; return true; } namespace { /// Match a bridged argument. /// /// /// %15 = function_ref @$SSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF /// %16 = apply %15(%14) : /// $@convention(method) (@guaranteed String) -> @owned NSString /// %17 = enum $Optional, #Optional.some!enumelt, %16 : $NSString /// release_value %14 : $String /// /// apply %objcMethod(%17, ...) : $@convention(objc_method) (Optional ...) -> /// release_value %17 : $Optional /// /// NOTE: If release_value %14 is found, our outlined function will have an /// owned convention for self. Otherwise, if we do not find it, we will have a /// guaranteed one. class BridgedArgument { public: FunctionRefInst *BridgeFun; ApplyInst *BridgeCall; EnumInst *OptionalResult; SILValue BridgedValue; SILInstruction *ReleaseAfterBridge; SILInstruction *ReleaseArgAfterCall; unsigned Idx = 0; // Matched bridged argument. BridgedArgument(unsigned Idx, FunctionRefInst *F, ApplyInst *A, EnumInst *E, SILInstruction *R0, SILInstruction *R1) : BridgeFun(F), BridgeCall(A), OptionalResult(E), BridgedValue(FullApplySite(A).getSelfArgument()), ReleaseAfterBridge(R0), ReleaseArgAfterCall(R1), Idx(Idx) { assert(!R0 || isa(R0) || isa(R0)); } /// Invalid argument constructor. BridgedArgument() : BridgeFun(nullptr), BridgeCall(nullptr), OptionalResult(nullptr), ReleaseAfterBridge(nullptr), ReleaseArgAfterCall(nullptr), Idx(0) {} static BridgedArgument match(unsigned ArgIdx, SILValue Arg, ApplyInst *AI, DeadEndBlocks *deBlocks); operator bool() const { return BridgeFun != nullptr; } SILValue bridgedValue() { return BridgedValue; } bool isGuaranteed() const { return ReleaseAfterBridge == nullptr; } ParameterConvention getConvention() const { if (isGuaranteed()) return ParameterConvention::Direct_Guaranteed; return ParameterConvention::Direct_Owned; } void eraseFromParent(); /// Move the bridged argument sequence to the bridged call block. /// Precondition: The bridged call has already been moved to the outlined /// function. void transferTo(SILValue BridgedValueFunArg, ApplyInst *BridgedCall); }; } void BridgedArgument::transferTo(SILValue BridgedValue, ApplyInst *BridgedCall) { assert(BridgedCall->getParent() != BridgeFun->getParent()); // Move the instructions to the bridged call that we have already moved and // update the uses of the bridge value by the function argument value passed // to this function. auto *DestBB = BridgedCall->getParent(); DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), BridgeFun); DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), BridgeCall); BridgeCall->setArgument(0, BridgedValue); DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), OptionalResult); if (ReleaseAfterBridge) { DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), ReleaseAfterBridge); ReleaseAfterBridge->setOperand(0, BridgedValue); } auto AfterCall = std::next(SILBasicBlock::iterator(BridgedCall)); DestBB->moveTo(SILBasicBlock::iterator(AfterCall), ReleaseArgAfterCall); } void BridgedArgument::eraseFromParent() { if (ReleaseAfterBridge) ReleaseAfterBridge->eraseFromParent(); ReleaseArgAfterCall->eraseFromParent(); OptionalResult->eraseFromParent(); BridgeCall->eraseFromParent(); BridgeFun->eraseFromParent(); } static SILInstruction *findReleaseOf(SILValue releasedValue, SILBasicBlock::iterator from, SILBasicBlock::iterator to) { bool hasOwnership = releasedValue->getFunction()->hasOwnership(); while (from != to) { if (hasOwnership) { auto destroy = dyn_cast(&*from); if (destroy && destroy->getOperand() == releasedValue) return destroy; } else { auto release = dyn_cast(&*from); if (release && release->getOperand() == releasedValue) return release; } ++from; } return nullptr; } BridgedArgument BridgedArgument::match(unsigned ArgIdx, SILValue Arg, ApplyInst *AI, DeadEndBlocks *deBlocks) { // Match // %15 = function_ref @$SSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF // %16 = apply %15(%14) : $@convention(method) (@guaranteed String) -> @owned NSString // %17 = enum $Optional, #Optional.some!enumelt, %16 : $NSString // [release_value | destroy_value] %14 : $String // ... // apply %objcMethod(%17, ...) : $@convention(objc_method) (Optional...) -> // release_value ... // [release_value | destroy_value] %17 : $Optional // auto *Enum = dyn_cast(Arg); if (!Enum || !Enum->hasOperand()) return BridgedArgument(); if (SILBasicBlock::iterator(Enum) == Enum->getParent()->begin()) return BridgedArgument(); auto *BridgeCall = dyn_cast(std::prev(SILBasicBlock::iterator(Enum))); if (!BridgeCall || BridgeCall->getNumArguments() != 1 || Enum->getOperand() != BridgeCall || !BridgeCall->hasOneUse() || !FullApplySite(BridgeCall).hasSelfArgument()) return BridgedArgument(); auto &selfArg = FullApplySite(BridgeCall).getSelfArgumentOperand(); auto selfConvention = FullApplySite(BridgeCall).getArgumentConvention(selfArg); if (selfConvention != SILArgumentConvention::Direct_Guaranteed && selfConvention != SILArgumentConvention::Direct_Owned) return BridgedArgument(); auto BridgedValue = BridgeCall->getArgument(0); auto Next = std::next(SILBasicBlock::iterator(Enum)); if (Next == Enum->getParent()->end()) return BridgedArgument(); // Make sure that if we have a bridged value release that it is on the bridged // value. if (Enum->getParent() != AI->getParent()) return BridgedArgument(); bool hasOwnership = AI->getFunction()->hasOwnership(); auto *BridgedValueRelease = findReleaseOf(BridgedValue, std::next(SILBasicBlock::iterator(Enum)), SILBasicBlock::iterator(AI)); assert(!BridgedValueRelease || (hasOwnership && isa(BridgedValueRelease)) || isa(BridgedValueRelease)); if (BridgedValueRelease && BridgedValueRelease->getOperand(0) != BridgedValue) return BridgedArgument(); if (SILBasicBlock::iterator(BridgeCall) == BridgeCall->getParent()->begin()) return BridgedArgument(); auto *FunRef = dyn_cast(std::prev(SILBasicBlock::iterator(BridgeCall))); if (!FunRef || !FunRef->hasOneUse() || BridgeCall->getCallee() != FunRef) return BridgedArgument(); SILInstruction *ReleaseAfter = nullptr; for (auto *Use : Enum->getUses()) { if (Use->getUser() == AI) continue; // The enum must only have two uses the release and the apply. if (ReleaseAfter) return BridgedArgument(); if (hasOwnership) { ReleaseAfter = dyn_cast(Use->getUser()); } else { ReleaseAfter = dyn_cast(Use->getUser()); } if (!ReleaseAfter) return BridgedArgument(); } // Make sure we are calling the actual bridge witness. auto NativeType = BridgedValue->getType().getASTType(); auto *BridgeFun = FunRef->getReferencedFunction(); // Not every type conforms to the ObjectiveCBridgeable protocol in such a case // getBridgeToObjectiveC returns SILDeclRef(). auto bridgeWitness = getBridgeToObjectiveC(NativeType); if (bridgeWitness == SILDeclRef() || BridgeFun->getName() != bridgeWitness.mangle()) return BridgedArgument(); if (hasOwnership && !BridgedValueRelease) { SmallVector newUses{&AI->getOperandRef(ArgIdx)}; if (!areUsesWithinValueLifetime(BridgedValue, newUses, deBlocks)) { return BridgedArgument(); } } return BridgedArgument(ArgIdx, FunRef, BridgeCall, Enum, BridgedValueRelease, ReleaseAfter); } namespace { // Match the return value bridging pattern. // switch_enum %20 : $Optional, case #O.some: bb1, case #O.none: bb2 // // bb1(%23 : $NSString): // %24 = function_ref @_unconditionallyBridgeFromObjectiveC // %25 = enum $Optional, #Optional.some!enumelt, %23 : $NSString // %26 = metatype $@thin String.Type // %27 = apply %24(%25, %26) // %28 = enum $Optional, #Optional.some!enumelt, %27 : $String // br bb3(%28 : $Optional) // // bb2: // %30 = enum $Optional, #Optional.none!enumelt // br bb3(%30 : $Optional) // // bb3(%32 : $Optional): class BridgedReturn { DeadEndBlocks *deBlocks; SwitchInfo switchInfo; public: BridgedReturn(DeadEndBlocks *deBlocks) : deBlocks(deBlocks) {} bool match(ApplyInst *BridgedCall) { switchInfo = SwitchInfo(); auto *SwitchBB = BridgedCall->getParent(); return matchSwitch(switchInfo, SwitchBB->getTerminator(), BridgedCall); } operator bool() { return switchInfo.SomeBB != nullptr; } CanType getReturnType() { return switchInfo.Br->getArg(0)->getType().getASTType(); } /// Outline the return value bridging blocks. void outline(SILFunction *Fun, ApplyInst *NewOutlinedCall); }; } void BridgedReturn::outline(SILFunction *Fun, ApplyInst *NewOutlinedCall) { // Outline the bridged return result blocks. // switch_enum %20 : $Optional, case #O.some: bb1, case #O.none: bb2 // // bb1(%23 : $NSString): // %24 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveC // %25 = enum $Optional, #Optional.some!enumelt, %23 : $NSString // %26 = metatype $@thin String.Type // %27 = apply %24(%25, %26) // %28 = enum $Optional, #Optional.some!enumelt, %27 : $String // br bb3(%28 : $Optional) // // bb2: // %30 = enum $Optional, #Optional.none!enumelt // br bb3(%30 : $Optional) // // bb3(%32 : $Optional): auto *StartBB = switchInfo.SwitchEnum->getParent(); auto *OutlinedEntryBB = StartBB->split(SILBasicBlock::iterator(switchInfo.SwitchEnum)); auto *OldMergeBB = switchInfo.Br->getDestBB(); auto *NewTailBB = OldMergeBB->split(OldMergeBB->begin()); if (deBlocks) { deBlocks->updateForNewBlock(NewTailBB); } auto Loc = switchInfo.SwitchEnum->getLoc(); { SILBuilder Builder(StartBB); Builder.createBranch(Loc, NewTailBB); OldMergeBB->getArgument(0)->replaceAllUsesWith(NewOutlinedCall); } // Outlined function already existed. Just delete instructions and wire up // blocks. if (!Fun) { eraseBlock(OutlinedEntryBB); eraseBlock(switchInfo.NoneBB); eraseBlock(switchInfo.SomeBB); eraseBlock(OldMergeBB); return; } // Move the blocks into the new function. assert(Fun->begin() != Fun->end() && "The entry block must already have been created"); SILBasicBlock *EntryBB = &*Fun->begin(); Fun->moveBlockFromOtherFunction(OldMergeBB, Fun->begin()); Fun->moveBlockAfter(OldMergeBB, EntryBB); auto InsertPt = SILFunction::iterator(OldMergeBB); Fun->moveBlockFromOtherFunction(OutlinedEntryBB, InsertPt); Fun->moveBlockFromOtherFunction(switchInfo.NoneBB, InsertPt); Fun->moveBlockFromOtherFunction(switchInfo.SomeBB, InsertPt); SILBuilder Builder (EntryBB); Builder.createBranch(Loc, OutlinedEntryBB); Builder.setInsertionPoint(OldMergeBB); Builder.createReturn(Loc, OldMergeBB->getArgument(0)); } namespace { class ObjCMethodCall : public OutlinePattern { ObjCMethodInst *ObjCMethod; ApplyInst *BridgedCall; SmallVector BridgedArguments; std::string OutlinedName; llvm::BitVector IsBridgedArgument; llvm::BitVector IsGuaranteedArgument; ::BridgedReturn BridgedReturn; public: bool matchInstSequence(SILBasicBlock::iterator I) override; std::pair outline(SILModule &M) override; ObjCMethodCall(SILOptFunctionBuilder &FuncBuilder, InstModCallbacks callbacks, DeadEndBlocks *deBlocks) : OutlinePattern(FuncBuilder, callbacks, deBlocks), BridgedReturn(deBlocks) {} ~ObjCMethodCall(); private: void clearState(); std::string getOutlinedFunctionName() override; CanSILFunctionType getOutlinedFunctionType(SILModule &M); }; } ObjCMethodCall::~ObjCMethodCall() { clearState(); } void ObjCMethodCall::clearState() { ObjCMethod = nullptr; BridgedCall = nullptr; BridgedArguments.clear(); OutlinedName.clear(); IsBridgedArgument.clear(); IsGuaranteedArgument.clear(); } std::pair ObjCMethodCall::outline(SILModule &M) { auto FunctionType = getOutlinedFunctionType(M); std::string nameTmp = getOutlinedFunctionName(); auto name = M.allocateCopy(nameTmp); auto *Fun = FuncBuilder.getOrCreateFunction( ObjCMethod->getLoc(), name, SILLinkage::Shared, FunctionType, IsNotBare, IsNotTransparent, IsSerialized, IsNotDynamic, IsNotDistributed, IsNotRuntimeAccessible); bool NeedsDefinition = Fun->empty(); // Call the outlined function. ApplyInst *OutlinedCall; { SILBuilder Builder(BridgedCall); auto Loc = BridgedCall->getLoc(); SILValue FunRef(Builder.createFunctionRef(Loc, Fun)); // Collect the arguments for the apply. SmallVector Args; unsigned OrigSigIdx = 0; unsigned BridgedArgIdx = 0; for (auto Arg : BridgedCall->getArguments()) { if (BridgedArgIdx < BridgedArguments.size() && BridgedArguments[BridgedArgIdx].Idx == OrigSigIdx) { auto bridgedArgValue = BridgedArguments[BridgedArgIdx].bridgedValue(); Args.push_back(bridgedArgValue); ++BridgedArgIdx; } else { // Otherwise, use the original type convention. Args.push_back(Arg); } ++OrigSigIdx; } OutlinedCall = Builder.createApply(Loc, FunRef, SubstitutionMap(), Args); if (!BridgedCall->use_empty() && !BridgedReturn) BridgedCall->replaceAllUsesWith(OutlinedCall); } // Outlined function already exists. Only need to delete basic blocks/instructions. if (!NeedsDefinition) { if (BridgedReturn) BridgedReturn.outline(nullptr, OutlinedCall); BridgedCall->eraseFromParent(); ObjCMethod->eraseFromParent(); // Remove bridged argument code. for (auto Arg : BridgedArguments) Arg.eraseFromParent(); SILBasicBlock::iterator I(OutlinedCall); return std::make_pair(Fun, I); } if (!ObjCMethod->getFunction()->hasOwnership()) Fun->setOwnershipEliminated(); Fun->setInlineStrategy(NoInline); // Create the entry block. auto *EntryBB = Fun->createBasicBlock(); // Move the bridged call. EntryBB->moveTo(EntryBB->end(), ObjCMethod); EntryBB->moveTo(EntryBB->end(), BridgedCall); // Create the arguments. unsigned OrigSigIdx = 0; unsigned BridgedArgIdx = 0; SILValue LastArg; for (auto Arg : BridgedCall->getArguments()) { if (BridgedArgIdx < BridgedArguments.size() && BridgedArguments[BridgedArgIdx].Idx == OrigSigIdx) { auto &BridgedArg = BridgedArguments[BridgedArgIdx]; auto *FunArg = EntryBB->createFunctionArgument(BridgedArg.bridgedValue()->getType()); BridgedArg.transferTo(FunArg, BridgedCall); ++BridgedArgIdx; } else { auto *FunArg = EntryBB->createFunctionArgument(Arg->getType()); BridgedCall->setArgument(OrigSigIdx, FunArg); LastArg = FunArg; } ++OrigSigIdx; } // Set the method lookup's target. ObjCMethod->setOperand(LastArg); // Create the return and optionally move the bridging code. if (!BridgedReturn) { SILBuilder Builder(EntryBB); Builder.createReturn(BridgedCall->getLoc(), BridgedCall); } else { BridgedReturn.outline(Fun, OutlinedCall); } SILBasicBlock::iterator I(OutlinedCall); return std::make_pair(Fun, I); } std::string ObjCMethodCall::getOutlinedFunctionName() { if (OutlinedName.empty()) { OutlinerMangler Mangler(ObjCMethod->getMember(), &IsBridgedArgument, &IsGuaranteedArgument, BridgedReturn); OutlinedName = Mangler.mangle(); } return OutlinedName; } bool ObjCMethodCall::matchInstSequence(SILBasicBlock::iterator I) { clearState(); ObjCMethod = dyn_cast(I); if (!ObjCMethod || ObjCMethod->getFunction()->getLoweredFunctionType()->isPolymorphic() || ObjCMethod->getType().castTo()->isPolymorphic() || ObjCMethod->getType().castTo()->hasOpenedExistential()) return false; auto *Use = ObjCMethod->getSingleUse(); if (!Use) return false; BridgedCall = dyn_cast(Use->getUser()); if (!BridgedCall || (!BridgedCall->hasOneUse() && !BridgedCall->use_empty()) || ObjCMethod->getParent() != BridgedCall->getParent()) return false; // Collect bridged parameters. unsigned Idx = 0; IsBridgedArgument.resize(BridgedCall->getNumArguments(), false); IsGuaranteedArgument.resize(BridgedCall->getNumArguments(), false); // Map from owned values whose bridged versions we've seen in the apply to // the index in the apply at which they appear or UINT_MAX if we've seen them // more than once (which means we've already nulled out all the // BridgedArguments' ReleaseAfterBridge). llvm::DenseMap seenOwnedBridgedValues; for (auto &Param : BridgedCall->getArgumentOperands()) { unsigned CurIdx = Idx++; // Look for Optional type. auto Ty = Param.get()->getType().getOptionalObjectType(); if (!Ty) continue; // Can't handle AnyObject. The concrete class type can be different since we // are passing 'id'. To make this work we would have to mangle the type into // the function name. if (Ty.isAnyObject()) continue; auto BridgedArg = BridgedArgument::match(CurIdx, Param.get(), BridgedCall, deBlocks); if (!BridgedArg) continue; BridgedArguments.push_back(BridgedArg); IsBridgedArgument.set(CurIdx); if (BridgedArg.isGuaranteed()) { IsGuaranteedArgument.set(CurIdx); continue; } // Record that this owned value was used at CurIdx. auto pair = seenOwnedBridgedValues.insert({BridgedArg.BridgedValue, CurIdx}); auto firstSighting = pair.second; if (firstSighting) { continue; } // This owned value was already seen. Convert the current argument to // guaranteed and the previous argument as well if necessary. auto iterator = pair.first; if (iterator->second != UINT_MAX) { // This is the _second_ time the value has been seen. Clear the previous // occurrence's ReleaseAfterBridge and sink the destroy after the apply. BridgedArguments[iterator->second].ReleaseAfterBridge->moveAfter( BridgedCall); BridgedArguments[iterator->second].ReleaseAfterBridge = nullptr; IsGuaranteedArgument.set(iterator->second); iterator->second = UINT_MAX; } BridgedArguments[CurIdx].ReleaseAfterBridge = nullptr; IsGuaranteedArgument.set(CurIdx); } // Try to match a bridged return value. BridgedReturn.match(BridgedCall); // Don't outline inside the outlined function. auto OutlinedName = getOutlinedFunctionName(); auto CurrentName = ObjCMethod->getFunction()->getName(); if (CurrentName == OutlinedName) return false; // Don't outline if we created an outlined function without the bridged result // from the outlined function with the bridged result (only the suffix is // different: MethodNameTem...n_ vs MethodNameTem...b_). if (OutlinedName.size() == CurrentName.size() && CurrentName.starts_with( StringRef(OutlinedName.c_str(), OutlinedName.size() - 2))) return false; return !BridgedArguments.empty(); } CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) { auto FunTy = BridgedCall->getSubstCalleeType(); SmallVector Parameters; unsigned OrigSigIdx = 0; unsigned BridgedArgIdx = 0; for (auto &ParamInfo : FunTy->getParameters()) { // Either use the bridged type passing it @owned. if (BridgedArgIdx < BridgedArguments.size() && BridgedArguments[BridgedArgIdx].Idx == OrigSigIdx) { auto convention = BridgedArguments[BridgedArgIdx].getConvention(); Parameters.push_back(SILParameterInfo(BridgedArguments[BridgedArgIdx] .bridgedValue() ->getType() .getASTType(), convention)); ++BridgedArgIdx; } else { // Otherwise, use the original type convention. Parameters.push_back(ParamInfo); } ++OrigSigIdx; } auto ExtInfo = SILFunctionType::ExtInfo::getThin(); SmallVector Results; // If we don't have a bridged return we changed from @autoreleased to @owned // if there is a result. if (!BridgedReturn) { if (FunTy->getNumResults()) { auto OrigResultInfo = FunTy->getSingleResult(); Results.push_back(SILResultInfo(OrigResultInfo.getInterfaceType(), OrigResultInfo.getConvention() == ResultConvention::Autoreleased ? ResultConvention::Owned : OrigResultInfo.getConvention())); } } else { // Otherwise, we used the bridged return type. Results.push_back( SILResultInfo(BridgedReturn.getReturnType(), ResultConvention::Owned)); } auto FunctionType = SILFunctionType::get( nullptr, ExtInfo, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, Parameters, {}, Results, std::nullopt, SubstitutionMap(), SubstitutionMap(), M.getASTContext()); return FunctionType; } namespace { /// A collection of outlineable patterns. class OutlinePatterns { BridgedProperty BridgedPropertyPattern; ObjCMethodCall ObjCMethodCallPattern; public: /// Try matching an outlineable pattern from the current instruction. OutlinePattern *tryToMatch(SILBasicBlock::iterator CurInst) { if (BridgedPropertyPattern.matchInstSequence(CurInst)) return &BridgedPropertyPattern; if (ObjCMethodCallPattern.matchInstSequence(CurInst)) return &ObjCMethodCallPattern; return nullptr; } OutlinePatterns(SILOptFunctionBuilder &FuncBuilder, InstModCallbacks callbacks, DeadEndBlocks *deBlocks) : BridgedPropertyPattern(FuncBuilder, callbacks, deBlocks), ObjCMethodCallPattern(FuncBuilder, callbacks, deBlocks) {} ~OutlinePatterns() {} OutlinePatterns(const OutlinePatterns&) = delete; OutlinePatterns& operator=(const OutlinePatterns) = delete; }; } // end anonymous namespace. /// Perform outlining on the function and return any newly created outlined /// functions. bool tryOutline(SILOptFunctionBuilder &FuncBuilder, SILFunction *Fun, SmallVectorImpl &FunctionsAdded, InstModCallbacks callbacks = InstModCallbacks(), DeadEndBlocks *deBlocks = nullptr) { BasicBlockWorklist Worklist(Fun->getEntryBlock()); OutlinePatterns patterns(FuncBuilder, callbacks, deBlocks); bool changed = false; // Traverse the function. while (SILBasicBlock *CurBlock = Worklist.pop()) { SILBasicBlock::iterator CurInst = CurBlock->begin(); // Go over the instructions trying to match and replace patterns. while (CurInst != CurBlock->end()) { if (OutlinePattern *match = patterns.tryToMatch(CurInst)) { SILFunction *F; SILBasicBlock::iterator LastInst; std::tie(F, LastInst) = match->outline(Fun->getModule()); if (F) FunctionsAdded.push_back(F); CurInst = LastInst; assert(LastInst->getParent() == CurBlock); changed = true; } else if (isa(CurInst)) { for (SILBasicBlock *succ : CurBlock->getSuccessors()) { Worklist.pushIfNotVisited(succ); } ++CurInst; } else { ++CurInst; } } } return changed; } namespace { class Outliner : public SILFunctionTransform { public: Outliner() { } void run() override { auto *Fun = getFunction(); // Only outline if we optimize for size. if (!Fun->optimizeForSize()) return; // Dump function if requested. if (DumpFuncsBeforeOutliner.size() && Fun->getName().contains(DumpFuncsBeforeOutliner)) { Fun->dump(); } DeadEndBlocksAnalysis *deBlocksAnalysis = PM->getAnalysis(); DeadEndBlocks *deBlocks = deBlocksAnalysis->get(Fun); InstModCallbacks callbacks; SILOptFunctionBuilder FuncBuilder(*this); SmallVector FunctionsAdded; bool Changed = false; if (Fun->hasOwnership()) { Changed = tryOutline(FuncBuilder, Fun, FunctionsAdded, callbacks, deBlocks); } else { Changed = tryOutline(FuncBuilder, Fun, FunctionsAdded); } if (!FunctionsAdded.empty()) { // Notify the pass manager of any new functions we outlined. for (auto *AddedFunc : FunctionsAdded) { addFunctionToPassManagerWorklist(AddedFunc, nullptr); } } if (Changed) { invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); } } }; } //end anonymous namespace. SILTransform *swift::createOutliner() { return new Outliner(); }