//===--- SILFunction.h - Defines the SILFunction class ----------*- 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 // //===----------------------------------------------------------------------===// // // This file defines the SILFunction class. // //===----------------------------------------------------------------------===// #ifndef SWIFT_SIL_SILFUNCTION_H #define SWIFT_SIL_SILFUNCTION_H #include "swift/AST/ASTNode.h" #include "swift/AST/AvailabilityRange.h" #include "swift/AST/Module.h" #include "swift/AST/ResilienceExpansion.h" #include "swift/Basic/ProfileCounter.h" #include "swift/Basic/SwiftObjectHeader.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILDeclRef.h" #include "swift/SIL/SILLinkage.h" #include "swift/SIL/SILPrintContext.h" #include "swift/SIL/SILUndef.h" #include "llvm/ADT/MapVector.h" namespace swift { class ASTContext; class SILInstruction; class SILModule; class SILFunctionBuilder; class SILProfiler; class BasicBlockBitfield; class NodeBitfield; class OperandBitfield; class CalleeCache; class SILUndef; namespace Lowering { class TypeLowering; class AbstractionPattern; } enum IsBare_t { IsNotBare, IsBare }; enum IsTransparent_t { IsNotTransparent, IsTransparent }; enum Inline_t { InlineDefault, NoInline, AlwaysInline }; enum IsThunk_t { IsNotThunk, IsThunk, IsReabstractionThunk, IsSignatureOptimizedThunk, IsBackDeployedThunk, }; enum IsDynamicallyReplaceable_t { IsNotDynamic, IsDynamic }; enum IsExactSelfClass_t { IsNotExactSelfClass, IsExactSelfClass, }; enum IsDistributed_t { IsNotDistributed, IsDistributed, }; enum IsRuntimeAccessible_t { IsNotRuntimeAccessible, IsRuntimeAccessible }; enum ForceEnableLexicalLifetimes_t { DoNotForceEnableLexicalLifetimes, DoForceEnableLexicalLifetimes }; enum UseStackForPackMetadata_t { DoNotUseStackForPackMetadata, DoUseStackForPackMetadata, }; enum class PerformanceConstraints : uint8_t { None = 0, NoAllocation = 1, NoLocks = 2, NoRuntime = 3, NoExistentials = 4, NoObjCBridging = 5 }; class SILSpecializeAttr final { friend SILFunction; public: enum class SpecializationKind { Full, Partial }; static GenericSignature buildTypeErasedSignature( GenericSignature sig, ArrayRef typeErasedParams); static SILSpecializeAttr * create(SILModule &M, GenericSignature specializedSignature, ArrayRef typeErasedParams, bool exported, SpecializationKind kind, SILFunction *target, Identifier spiGroup, const ModuleDecl *spiModule, AvailabilityRange availability); bool isExported() const { return exported; } bool isFullSpecialization() const { return kind == SpecializationKind::Full; } bool isPartialSpecialization() const { return kind == SpecializationKind::Partial; } SpecializationKind getSpecializationKind() const { return kind; } GenericSignature getSpecializedSignature() const { return specializedSignature; } GenericSignature getUnerasedSpecializedSignature() const { return unerasedSpecializedSignature; } ArrayRef getTypeErasedParams() const { return typeErasedParams; } SILFunction *getFunction() const { return F; } SILFunction *getTargetFunction() const { return targetFunction; } Identifier getSPIGroup() const { return spiGroup; } const ModuleDecl *getSPIModule() const { return spiModule; } AvailabilityRange getAvailability() const { return availability; } void print(llvm::raw_ostream &OS) const; private: SpecializationKind kind; bool exported; GenericSignature specializedSignature; GenericSignature unerasedSpecializedSignature; llvm::SmallVector typeErasedParams; Identifier spiGroup; AvailabilityRange availability; const ModuleDecl *spiModule = nullptr; SILFunction *F = nullptr; SILFunction *targetFunction = nullptr; SILSpecializeAttr(bool exported, SpecializationKind kind, GenericSignature specializedSignature, GenericSignature unerasedSpecializedSignature, ArrayRef typeErasedParams, SILFunction *target, Identifier spiGroup, const ModuleDecl *spiModule, AvailabilityRange availability); }; /// SILFunction - A function body that has been lowered to SIL. This consists of /// zero or more SIL SILBasicBlock objects that contain the SILInstruction /// objects making up the function. class SILFunction : public llvm::ilist_node, public SILAllocated, public SwiftObjectHeader { private: void *libswiftSpecificData[4]; public: using BlockListType = llvm::iplist; // For more information see docs/SIL.rst enum class Purpose : uint8_t { None, GlobalInit, GlobalInitOnceFunction, LazyPropertyGetter }; private: friend class SILBasicBlock; friend class SILModule; friend class SILFunctionBuilder; template friend class BasicBlockData; template friend class SILBitfield; friend class BasicBlockBitfield; friend class NodeBitfield; friend class OperandBitfield; friend SILUndef; /// Module - The SIL module that the function belongs to. SILModule &Module; /// The mangled name of the SIL function, which will be propagated /// to the binary. A pointer into the module's lookup table. StringRef Name; /// A single-linked list of snapshots of the function. /// /// Snapshots are copies of the current function at a given point in time. SILFunction *snapshots = nullptr; /// The snapshot ID of this function. /// /// 0 means, it's not a snapshot, but the original function. int snapshotID = 0; /// The lowered type of the function. CanSILFunctionType LoweredType; /// The context archetypes of the function. GenericEnvironment *GenericEnv = nullptr; /// Captured local generic environments. ArrayRef CapturedEnvs; /// The information about specialization. /// Only set if this function is a specialization of another function. const GenericSpecializationInformation *SpecializationInfo = nullptr; /// The forwarding substitution map, lazily computed. SubstitutionMap ForwardingSubMap; /// The collection of all BasicBlocks in the SILFunction. Empty for external /// function references. BlockListType BlockList; /// The owning declaration of this function's clang node, if applicable. ValueDecl *ClangNodeOwner = nullptr; /// The source location and scope of the function. const SILDebugScope *DebugScope = nullptr; /// The AST decl context of the function. DeclContext *DeclCtxt = nullptr; /// The module that defines this function. This member should only be set as /// a fallback when a \c DeclCtxt is unavailable. ModuleDecl *ParentModule = nullptr; /// The profiler for instrumentation based profiling, or null if profiling is /// disabled. SILProfiler *Profiler = nullptr; /// The function this function is meant to replace. Null if this is not a /// @_dynamicReplacement(for:) function. SILFunction *ReplacedFunction = nullptr; /// The SILDeclRef that this function was created for by SILGen if one exists. SILDeclRef DeclRef = SILDeclRef(); /// This SILFunction REFerences an ad-hoc protocol requirement witness in /// order to keep it alive, such that it main be obtained in IRGen. Without /// this explicit reference, the witness would seem not-used, and not be /// accessible for IRGen. /// /// Specifically, one such case is the DistributedTargetInvocationDecoder's /// 'decodeNextArgument' which must be retained, as it is only used from IRGen /// and such, appears as-if unused in SIL and would get optimized away. // TODO: Consider making this a general "references adhoc functions" and make it an array? SILFunction *RefAdHocRequirementFunction = nullptr; Identifier ObjCReplacementFor; /// The head of a single-linked list of currently alive BasicBlockBitfield. BasicBlockBitfield *newestAliveBlockBitfield = nullptr; /// The head of a single-linked list of currently alive NodeBitfield. NodeBitfield *newestAliveNodeBitfield = nullptr; /// The head of a single-linked list of currently alive OperandBitfields. OperandBitfield *newestAliveOperandBitfield = nullptr; /// A monotonically increasing ID which is incremented whenever a /// BasicBlockBitfield, NodeBitfield, or OperandBitfield is constructed. For /// details see SILBitfield::bitfieldID; uint64_t currentBitfieldID = 1; /// Unique identifier for vector indexing and deterministic sorting. /// May be reused when zombie functions are recovered. unsigned index; /// The function's set of semantics attributes. /// /// TODO: Why is this using a std::string? Why don't we use uniqued /// StringRefs? std::vector SemanticsAttrSet; /// The function's remaining set of specialize attributes. std::vector SpecializeAttrSet; /// Name of a section if @_section attribute was used, otherwise empty. StringRef Section; /// Name of a Wasm export if @_expose(wasm) attribute was used, otherwise /// empty. StringRef WasmExportName; /// Name of a Wasm import module and field if @_extern(wasm) attribute std::optional> WasmImportModuleAndField; /// Has value if there's a profile for this function /// Contains Function Entry Count ProfileCounter EntryCount; /// The availability used to determine if declarations of this function /// should use weak linking. AvailabilityRange Availability; Purpose specialPurpose = Purpose::None; PerformanceConstraints perfConstraints = PerformanceConstraints::None; /// The undefs of each type in the function. llvm::SmallMapVector undefValues; /// This is the number of uses of this SILFunction inside the SIL. /// It does not include references from debug scopes. unsigned RefCount = 0; /// Used to verify if a BasicBlockData is not valid anymore. /// This counter is incremented every time a BasicBlockData re-assigns new /// block indices. unsigned BlockListChangeIdx = 0; /// The isolation of this function. std::optional actorIsolation; /// The function's bare attribute. Bare means that the function is SIL-only /// and does not require debug info. unsigned Bare : 1; /// The function's transparent attribute. unsigned Transparent : 1; /// The function's serialized attribute. unsigned SerializedKind : 2; /// Specifies if this function is a thunk or a reabstraction thunk. /// /// The inliner uses this information to avoid inlining (non-trivial) /// functions into the thunk. unsigned Thunk : 3; /// The scope in which the parent class can be subclassed, if this is a method /// which is contained in the vtable of that class. unsigned ClassSubclassScope : 2; /// The function's global_init attribute. unsigned GlobalInitFlag : 1; /// The function's noinline attribute. unsigned InlineStrategy : 2; /// The linkage of the function. unsigned Linkage : NumSILLinkageBits; /// Set if the function may be referenced from C code and should thus be /// preserved and exported more widely than its Swift linkage and usage /// would indicate. unsigned HasCReferences : 1; /// Whether attribute @_used was present unsigned MarkedAsUsed : 1; /// Whether cross-module references to this function should always use weak /// linking. unsigned IsAlwaysWeakImported : 1; /// Whether the implementation can be dynamically replaced. unsigned IsDynamicReplaceable : 1; /// If true, this indicates that a class method implementation will always be /// invoked with a `self` argument of the exact base class type. unsigned ExactSelfClass : 1; /// Check whether this is a distributed method. unsigned IsDistributed : 1; /// Check whether this function could be looked up at runtime via special API. unsigned IsRuntimeAccessible : 1; unsigned stackProtection : 1; /// True if this function is inlined at least once. This means that the /// debug info keeps a pointer to this function. unsigned Inlined : 1; /// True if this function is a zombie function. This means that the function /// is dead and not referenced from anywhere inside the SIL. But it is kept /// for other purposes: /// *) It is inlined and the debug info keeps a reference to the function. /// *) It is a dead method of a class which has higher visibility than the /// method itself. In this case we need to create a vtable stub for it. /// *) It is a function referenced by the specialization information. unsigned Zombie : 1; /// True if this function is in Ownership SSA form and thus must pass /// ownership verification. /// /// This enables the verifier to easily prove that before the Ownership Model /// Eliminator runs on a function, we only see a non-semantic-arc world and /// after the pass runs, we only see a semantic-arc world. unsigned HasOwnership : 1; /// Set if the function body was deserialized from canonical SIL. This implies /// that the function's home module performed SIL diagnostics prior to /// serialization. unsigned WasDeserializedCanonical : 1; /// True if this is a reabstraction thunk of escaping function type whose /// single argument is a potentially non-escaping closure. This is an escape /// hatch to allow non-escaping functions to be stored or passed as an /// argument with escaping function type. The thunk argument's function type /// is not necessarily @noescape. The only relevant aspect of the argument is /// that it may have unboxed capture (i.e. @inout_aliasable parameters). unsigned IsWithoutActuallyEscapingThunk : 1; /// If != OptimizationMode::NotSet, the optimization mode specified with an /// function attribute. unsigned OptMode : NumOptimizationModeBits; /// The function's effects attribute. unsigned EffectsKindAttr : NumEffectsKindBits; /// If true, the function has lexical lifetimes even if the module does not. unsigned ForceEnableLexicalLifetimes : 1; /// If true, the function contains an instruction that prevents stack nesting /// from running with pack metadata markers in place. unsigned UseStackForPackMetadata : 1; /// If true, the function returns a non-escapable value without any /// lifetime-dependence on an argument. unsigned HasUnsafeNonEscapableResult : 1; /// True, if this function or a caller (transitively) has a performance /// constraint. /// If true, optimizations must not introduce new runtime calls or metadata /// creation, which are not there after SILGen. /// Note that this flag is not serialized, because it's computed locally /// within a module by the MandatoryOptimizations pass. unsigned IsPerformanceConstraint : 1; static void validateSubclassScope(SubclassScope scope, IsThunk_t isThunk, const GenericSpecializationInformation *genericInfo) { #ifndef NDEBUG // The _original_ function for a method can turn into a thunk through // signature optimization, meaning it needs to retain its subclassScope, but // other thunks and specializations are implementation details and so // shouldn't be connected to their parent class. bool thunkCanHaveSubclassScope; switch (isThunk) { case IsNotThunk: case IsSignatureOptimizedThunk: thunkCanHaveSubclassScope = true; break; case IsThunk: case IsReabstractionThunk: case IsBackDeployedThunk: thunkCanHaveSubclassScope = false; break; } auto allowsInterestingScopes = thunkCanHaveSubclassScope && !genericInfo; assert( allowsInterestingScopes || scope == SubclassScope::NotApplicable && "SubclassScope on specialization or non-signature-optimized thunk"); #endif } SILFunction(SILModule &module, SILLinkage linkage, StringRef mangledName, CanSILFunctionType loweredType, GenericEnvironment *genericEnv, IsBare_t isBareSILFunction, IsTransparent_t isTrans, SerializedKind_t serializedKind, ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope classSubclassScope, Inline_t inlineStrategy, EffectsKind E, const SILDebugScope *debugScope, IsDynamicallyReplaceable_t isDynamic, IsExactSelfClass_t isExactSelfClass, IsDistributed_t isDistributed, IsRuntimeAccessible_t isRuntimeAccessible); static SILFunction * create(SILModule &M, SILLinkage linkage, StringRef name, CanSILFunctionType loweredType, GenericEnvironment *genericEnv, std::optional loc, IsBare_t isBareSILFunction, IsTransparent_t isTrans, SerializedKind_t serializedKind, ProfileCounter entryCount, IsDynamicallyReplaceable_t isDynamic, IsDistributed_t isDistributed, IsRuntimeAccessible_t isRuntimeAccessible, IsExactSelfClass_t isExactSelfClass, IsThunk_t isThunk = IsNotThunk, SubclassScope classSubclassScope = SubclassScope::NotApplicable, Inline_t inlineStrategy = InlineDefault, EffectsKind EffectsKindAttr = EffectsKind::Unspecified, SILFunction *InsertBefore = nullptr, const SILDebugScope *DebugScope = nullptr); void init(SILLinkage Linkage, StringRef Name, CanSILFunctionType LoweredType, GenericEnvironment *genericEnv, IsBare_t isBareSILFunction, IsTransparent_t isTrans, SerializedKind_t serializedKind, ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope classSubclassScope, Inline_t inlineStrategy, EffectsKind E, const SILDebugScope *DebugScope, IsDynamicallyReplaceable_t isDynamic, IsExactSelfClass_t isExactSelfClass, IsDistributed_t isDistributed, IsRuntimeAccessible_t isRuntimeAccessible); /// Set has ownership to the given value. True means that the function has /// ownership, false means it does not. /// /// Only for use by FunctionBuilders! void setHasOwnership(bool newValue) { HasOwnership = newValue; } void setName(StringRef name) { // All the snapshots share the same name. SILFunction *sn = this; do { sn->Name = name; } while ((sn = sn->snapshots) != nullptr); } public: ~SILFunction(); SILModule &getModule() const { return Module; } /// Creates a snapshot with a given `ID` from the current function. void createSnapshot(int ID); /// Returns the snapshot with the given `ID` or null if no such snapshot exists. SILFunction *getSnapshot(int ID); /// Restores the current function from a given snapshot. void restoreFromSnapshot(int ID); /// Deletes a snapshot with the `ID`. void deleteSnapshot(int ID); SILType getLoweredType() const { return SILType::getPrimitiveObjectType(LoweredType); } CanSILFunctionType getLoweredFunctionType() const { return LoweredType; } CanSILFunctionType getLoweredFunctionTypeInContext(TypeExpansionContext context) const; SILType getLoweredTypeInContext(TypeExpansionContext context) const { return SILType::getPrimitiveObjectType( getLoweredFunctionTypeInContext(context)); } SILFunctionConventions getConventions() const { return SILFunctionConventions(LoweredType, getModule()); } SILFunctionConventions getConventionsInContext() const { auto fnType = getLoweredFunctionTypeInContext(getTypeExpansionContext()); return SILFunctionConventions(fnType, getModule()); } unsigned getIndex() const { return index; } SILProfiler *getProfiler() const { return Profiler; } SILFunction *getDynamicallyReplacedFunction() const { return ReplacedFunction; } void setDynamicallyReplacedFunction(SILFunction *f) { assert(ReplacedFunction == nullptr && "already set"); assert(!hasObjCReplacement()); if (f == nullptr) return; ReplacedFunction = f; ReplacedFunction->incrementRefCount(); } /// This function should only be called when SILFunctions are bulk deleted. void dropDynamicallyReplacedFunction() { if (!ReplacedFunction) return; ReplacedFunction->decrementRefCount(); ReplacedFunction = nullptr; } SILFunction *getReferencedAdHocRequirementWitnessFunction() const { return RefAdHocRequirementFunction; } // Marks that this `SILFunction` uses the passed in ad-hoc protocol // requirement witness `f` and therefore must retain it explicitly, // otherwise we might not be able to get a reference to it. void setReferencedAdHocRequirementWitnessFunction(SILFunction *f) { assert(RefAdHocRequirementFunction == nullptr && "already set"); if (f == nullptr) return; RefAdHocRequirementFunction = f; RefAdHocRequirementFunction->incrementRefCount(); } void dropReferencedAdHocRequirementWitnessFunction() { if (!RefAdHocRequirementFunction) return; RefAdHocRequirementFunction->decrementRefCount(); RefAdHocRequirementFunction = nullptr; } bool hasObjCReplacement() const { return !ObjCReplacementFor.empty(); } Identifier getObjCReplacement() const { return ObjCReplacementFor; } void setObjCReplacement(AbstractFunctionDecl *replacedDecl); void setObjCReplacement(Identifier replacedDecl); void setProfiler(SILProfiler *InheritedProfiler) { assert(!Profiler && "Function already has a profiler"); Profiler = InheritedProfiler; } void createProfiler(SILDeclRef Ref); ProfileCounter getEntryCount() const { return EntryCount; } void setEntryCount(ProfileCounter Count) { EntryCount = Count; } bool isNoReturnFunction(TypeExpansionContext context) const; /// Unsafely rewrite the lowered type of this function. /// /// This routine does not touch the entry block arguments /// or return instructions; you need to do that yourself /// if you care. /// /// This routine does not update all the references in the module /// You have to do that yourself void rewriteLoweredTypeUnsafe(CanSILFunctionType newType) { LoweredType = newType; } /// Return the number of entities referring to this function (other /// than the SILModule). unsigned getRefCount() const { return RefCount; } /// Increment the reference count. void incrementRefCount() { RefCount++; assert(RefCount != 0 && "Overflow of reference count!"); } /// Decrement the reference count. void decrementRefCount() { assert(RefCount != 0 && "Expected non-zero reference count on decrement!"); RefCount--; } /// Drops all uses belonging to instructions in this function. The only valid /// operation performable on this object after this is called is called the /// destructor or deallocation. void dropAllReferences() { for (SILBasicBlock &BB : *this) BB.dropAllReferences(); } /// Notify that this function was inlined. This implies that it is still /// needed for debug info generation, even if it is removed afterwards. void setInlined() { assert(!isZombie() && "Can't inline a zombie function"); Inlined = true; } /// Returns true if this function was inlined. bool isInlined() const { return Inlined; } /// Mark this function as removed from the module's function list, but kept /// as "zombie" for debug info or vtable stub generation. void setZombie() { assert(!isZombie() && "Function is a zombie function already"); Zombie = true; } /// Returns true if this function is dead, but kept in the module's zombie list. bool isZombie() const { return Zombie; } /// Returns true if this function has qualified ownership instructions in it. bool hasOwnership() const { return HasOwnership; } /// Sets the HasOwnership flag to false. This signals to SIL that no /// ownership instructions should be in this function any more. void setOwnershipEliminated() { setHasOwnership(false); } /// Returns true if this function was deserialized from canonical /// SIL. (.swiftmodule files contain canonical SIL; .sib files may be 'raw' /// SIL). If so, diagnostics should not be reapplied. bool wasDeserializedCanonical() const { return WasDeserializedCanonical; } void setWasDeserializedCanonical(bool val = true) { WasDeserializedCanonical = val; } ForceEnableLexicalLifetimes_t forceEnableLexicalLifetimes() const { return ForceEnableLexicalLifetimes_t(ForceEnableLexicalLifetimes); } void setForceEnableLexicalLifetimes(ForceEnableLexicalLifetimes_t value) { ForceEnableLexicalLifetimes = value; } UseStackForPackMetadata_t useStackForPackMetadata() const { return UseStackForPackMetadata_t(UseStackForPackMetadata); } void setUseStackForPackMetadata(UseStackForPackMetadata_t value) { UseStackForPackMetadata = value; } bool hasUnsafeNonEscapableResult() const { return HasUnsafeNonEscapableResult; } void setHasUnsafeNonEscapableResult(bool value) { HasUnsafeNonEscapableResult = value; } /// Returns true if this is a reabstraction thunk of escaping function type /// whose single argument is a potentially non-escaping closure. i.e. the /// thunks' function argument may itself have @inout_aliasable parameters. bool isWithoutActuallyEscapingThunk() const { return IsWithoutActuallyEscapingThunk; } void setWithoutActuallyEscapingThunk(bool val = true) { assert(!val || isThunk() == IsReabstractionThunk); IsWithoutActuallyEscapingThunk = val; } bool isAsync() const { return LoweredType->isAsync(); } bool isCalleeAllocatedCoroutine() const { return LoweredType->isCalleeAllocatedCoroutine(); } /// Returns the calling convention used by this entry point. SILFunctionTypeRepresentation getRepresentation() const { return getLoweredFunctionType()->getRepresentation(); } ResilienceExpansion getResilienceExpansion() const; // Returns the type expansion context to be used inside this function. TypeExpansionContext getTypeExpansionContext() const { return TypeExpansionContext(*this); } const Lowering::TypeLowering & getTypeLowering(Lowering::AbstractionPattern orig, Type subst); const Lowering::TypeLowering &getTypeLowering(Type t) const; SILType getLoweredType(Lowering::AbstractionPattern orig, Type subst) const; SILType getLoweredType(Type t) const; CanType getLoweredRValueType(Lowering::AbstractionPattern orig, Type subst) const; CanType getLoweredRValueType(Type t) const; SILType getLoweredLoadableType(Type t) const; SILType getLoweredType(SILType t) const; const Lowering::TypeLowering &getTypeLowering(SILType type) const; bool isTypeABIAccessible(SILType type) const; /// Returns true if this function has a calling convention that has a self /// argument. bool hasSelfParam() const { return getLoweredFunctionType()->hasSelfParam(); } /// Returns true if the function has parameters that are consumed by the // callee. bool hasOwnedParameters() const { for (auto &ParamInfo : getLoweredFunctionType()->getParameters()) { if (ParamInfo.isConsumedInCallee()) return true; } return false; } // Returns true if the function has indirect out parameters. bool hasIndirectFormalResults() const { return getLoweredFunctionType()->hasIndirectFormalResults(); } // Returns true if the function has any generic arguments. bool isGeneric() const { auto s = getLoweredFunctionType()->getInvocationGenericSignature(); return s && !s->areAllParamsConcrete(); } /// Returns true if this function ie either a class method, or a /// closure that captures the 'self' value or its metatype. /// /// If this returns true, DynamicSelfType can be used in the body /// of the function. /// /// Note that this is not the same as hasSelfParam(). /// /// For closures that capture DynamicSelfType, hasDynamicSelfMetadata() /// is true and hasSelfParam() is false. For methods on value types, /// hasSelfParam() is true and hasDynamicSelfMetadata() is false. bool hasDynamicSelfMetadata() const; /// Return the mangled name of this SILFunction. StringRef getName() const { return Name; } /// A convenience function which checks if the function has a specific /// \p name. It is equivalent to getName() == Name, but as it is not /// inlined it can be called from the debugger. bool hasName(const char *Name) const; /// True if this is a declaration of a function defined in another module. bool isExternalDeclaration() const { return BlockList.empty(); } /// Returns true if this is a definition of a function defined in this module. bool isDefinition() const { return !isExternalDeclaration(); } /// Returns true if there exist pre-specializations. bool hasPrespecialization() const; /// Get this function's linkage attribute. SILLinkage getLinkage() const { return SILLinkage(Linkage); } /// Set the function's linkage attribute. void setLinkage(SILLinkage linkage) { Linkage = unsigned(linkage); } /// Checks if this (callee) function body can be inlined into the caller /// by comparing their `SerializedKind_t` values. /// /// If both callee and caller are `not_serialized`, the callee can be inlined /// into the caller during SIL inlining passes even if it (and the caller) /// might contain private symbols. If this callee is `serialized_for_pkg`, /// it can only be referenced by a serialized caller but not inlined into /// it. /// /// ``` /// canInlineInto: Caller /// | not_serialized | serialized_for_pkg | serialized /// not_serialized | ok | no | no /// Callee serialized_for_pkg | ok | ok | no /// serialized | ok | ok | ok /// /// ``` /// /// \p callerSerializedKind The caller's SerializedKind. bool canBeInlinedIntoCaller(SerializedKind_t callerSerializedKind) const; /// Returns true if this function can be referenced from a fragile function /// body. /// \p callerSerializedKind The caller's SerializedKind. Used to be passed to /// \c canBeInlinedIntoCaller. bool hasValidLinkageForFragileRef(SerializedKind_t callerSerializedKind) const; /// Get's the effective linkage which is used to derive the llvm linkage. /// Usually this is the same as getLinkage(), except in one case: if this /// function is a method in a class which has higher visibility than the /// method itself, the function can be referenced from vtables of derived /// classes in other compilation units. SILLinkage getEffectiveSymbolLinkage() const { return effectiveLinkageForClassMember(getLinkage(), getClassSubclassScope()); } /// Helper method which returns true if this function has "external" linkage. bool isAvailableExternally() const { return swift::isAvailableExternally(getLinkage()); } /// Helper method which returns true if the linkage of the SILFunction /// indicates that the object's definition might be required outside the /// current SILModule. bool isPossiblyUsedExternally() const; /// Helper method which returns whether this function should be preserved so /// it can potentially be used in the debugger. bool shouldBePreservedForDebugger() const; /// In addition to isPossiblyUsedExternally() it returns also true if this /// is a (private or internal) vtable method which can be referenced by /// vtables of derived classes outside the compilation unit. bool isExternallyUsedSymbol() const; /// Return whether this function may be referenced by C code. bool hasCReferences() const { return HasCReferences; } void setHasCReferences(bool value) { HasCReferences = value; } /// Returns the availability context used to determine if the function's /// symbol should be weakly referenced across module boundaries. AvailabilityRange getAvailabilityForLinkage() const { return Availability; } void setAvailabilityForLinkage(AvailabilityRange availability) { Availability = availability; } /// Returns whether this function's symbol must always be weakly referenced /// across module boundaries. bool isAlwaysWeakImported() const { return IsAlwaysWeakImported; } void setIsAlwaysWeakImported(bool value) { IsAlwaysWeakImported = value; } bool isWeakImported(ModuleDecl *module) const; /// Returns whether this function implementation can be dynamically replaced. IsDynamicallyReplaceable_t isDynamicallyReplaceable() const { return IsDynamicallyReplaceable_t(IsDynamicReplaceable); } void setIsDynamic(IsDynamicallyReplaceable_t value = IsDynamic) { IsDynamicReplaceable = value; assert(!Transparent || !IsDynamicReplaceable); } IsExactSelfClass_t isExactSelfClass() const { return IsExactSelfClass_t(ExactSelfClass); } void setIsExactSelfClass(IsExactSelfClass_t t) { ExactSelfClass = t; } IsDistributed_t isDistributed() const { return IsDistributed_t(IsDistributed); } void setIsDistributed(IsDistributed_t value = IsDistributed_t::IsDistributed) { IsDistributed = value; } IsRuntimeAccessible_t isRuntimeAccessible() const { return IsRuntimeAccessible_t(IsRuntimeAccessible); } void setIsRuntimeAccessible(IsRuntimeAccessible_t value = IsRuntimeAccessible_t::IsRuntimeAccessible) { IsRuntimeAccessible = value; } bool needsStackProtection() const { return stackProtection; } void setNeedStackProtection(bool needSP) { stackProtection = needSP; } /// Get the DeclContext of this function. DeclContext *getDeclContext() const { return DeclCtxt; } /// \returns True if the function is marked with the @_semantics attribute /// and has special semantics that the optimizer can use to optimize the /// function. bool hasSemanticsAttrs() const { return !SemanticsAttrSet.empty(); } /// \returns True if the function has a semantic attribute that starts with a /// specific string. /// /// TODO: This needs a better name. bool hasSemanticsAttrThatStartsWith(StringRef S) { return count_if(getSemanticsAttrs(), [&S](const std::string &Attr) -> bool { return StringRef(Attr).starts_with(S); }); } /// \returns the semantics tag that describes this function. ArrayRef getSemanticsAttrs() const { return SemanticsAttrSet; } /// \returns True if the function has the semantics flag \p Value; bool hasSemanticsAttr(StringRef Value) const { return count(SemanticsAttrSet, Value); } /// Add the given semantics attribute to the attr list set. void addSemanticsAttr(StringRef Ref) { if (hasSemanticsAttr(Ref)) return; SemanticsAttrSet.push_back(Ref.str()); std::sort(SemanticsAttrSet.begin(), SemanticsAttrSet.end()); } /// Remove the semantics void removeSemanticsAttr(StringRef Ref) { auto Iter = std::remove(SemanticsAttrSet.begin(), SemanticsAttrSet.end(), Ref); SemanticsAttrSet.erase(Iter); } /// \returns the range of specialize attributes. ArrayRef getSpecializeAttrs() const { return SpecializeAttrSet; } /// Removes all specialize attributes from this function. void clearSpecializeAttrs() { forEachSpecializeAttrTargetFunction( [](SILFunction *targetFun) { targetFun->decrementRefCount(); }); SpecializeAttrSet.clear(); } void addSpecializeAttr(SILSpecializeAttr *Attr); void removeSpecializeAttr(SILSpecializeAttr *attr); void forEachSpecializeAttrTargetFunction( llvm::function_ref action); /// Get this function's optimization mode or OptimizationMode::NotSet if it is /// not set for this specific function. OptimizationMode getOptimizationMode() const { return OptimizationMode(OptMode); } /// Returns the optimization mode for the function. If no mode is set for the /// function, returns the global mode, i.e. the mode of the module's options. OptimizationMode getEffectiveOptimizationMode() const; void setOptimizationMode(OptimizationMode mode) { OptMode = unsigned(mode); } /// True if debug information must be preserved (-Onone). /// /// If this is false (-O), then the presence of debug info must not affect the /// outcome of any transformations. /// /// Typically used to determine whether a debug_value is a normal SSA use or /// incidental use. bool preserveDebugInfo() const; PerformanceConstraints getPerfConstraints() const { return perfConstraints; } void setPerfConstraints(PerformanceConstraints perfConstr) { perfConstraints = perfConstr; } // see `IsPerformanceConstraint` bool isPerformanceConstraint() const { return IsPerformanceConstraint; } void setIsPerformanceConstraint(bool flag = true) { IsPerformanceConstraint = flag; } /// \returns True if the function is optimizable (i.e. not marked as no-opt), /// or is raw SIL (so that the mandatory passes still run). bool shouldOptimize() const; /// Returns true if this function should be optimized for size. bool optimizeForSize() const { return getEffectiveOptimizationMode() == OptimizationMode::ForSize; } /// Returns true if this is a function that should have its ownership /// verified. bool shouldVerifyOwnership() const; /// Check if the function has a location. /// FIXME: All functions should have locations, so this method should not be /// necessary. bool hasLocation() const { return DebugScope && !DebugScope->Loc.isNull(); } /// Get the source location of the function. SILLocation getLocation() const { if (!DebugScope) { return SILLocation::invalid(); } return getDebugScope()->Loc; } /// Initialize the debug scope of the function and also set the DeclCtxt. void setDebugScope(const SILDebugScope *DS) { DebugScope = DS; DeclCtxt = (DS ? DebugScope->Loc.getAsDeclContext() : nullptr); } /// Returns the module that defines this function. ModuleDecl *getParentModule() const { return DeclCtxt ? DeclCtxt->getParentModule() : ParentModule; } /// Sets \c ParentModule as fallback if \c DeclCtxt is not available to /// provide the parent module. void setParentModule(ModuleDecl *module) { assert(!DeclCtxt && "already have a DeclCtxt"); ParentModule = module; } /// Initialize the debug scope for debug info on SIL level /// (-sil-based-debuginfo). void setSILDebugScope(const SILDebugScope *DS) { DebugScope = DS; } /// Get the source location of the function. const SILDebugScope *getDebugScope() const { return DebugScope; } /// Return the SILDeclRef for this SILFunction if one was assigned by SILGen. SILDeclRef getDeclRef() const { return DeclRef; } /// Set the SILDeclRef for this SILFunction. Used mainly by SILGen. void setDeclRef(SILDeclRef declRef) { DeclRef = declRef; } /// Get this function's bare attribute. IsBare_t isBare() const { return IsBare_t(Bare); } void setBare(IsBare_t isB) { Bare = isB; } /// Get this function's transparent attribute. IsTransparent_t isTransparent() const { return IsTransparent_t(Transparent); } void setTransparent(IsTransparent_t isT) { Transparent = isT; assert(!Transparent || !IsDynamicReplaceable); } bool isSerialized() const { return SerializedKind_t(SerializedKind) == IsSerialized; } bool isAnySerialized() const { return SerializedKind_t(SerializedKind) == IsSerialized || SerializedKind_t(SerializedKind) == IsSerializedForPackage; } /// Get this function's serialized attribute. SerializedKind_t getSerializedKind() const { return SerializedKind_t(SerializedKind); } void setSerializedKind(SerializedKind_t serializedKind) { SerializedKind = serializedKind; assert(this->getSerializedKind() == serializedKind && "too few bits for Serialized storage"); } /// Get this function's thunk attribute. IsThunk_t isThunk() const { return IsThunk_t(Thunk); } void setThunk(IsThunk_t isThunk) { validateSubclassScope(getClassSubclassScope(), isThunk, SpecializationInfo); Thunk = isThunk; } /// Get the class visibility (relevant for class methods). SubclassScope getClassSubclassScope() const { return SubclassScope(ClassSubclassScope); } void setClassSubclassScope(SubclassScope scope) { validateSubclassScope(scope, isThunk(), SpecializationInfo); ClassSubclassScope = static_cast(scope); } /// Get this function's noinline attribute. Inline_t getInlineStrategy() const { return Inline_t(InlineStrategy); } void setInlineStrategy(Inline_t inStr) { InlineStrategy = inStr; } /// \return the function side effects information. EffectsKind getEffectsKind() const { return EffectsKind(EffectsKindAttr); } /// \return True if the function is annotated with the @_effects attribute. bool hasEffectsKind() const { return EffectsKind(EffectsKindAttr) != EffectsKind::Unspecified; } /// Set the function side effect information. void setEffectsKind(EffectsKind E) { EffectsKindAttr = unsigned(E); } std::pair parseArgumentEffectsFromSource(StringRef effectStr, ArrayRef paramNames); std::pair parseArgumentEffectsFromSIL(StringRef effectStr, int argumentIndex); std::pair parseGlobalEffectsFromSIL(StringRef effectStr); std::pair parseMultipleEffectsFromSIL(StringRef effectStr); void writeEffect(llvm::raw_ostream &OS, int effectIdx) const; void writeEffects(llvm::raw_ostream &OS) const { writeEffect(OS, -1); } void copyEffects(SILFunction *from); bool hasArgumentEffects() const; void visitArgEffects(std::function c) const; MemoryBehavior getMemoryBehavior(bool observeRetains); // Used by the MemoryLifetimeVerifier bool argumentMayRead(Operand *argOp, SILValue addr); Purpose getSpecialPurpose() const { return specialPurpose; } /// Get this function's global_init attribute. /// /// The implied semantics are: /// - side-effects can occur any time before the first invocation. /// - all calls to the same global_init function have the same side-effects. /// - any operation that may observe the initializer's side-effects must be /// preceded by a call to the initializer. /// /// This is currently true if the function is an addressor that was lazily /// generated from a global variable access. Note that the initialization /// function itself does not need this attribute. It is private and only /// called within the addressor. bool isGlobalInit() const { return specialPurpose == Purpose::GlobalInit; } bool isGlobalInitOnceFunction() const { return specialPurpose == Purpose::GlobalInitOnceFunction; } bool isLazyPropertyGetter() const { return specialPurpose == Purpose::LazyPropertyGetter; } void setSpecialPurpose(Purpose purpose) { specialPurpose = purpose; } /// Return whether this function has a foreign implementation which can /// be emitted on demand. bool hasForeignBody() const; /// Return whether this function corresponds to a Clang node. bool hasClangNode() const { return ClangNodeOwner != nullptr; } /// Set the owning declaration of the Clang node associated with this /// function. We have to store an owner (a Swift declaration) instead of /// directly referencing the original declaration due to current /// limitations in the serializer. void setClangNodeOwner(ValueDecl *owner) { assert(owner->hasClangNode()); ClangNodeOwner = owner; } /// Return the owning declaration of the Clang node associated with this /// function. This should only be used for serialization. ValueDecl *getClangNodeOwner() const { return ClangNodeOwner; } /// Return the Clang node associated with this function if it has one. ClangNode getClangNode() const { return (ClangNodeOwner ? ClangNodeOwner->getClangNode() : ClangNode()); } const clang::Decl *getClangDecl() const { return (ClangNodeOwner ? ClangNodeOwner->getClangDecl() : nullptr); } /// Returns whether this function is a specialization. bool isSpecialization() const { return SpecializationInfo != nullptr; } /// Return the specialization information. const GenericSpecializationInformation *getSpecializationInfo() const { assert(isSpecialization()); return SpecializationInfo; } void setSpecializationInfo(const GenericSpecializationInformation *Info) { assert(!isSpecialization()); validateSubclassScope(getClassSubclassScope(), isThunk(), Info); SpecializationInfo = Info; } /// If this function is a specialization, return the original function from /// which this function was specialized. const SILFunction *getOriginOfSpecialization() const; /// Retrieve the generic environment containing the mapping from interface /// types to context archetypes for this function. Only present if the /// function has a body. GenericEnvironment *getGenericEnvironment() const { return GenericEnv; } /// Return any captured local generic environments, currently used for pack /// element environments only. After SILGen, these are rewritten into /// primary archetypes. ArrayRef getCapturedEnvironments() const { return CapturedEnvs; } void setGenericEnvironment(GenericEnvironment *env); void setGenericEnvironment(GenericEnvironment *env, ArrayRef capturedEnvs, SubstitutionMap forwardingSubs) { GenericEnv = env; CapturedEnvs = capturedEnvs; ForwardingSubMap = forwardingSubs; } /// Retrieve the generic signature from the generic environment of this /// function, if any. Else returns the null \c GenericSignature. GenericSignature getGenericSignature() const; /// Map the given type, which is based on an interface SILFunctionType and may /// therefore be dependent, to a type based on the context archetypes of this /// SILFunction. Type mapTypeIntoContext(Type type) const; /// Map the given type, which is based on an interface SILFunctionType and may /// therefore be dependent, to a type based on the context archetypes of this /// SILFunction. SILType mapTypeIntoContext(SILType type) const; /// Converts the given function definition to a declaration. void convertToDeclaration() { assert(isDefinition() && "Can only convert definitions to declarations"); clear(); } void clear(); /// Like `clear`, but does not call `dropAllReferences`, which is the /// responsibility of the caller. void eraseAllBlocks(); /// A substitution map that sends the generic parameters of the invocation /// generic signature to some combination of primar and local archetypes. /// /// CAUTION: If this is a SILFunction that captures pack element environments, /// then at SILGen time, this is not actually the forwarding substitution map /// of the SILFunction's generic environment. This is because: /// /// 1) The SILFunction's generic signature includes extra generic parameters, /// to model captured pack elements; /// 2) The SILFunction's generic environment is the AST generic environment, /// so it's based on the original generic signature; /// 3) SILGen uses this AST generic environment together with local archetypes /// for lowering SIL instructions. /// /// Therefore, the SILFunction's forwarding substitution map takes the extended /// generic signature (1). It maps the original generic parameters to the /// archetypes of (2), and the extended generic parameters to the local archetypes /// of (3). /// /// After SILGen, all archetypes are re-instantiated inside the SIL function, /// and the forwarding substitution map and generic environment then align. SubstitutionMap getForwardingSubstitutionMap() const { return ForwardingSubMap; } /// Returns true if this SILFunction must be a defer statement. /// /// NOTE: This may return false for defer statements that have been /// deserialized without a DeclContext. This means that this is guaranteed to /// be correct for SILFunctions in Raw SIL that were not deserialized as /// canonical. Thus one can use it for diagnostics. bool isDefer() const { if (auto *dc = getDeclContext()) if (auto *decl = dyn_cast_or_null(dc->getAsDecl())) return decl->isDeferBody(); return false; } /// Returns true if this function belongs to a declaration that /// has `@_alwaysEmitIntoClient` attribute. bool markedAsAlwaysEmitIntoClient() const { if (!hasLocation()) return false; auto *V = getLocation().getAsASTNode(); return V && V->getAttrs().hasAttribute(); } /// Return whether this function has attribute @_used on it bool markedAsUsed() const { return MarkedAsUsed; } void setMarkedAsUsed(bool value) { MarkedAsUsed = value; } /// Return custom section name if @_section was used, otherwise empty StringRef section() const { return Section; } void setSection(StringRef value) { Section = value; } /// Return Wasm export name if @_expose(wasm) was used, otherwise empty StringRef wasmExportName() const { return WasmExportName; } void setWasmExportName(StringRef value) { WasmExportName = value; } /// Return Wasm import module name if @_extern(wasm) was used otherwise empty StringRef wasmImportModuleName() const { if (WasmImportModuleAndField) return WasmImportModuleAndField->first; return StringRef(); } /// Return Wasm import field name if @_extern(wasm) was used otherwise empty StringRef wasmImportFieldName() const { if (WasmImportModuleAndField) return WasmImportModuleAndField->second; return StringRef(); } void setWasmImportModuleAndField(StringRef module, StringRef field) { WasmImportModuleAndField = std::make_pair(module, field); } bool isExternForwardDeclaration() const { if (isExternalDeclaration()) { if (auto declContext = getDeclContext()) { if (auto decl = declContext->getAsDecl()) { if (decl->getAttrs().hasAttribute()) return true; if (decl->getAttrs().hasAttribute()) return true; } } } return false; } /// Returns true if this function belongs to a declaration that returns /// an opaque result type with one or more availability conditions that are /// allowed to produce a different underlying type at runtime. bool hasOpaqueResultTypeWithAvailabilityConditions() const { if (!hasLocation()) return false; if (auto *V = getLocation().getAsASTNode()) { auto *opaqueResult = V->getOpaqueResultTypeDecl(); return opaqueResult && opaqueResult->hasConditionallyAvailableSubstitutions(); } return false; } void setActorIsolation(ActorIsolation newActorIsolation) { actorIsolation = newActorIsolation; } std::optional getActorIsolation() const { return actorIsolation; } /// Return the source file that this SILFunction belongs to if it exists. SourceFile *getSourceFile() const; //===--------------------------------------------------------------------===// // Block List Access //===--------------------------------------------------------------------===// using iterator = BlockListType::iterator; using reverse_iterator = BlockListType::reverse_iterator; using const_iterator = BlockListType::const_iterator; bool empty() const { return BlockList.empty(); } iterator begin() { return BlockList.begin(); } iterator end() { return BlockList.end(); } reverse_iterator rbegin() { return BlockList.rbegin(); } reverse_iterator rend() { return BlockList.rend(); } const_iterator begin() const { return BlockList.begin(); } const_iterator end() const { return BlockList.end(); } unsigned size() const { return BlockList.size(); } SILBasicBlock &front() { return *begin(); } const SILBasicBlock &front() const { return *begin(); } SILBasicBlock *getEntryBlock() { return &front(); } const SILBasicBlock *getEntryBlock() const { return &front(); } SILBasicBlock *createBasicBlock(); SILBasicBlock *createBasicBlock(llvm::StringRef debugName); SILBasicBlock *createBasicBlockAfter(SILBasicBlock *afterBB); SILBasicBlock *createBasicBlockBefore(SILBasicBlock *beforeBB); /// Removes and destroys \p BB; void eraseBlock(SILBasicBlock *BB) { assert(BB->getParent() == this); BlockList.erase(BB); } /// Transfer all blocks of \p F into this function, at the begin of the block /// list. void moveAllBlocksFromOtherFunction(SILFunction *F); /// Transfer \p blockInOtherFunction of another function into this function, /// before \p insertPointInThisFunction. void moveBlockFromOtherFunction(SILBasicBlock *blockInOtherFunction, iterator insertPointInThisFunction); /// Move block \p BB to immediately before the iterator \p IP. /// /// The block must be part of this function. void moveBlockBefore(SILBasicBlock *BB, SILFunction::iterator IP); /// Move block \p BB to immediately after block \p After. /// /// The block must be part of this function. void moveBlockAfter(SILBasicBlock *BB, SILBasicBlock *After) { moveBlockBefore(BB, std::next(After->getIterator())); } /// Return the unique basic block containing a return inst if it /// exists. Otherwise, returns end. iterator findReturnBB() { return std::find_if(begin(), end(), [](const SILBasicBlock &BB) -> bool { const TermInst *TI = BB.getTerminator(); return isa(TI); }); } /// Return the unique basic block containing a return inst if it /// exists. Otherwise, returns end. const_iterator findReturnBB() const { return std::find_if(begin(), end(), [](const SILBasicBlock &BB) -> bool { const TermInst *TI = BB.getTerminator(); return isa(TI); }); } /// Return the unique basic block containing a throw inst if it /// exists. Otherwise, returns end. iterator findThrowBB() { return std::find_if(begin(), end(), [](const SILBasicBlock &BB) -> bool { const TermInst *TI = BB.getTerminator(); return isa(TI) || isa(TI); }); } /// Return the unique basic block containing a throw inst if it /// exists. Otherwise, returns end. const_iterator findThrowBB() const { return std::find_if(begin(), end(), [](const SILBasicBlock &BB) -> bool { const TermInst *TI = BB.getTerminator(); return isa(TI) || isa(TI); }); } /// Loop over all blocks in this function and add all function exiting blocks /// to output. void findExitingBlocks(llvm::SmallVectorImpl &output) const { for (auto &Block : const_cast(*this)) { if (Block.getTerminator()->isFunctionExiting()) { output.emplace_back(&Block); } } } /// Populate \p output with every block terminated by an unreachable /// instruction. void visitUnreachableTerminatedBlocks( llvm::function_ref visitor) const { for (auto &block : const_cast(*this)) { if (isa(block.getTerminator())) { visitor(block); } } } //===--------------------------------------------------------------------===// // Argument Helper Methods //===--------------------------------------------------------------------===// SILArgument *getArgument(unsigned i) { assert(!empty() && "Cannot get argument of a function without a body"); return begin()->getArgument(i); } const SILArgument *getArgument(unsigned i) const { assert(!empty() && "Cannot get argument of a function without a body"); return begin()->getArgument(i); } ArrayRef getArguments() const { assert(!empty() && "Cannot get arguments of a function without a body"); return begin()->getArguments(); } ArrayRef getIndirectResults() const { assert(!empty() && "Cannot get arguments of a function without a body"); return begin()->getArguments().slice( 0, getConventions().getNumIndirectSILResults()); } ArrayRef getArgumentsWithoutIndirectResults() const { assert(!empty() && "Cannot get arguments of a function without a body"); return begin()->getArguments().slice( getConventions().getNumIndirectSILResults() + getConventions().getNumIndirectSILErrorResults()); } const SILArgument *getSelfArgument() const { assert(hasSelfParam() && "This method can only be called if the " "SILFunction has a self parameter"); return getArguments().back(); } /// Like getSelfArgument() except it returns a nullptr if we do not have a /// selfparam. const SILArgument *maybeGetSelfArgument() const { if (!hasSelfParam()) return nullptr; return getArguments().back(); } /// If we have an isolated argument, return that. Returns nullptr otherwise. const SILArgument *maybeGetIsolatedArgument() const { for (auto *arg : getArgumentsWithoutIndirectResults()) { if (cast(arg)->getKnownParameterInfo().hasOption( SILParameterInfo::Isolated)) return arg; } return nullptr; } const SILArgument *getDynamicSelfMetadata() const { assert(hasDynamicSelfMetadata() && "This method can only be called if the " "SILFunction has a self-metadata parameter"); return getArguments().back(); } //===--------------------------------------------------------------------===// // Miscellaneous //===--------------------------------------------------------------------===// /// A value's lifetime, determined by looking at annotations on its decl and /// the default lifetime for the type. Lifetime getLifetime(VarDecl *decl, SILType ty) { return ty.getLifetime(*this).getLifetimeForAnnotatedValue( decl->getLifetimeAnnotation()); } ArrayRef> getUndefValues() { return {undefValues.begin(), undefValues.end()}; } /// verify - Run the SIL verifier to make sure that the SILFunction follows /// invariants. void verify(CalleeCache *calleeCache = nullptr, bool SingleFunction = true, bool isCompleteOSSA = true, bool checkLinearLifetime = true) const; /// Run the SIL verifier without assuming OSSA lifetimes end at dead end /// blocks. void verifyIncompleteOSSA() const { verify(/*calleeCache*/nullptr, /*SingleFunction=*/true, /*completeOSSALifetimes=*/false); } /// Verifies the lifetime of memory locations in the function. void verifyMemoryLifetime(CalleeCache *calleeCache); /// Verifies ownership of the function. /// Since we don't have complete lifetimes everywhere, computes DeadEndBlocks /// and calls verifyOwnership(DeadEndBlocks *deadEndBlocks) void verifyOwnership() const; /// Run the SIL ownership verifier to check that all values with ownership /// have a linear lifetime. Regular OSSA invariants are checked separately in /// normal SIL verification. /// /// \p deadEndBlocks is nullptr when OSSA lifetimes are complete. /// /// NOTE: The ownership verifier is run when performing normal IR /// verification, so this verification can be viewed as a subset of /// SILFunction::verify(checkLinearLifetimes=true). void verifyOwnership(DeadEndBlocks *deadEndBlocks) const; /// Verify that all non-cond-br critical edges have been split. /// /// This is a fast subset of the checks performed in the SILVerifier. void verifyCriticalEdges() const; /// Validate that all SILUndefs stored in the function's type -> SILUndef map /// have this function as their parent function. /// /// Please only call this from the SILVerifier. void verifySILUndefMap() const; /// Pretty-print the SILFunction. void dump(bool Verbose) const; void dump() const; /// Pretty-print the SILFunction. /// Useful for dumping the function when running in a debugger. /// Warning: no error handling is done. Fails with an assert if the file /// cannot be opened. void dump(const char *FileName) const; /// Pretty-print the SILFunction to the tream \p OS. /// /// \param Verbose Dump SIL location information in verbose mode. void print(raw_ostream &OS, bool Verbose = false) const { SILPrintContext PrintCtx(OS, Verbose); print(PrintCtx); } /// Pretty-print the SILFunction with the context \p PrintCtx. void print(SILPrintContext &PrintCtx) const; /// Pretty-print the SILFunction's name using SIL syntax, /// '@function_mangled_name'. void printName(raw_ostream &OS) const; /// Assigns consecutive numbers to all the SILNodes in the function. /// For instructions, both the instruction node and the value nodes of /// any results will be assigned numbers; the instruction node will /// be numbered the same as the first result, if there are any results. void numberValues(llvm::DenseMap &nodeToNumberMap) const; ASTContext &getASTContext() const; /// This function is meant for use from the debugger. You can just say 'call /// F->viewCFG()' and a ghostview window should pop up from the program, /// displaying the CFG of the current function with the code for each basic /// block inside. This depends on there being a 'dot' and 'gv' program in /// your path. void viewCFG() const; /// Like ViewCFG, but the graph does not show the contents of basic blocks. void viewCFGOnly() const; }; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SILFunction &F) { F.print(OS); return OS; } } // end swift namespace //===----------------------------------------------------------------------===// // ilist_traits for SILFunction //===----------------------------------------------------------------------===// namespace llvm { template <> struct ilist_traits<::swift::SILFunction> : public ilist_node_traits<::swift::SILFunction> { using SILFunction = ::swift::SILFunction; public: static void deleteNode(SILFunction *V) { V->~SILFunction(); } private: void createNode(const SILFunction &); }; } // end llvm namespace //===----------------------------------------------------------------------===// // Inline SIL implementations //===----------------------------------------------------------------------===// namespace swift { inline bool SILBasicBlock::isEntry() const { return this == &*getParent()->begin(); } inline SILModule &SILInstruction::getModule() const { return getFunction()->getModule(); } } // end swift namespace #endif