| //===--- FunctionSignatureOptUtils.h ----------------------------*- C++ -*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_SIL_FUNCTIONSIGOPTUTILS_H |
| #define SWIFT_SIL_FUNCTIONSIGOPTUTILS_H |
| |
| #include "swift/SIL/SILInstruction.h" |
| #include "swift/SIL/SILValue.h" |
| #include "swift/SIL/SILArgument.h" |
| #include "swift/SIL/SILFunction.h" |
| #include "swift/SIL/SILDebugScope.h" |
| #include "swift/SIL/Projection.h" |
| #include "swift/SILOptimizer/Analysis/Analysis.h" |
| #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" |
| #include "swift/SILOptimizer/Analysis/ARCAnalysis.h" |
| #include "swift/SILOptimizer/Analysis/CallerAnalysis.h" |
| #include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h" |
| #include "swift/SILOptimizer/Utils/Local.h" |
| #include "swift/SILOptimizer/PassManager/PassManager.h" |
| |
| namespace swift { |
| |
| /// A structure that maintains all of the information about a specific |
| /// SILArgument that we are tracking. |
| struct ArgumentDescriptor { |
| /// The argument that we are tracking original data for. |
| SILFunctionArgument *Arg; |
| |
| /// Parameter Info. |
| Optional<SILParameterInfo> PInfo; |
| |
| /// The original index of this argument. |
| unsigned Index; |
| |
| /// The original decl of this Argument. |
| const ValueDecl *Decl; |
| |
| /// Was this parameter originally dead? |
| bool IsEntirelyDead; |
| |
| /// Should the argument be exploded ? |
| bool Explode; |
| |
| /// This parameter is owned to guaranteed. |
| bool OwnedToGuaranteed; |
| |
| /// Is this parameter an indirect result? |
| bool IsIndirectResult; |
| |
| /// If non-null, this is the release in the return block of the callee, which |
| /// is associated with this parameter if it is @owned. If the parameter is not |
| /// @owned or we could not find such a release in the callee, this is null. |
| ReleaseList CalleeRelease; |
| |
| /// The same as CalleeRelease, but the release in the throw block, if it is a |
| /// function which has a throw block. |
| ReleaseList CalleeReleaseInThrowBlock; |
| |
| /// The projection tree of this arguments. |
| ProjectionTree ProjTree; |
| |
| ArgumentDescriptor() = delete; |
| |
| /// Initialize this argument descriptor with all information from A that we |
| /// use in our optimization. |
| /// |
| /// *NOTE* We cache a lot of data from the argument and maintain a reference |
| /// to the original argument. The reason why we do this is to make sure we |
| /// have access to the original argument's state if we modify the argument |
| /// when optimizing. |
| ArgumentDescriptor(SILFunctionArgument *A) |
| : Arg(A), |
| PInfo(A->getKnownParameterInfo()), |
| Index(A->getIndex()), |
| Decl(A->getDecl()), IsEntirelyDead(false), Explode(false), |
| OwnedToGuaranteed(false), IsIndirectResult(A->isIndirectResult()), |
| CalleeRelease(), CalleeReleaseInThrowBlock(), |
| ProjTree(A->getModule(), A->getType()) { |
| if(!A->isIndirectResult()) { |
| PInfo = Arg->getKnownParameterInfo(); |
| } |
| } |
| |
| ArgumentDescriptor(const ArgumentDescriptor &) = delete; |
| ArgumentDescriptor(ArgumentDescriptor &&) = default; |
| ArgumentDescriptor &operator=(const ArgumentDescriptor &) = delete; |
| ArgumentDescriptor &operator=(ArgumentDescriptor &&) = default; |
| |
| /// \returns true if this argument's convention is P. |
| bool hasConvention(SILArgumentConvention P) const { |
| return Arg->hasConvention(P); |
| } |
| |
| bool canOptimizeLiveArg() const { |
| if (Arg->getType().isObject()) |
| return true; |
| // @in arguments of generic types can be processed. |
| if (Arg->getType().getSwiftRValueType()->hasArchetype() && |
| Arg->getType().isAddress() && |
| (Arg->hasConvention(SILArgumentConvention::Indirect_In) || |
| Arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed))) |
| return true; |
| return false; |
| } |
| |
| /// Return true if it's both legal and a good idea to explode this argument. |
| bool shouldExplode(ConsumedArgToEpilogueReleaseMatcher &ERM) const { |
| // We cannot optimize the argument. |
| if (!canOptimizeLiveArg()) |
| return false; |
| |
| // See if the projection tree consists of potentially multiple levels of |
| // structs containing one field. In such a case, there is no point in |
| // exploding the argument. |
| // |
| // Also, in case of a type can not be exploded, e.g an enum, we treat it |
| // as a singleton. |
| if (ProjTree.isSingleton()) |
| return false; |
| |
| // If this argument is @owned and we can not find all the releases for it |
| // try to explode it, maybe we can find some of the releases and O2G some |
| // of its components. |
| // |
| // This is a potentially a very profitable optimization. Ignore other |
| // heuristics. |
| if (hasConvention(SILArgumentConvention::Direct_Owned) && |
| ERM.hasSomeReleasesForArgument(Arg)) |
| return true; |
| |
| size_t explosionSize = ProjTree.liveLeafCount(); |
| return explosionSize >= 1 && explosionSize <= 3; |
| } |
| |
| llvm::Optional<ValueOwnershipKind> |
| getTransformedOwnershipKind(SILType SubTy) { |
| if (IsEntirelyDead) |
| return None; |
| if (SubTy.isTrivial(Arg->getModule())) |
| return Optional<ValueOwnershipKind>(ValueOwnershipKind::Trivial); |
| if (OwnedToGuaranteed) |
| return Optional<ValueOwnershipKind>(ValueOwnershipKind::Guaranteed); |
| return Arg->getOwnershipKind(); |
| } |
| }; |
| |
| /// A structure that maintains all of the information about a specific |
| /// direct result that we are tracking. |
| struct ResultDescriptor { |
| /// The original parameter info of this argument. |
| SILResultInfo ResultInfo; |
| |
| /// If non-null, this is the release in the return block of the callee, which |
| /// is associated with this parameter if it is @owned. If the parameter is not |
| /// @owned or we could not find such a release in the callee, this is null. |
| llvm::SmallSetVector<SILInstruction *, 1> CalleeRetain; |
| |
| /// This is owned to guaranteed. |
| bool OwnedToGuaranteed; |
| |
| /// Initialize this argument descriptor with all information from A that we |
| /// use in our optimization. |
| /// |
| /// *NOTE* We cache a lot of data from the argument and maintain a reference |
| /// to the original argument. The reason why we do this is to make sure we |
| /// have access to the original argument's state if we modify the argument |
| /// when optimizing. |
| ResultDescriptor() {} |
| ResultDescriptor(SILResultInfo RI) |
| : ResultInfo(RI), CalleeRetain(), OwnedToGuaranteed(false) {} |
| |
| ResultDescriptor(const ResultDescriptor &) = delete; |
| ResultDescriptor(ResultDescriptor &&) = default; |
| ResultDescriptor &operator=(const ResultDescriptor &) = delete; |
| ResultDescriptor &operator=(ResultDescriptor &&) = default; |
| |
| /// \returns true if this argument's ParameterConvention is P. |
| bool hasConvention(ResultConvention R) const { |
| return ResultInfo.getConvention() == R; |
| } |
| }; |
| |
| /// Returns true if F is a function which the pass know show to specialize |
| /// function signatures for. |
| bool canSpecializeFunction(SILFunction *F, |
| const CallerAnalysis::FunctionInfo *FuncInfo, |
| bool OptForPartialApply); |
| |
| /// Return true if this argument is used in a non-trivial way. |
| bool hasNonTrivialNonDebugUse(SILArgument *Arg); |
| |
| } // end namespace swift |
| |
| #endif |