//===--- DIMemoryUseCollector.h -------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 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 // //===----------------------------------------------------------------------===// // // This file declares logic used by definitive analysis related passes that look // at all the instructions that access a memory object. This is quite specific // to definitive analysis in that it is tuple element sensitive instead of // relying on SROA. // //===----------------------------------------------------------------------===// #ifndef SWIFT_SILOPTIMIZER_PASSMANAGER_DIMEMORYUSECOLLECTOR_H #define SWIFT_SILOPTIMIZER_PASSMANAGER_DIMEMORYUSECOLLECTOR_H #include "swift/Basic/LLVM.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILType.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/TinyPtrVector.h" namespace swift { class SILBuilder; namespace ownership { struct DIElementUseInfo; /// This struct holds information about the memory object being analyzed that is /// required to correctly break it down into elements. /// /// This includes a collection of utilities for reasoning about (potentially /// recursively) exploded aggregate elements, and computing access paths and /// indexes into the flattened namespace. /// /// The flattened namespace is assigned lexicographically. For example, in: /// (Int, ((Float, (), Double))) /// the Int member is numbered 0, the Float is numbered 1, and the Double is /// numbered 2. Empty tuples don't get numbered since they contain no state. /// /// Structs and classes have their elements exploded when we are analyzing the /// 'self' member in an initializer for the aggregate. /// /// Derived classes have an additional field at the end that models whether or /// not super.init() has been called or not. class DIMemoryObjectInfo { /// The uninitialized memory that we are analyzing. MarkUninitializedInst *MemoryInst; /// This is the base type of the memory allocation. SILType MemorySILType; /// This is the count of elements being analyzed. For memory objects that are /// tuples, this is the flattened element count. For 'self' members in init /// methods, this is the local field count (+1 for super/self classes were /// initialized). unsigned NumElements; /// True if the memory object being analyzed represents a 'let', which is /// initialize-only (reassignments are not allowed). bool IsLet = false; /// True if NumElements has a dummy value in it to force a struct to be /// non-empty. bool HasDummyElement = false; /// True if this object has a single user of type ProjectBoxInst. bool IsBox = false; public: DIMemoryObjectInfo(MarkUninitializedInst *MemoryInst); SILLocation getLoc() const { return MemoryInst->getLoc(); } SILFunction &getFunction() const { return *MemoryInst->getFunction(); } SILModule &getModule() const { return MemoryInst->getModule(); } SILBasicBlock *getParentBlock() const { return MemoryInst->getParent(); } /// Return the first instruction of the function containing the memory object. SILInstruction *getFunctionEntryPoint() const; CanType getASTType() const { return MemorySILType.getASTType(); } SILType getType() const { return MemorySILType; } /// Returns true if this memory object is of trivial type. bool hasTrivialType() const { return MemorySILType.isTrivial(getFunction()); } /// Returns true if NumElements has a dummy value in it to force a struct to /// be non-empty. bool hasDummyElement() const { return HasDummyElement; } /// Return the actual 'uninitialized' memory. In the case of alloc_ref, /// alloc_stack, this always just returns the actual mark_uninitialized /// instruction. For alloc_box though it returns the project_box associated /// with the memory info. SingleValueInstruction *getUninitializedValue() const; /// Return the number of elements, without the extra "super.init" tracker in /// initializers of derived classes. unsigned getNumMemoryElements() const { return NumElements - (unsigned)isDerivedClassSelf(); } /// Return the number of elements, including the extra "super.init" tracker in /// initializers of derived classes. /// /// \see getNumMemoryElements() for the number of elements, excluding the /// extra "super.init" tracker in the initializers of derived classes. unsigned getNumElements() const { return NumElements; } /// Return true if this is 'self' in any kind of initializer. bool isAnyInitSelf() const { return !MemoryInst->isVar() && !MemoryInst->isOut(); } /// Return uninitialized value of 'self' if current memory object /// is located in an initializer (of any kind). SingleValueInstruction *findUninitializedSelfValue() const; /// True if the memory object is the 'self' argument of a struct initializer. bool isStructInitSelf() const { if (MemoryInst->isRootSelf() || MemoryInst->isCrossModuleRootSelf()) { if (auto decl = getASTType()->getAnyNominal()) { if (isa(decl)) { return true; } } } return false; } /// True if the memory object is the 'self' argument of a non-delegating /// cross-module struct initializer. bool isCrossModuleStructInitSelf() const { if (MemoryInst->isCrossModuleRootSelf()) { assert(isStructInitSelf()); return true; } return false; } /// True if the memory object is the 'self' argument of a class designated /// initializer. bool isClassInitSelf() const { if (MemoryInst->isDelegatingSelf()) return false; if (!MemoryInst->isVar() && !MemoryInst->isOut()) { if (auto decl = getASTType()->getAnyNominal()) { if (isa(decl)) { return true; } } } return false; } /// Returns the initializer if the memory use is 'self' and appears in an /// actor's initializer. Otherwise, returns nullptr. ConstructorDecl *getActorInitSelf() const; /// True if this memory object is the 'self' of a derived class initializer. bool isDerivedClassSelf() const { return MemoryInst->isDerivedClassSelf(); } /// True if this memory object is the 'self' of a derived class initializer for /// which we can assume that all ivars have been initialized. bool isDerivedClassSelfOnly() const { return MemoryInst->isDerivedClassSelfOnly(); } /// True if this memory object is the 'self' of a derived class init method, /// regardless of whether we're tracking ivar initializations or not. bool isAnyDerivedClassSelf() const { return MemoryInst->isDerivedClassSelf() || MemoryInst->isDerivedClassSelfOnly(); } /// True if this memory object is the 'self' of a root class init method. bool isRootClassSelf() const { return isClassInitSelf() && MemoryInst->isRootSelf(); } /// True if this memory object is the 'self' of a non-root class init method. bool isNonRootClassSelf() const { return isClassInitSelf() && !MemoryInst->isRootSelf(); } /// True if this is a delegating initializer, one that calls 'self.init'. bool isDelegatingInit() const { return MemoryInst->isDelegatingSelf() || MemoryInst->isDelegatingSelfAllocated(); } /// True if this is an initializer that initializes stored properties. bool isNonDelegatingInit() const { switch (MemoryInst->getMarkUninitializedKind()) { case MarkUninitializedInst::Var: case MarkUninitializedInst::Out: return false; case MarkUninitializedInst::RootSelf: case MarkUninitializedInst::CrossModuleRootSelf: case MarkUninitializedInst::DerivedSelf: case MarkUninitializedInst::DerivedSelfOnly: return true; case MarkUninitializedInst::DelegatingSelf: case MarkUninitializedInst::DelegatingSelfAllocated: return false; } return false; } bool isRootSelf() const { return MemoryInst->getMarkUninitializedKind() == MarkUninitializedInst::RootSelf; } bool isDelegatingSelfAllocated() const { return MemoryInst->isDelegatingSelfAllocated(); } bool isOut() const { return MemoryInst->isOut(); } enum class EndScopeKind { Borrow, Access }; /// Given an element number (in the flattened sense) return a pointer to a /// leaf element of the specified number. SILValue emitElementAddressForDestroy( unsigned TupleEltNo, SILLocation Loc, SILBuilder &B, SmallVectorImpl> &EndScopeList) const; /// Return the swift type of the specified element. SILType getElementType(unsigned EltNo) const; /// Push the symbolic path name to the specified element number onto the /// specified std::string. If the actual decl (or a subelement thereof) can /// be determined, return it. Otherwise, return null. ValueDecl *getPathStringToElement(unsigned Element, std::string &Result) const; /// If the specified value is a 'let' property in an initializer, return true. bool isElementLetProperty(unsigned Element) const; }; enum DIUseKind { /// The instruction is a Load. Load, /// The instruction is either an initialization or an assignment, we don't /// know which. This classification only happens with values of trivial type /// where the different isn't significant. InitOrAssign, /// The instruction is an initialization of the tuple element. Initialization, /// The instruction is an assignment, overwriting an already initialized /// value. Assign, /// The instruction is a setter call for a computed property after all of /// self is initialized. This is used for property wrappers and for init /// accessors. Set, /// The instruction is a store to a member of a larger struct value. PartialStore, /// This instruction is an init, assignment, or store to a /// @_compilerInitialized field that was _not_ automatically generated BadExplicitStore, /// An 'inout' argument of a function application. InOutArgument, /// An 'inout' self argument of a function application. InOutSelfArgument, /// An indirect 'in' parameter of an Apply instruction. IndirectIn, /// This instruction is a general escape of the value, e.g. a call to a /// closure that captures it. Escape, /// This instruction is a call to 'self.init' in a delegating initializer, /// or a call to 'super.init' in a designated initializer of a derived class.. SelfInit, /// This instruction is a load that's only used to answer a `type(of: self)` /// question. LoadForTypeOfSelf, /// This instruction is a value_metatype on the address of 'self'. TypeOfSelf, /// This instruction is the builtin for flow-sensitive current isolation /// within an actor initializer. It will be replaced with either a copy of /// its argument (injected into an (any Actor)?) or nil. FlowSensitiveSelfIsolation, }; /// This struct represents a single classified access to the memory object /// being analyzed, along with classification information about the access. struct DIMemoryUse { /// This is the instruction accessing the memory. SILInstruction *Inst; /// This is what kind of access it is, load, store, escape, etc. DIUseKind Kind; /// For memory objects of (potentially recursive) tuple type, this keeps /// track of which tuple elements are affected. unsigned FirstElement, NumElements; NullablePtr Field; DIMemoryUse(SILInstruction *Inst, DIUseKind Kind, unsigned FE, unsigned NE, NullablePtr Field = 0) : Inst(Inst), Kind(Kind), FirstElement(FE), NumElements(NE), Field(Field) {} DIMemoryUse() : Inst(nullptr) {} bool isInvalid() const { return Inst == nullptr; } bool isValid() const { return Inst != nullptr; } bool usesElement(unsigned i) const { return i >= FirstElement && i < static_cast(FirstElement + NumElements); } /// onlyTouchesTrivialElements - Return true if all of the accessed elements /// have trivial type and the access itself is a trivial instruction. bool onlyTouchesTrivialElements(const DIMemoryObjectInfo &MemoryInfo) const; /// getElementBitmask - Return a bitmask with the touched tuple elements /// set. APInt getElementBitmask(unsigned NumMemoryTupleElements) const { return APInt::getBitsSet(NumMemoryTupleElements, FirstElement, FirstElement + NumElements); } }; struct DIElementUseInfo { SmallVector Uses; SmallVector Releases; TinyPtrVector StoresToSelf; void trackUse(DIMemoryUse Use) { Uses.push_back(Use); } void trackDestroy(SILInstruction *Destroy) { Releases.push_back(Destroy); } void trackStoreToSelf(SILInstruction *I); }; /// collectDIElementUsesFrom - Analyze all uses of the specified allocation /// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them /// and storing the information found into the Uses and Releases lists. void collectDIElementUsesFrom(const DIMemoryObjectInfo &MemoryInfo, DIElementUseInfo &UseInfo); } // end namespace ownership } // end namespace swift #endif