//===--- SILFunction.cpp - Defines the SILFunction data structure ---------===// // // 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-function" #include "swift/SIL/SILFunction.h" #include "swift/AST/AvailabilityRange.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/LocalArchetypeRequirementCollector.h" #include "swift/AST/Module.h" #include "swift/AST/Stmt.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/OptimizationMode.h" #include "swift/Basic/Statistic.h" #include "swift/SIL/CFG.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILBridging.h" #include "swift/SIL/SILCloner.h" #include "swift/SIL/SILDeclRef.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILProfiler.h" #include "clang/AST/Decl.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/GraphWriter.h" #include using namespace swift; using namespace Lowering; GenericSignature SILSpecializeAttr::buildTypeErasedSignature( GenericSignature sig, ArrayRef typeErasedParams) { bool changedSignature = false; llvm::SmallVector requirementsErased; auto &C = sig->getASTContext(); for (auto req : sig.getRequirements()) { bool found = std::any_of(typeErasedParams.begin(), typeErasedParams.end(), [&](Type t) { auto other = req.getFirstType(); return t->isEqual(other); }); if (found && req.getKind() == RequirementKind::Layout) { auto layout = req.getLayoutConstraint(); if (layout->isClass()) { requirementsErased.push_back(Requirement(RequirementKind::SameType, req.getFirstType(), C.getAnyObjectType())); } else if (layout->isBridgeObject()) { requirementsErased.push_back(Requirement(RequirementKind::SameType, req.getFirstType(), C.TheBridgeObjectType)); } else if (layout->isFixedSizeTrivial()) { unsigned bitWidth = layout->getTrivialSizeInBits(); requirementsErased.push_back( Requirement(RequirementKind::SameType, req.getFirstType(), CanType(BuiltinIntegerType::get(bitWidth, C)))); } else if (layout->isTrivialStride()) { requirementsErased.push_back( Requirement(RequirementKind::SameType, req.getFirstType(), CanType(BuiltinVectorType::get( C, BuiltinIntegerType::get(8, C), layout->getTrivialStride())))); } else { requirementsErased.push_back(req); } } else { requirementsErased.push_back(req); } changedSignature |= found; } if (changedSignature) { return buildGenericSignature( C, GenericSignature(), SmallVector(sig.getGenericParams()), requirementsErased, /*allowInverses=*/false); } return sig; } SILSpecializeAttr::SILSpecializeAttr(bool exported, SpecializationKind kind, GenericSignature specializedSig, GenericSignature unerasedSpecializedSig, ArrayRef typeErasedParams, SILFunction *target, Identifier spiGroup, const ModuleDecl *spiModule, AvailabilityRange availability) : kind(kind), exported(exported), specializedSignature(specializedSig), unerasedSpecializedSignature(unerasedSpecializedSig), typeErasedParams(typeErasedParams.begin(), typeErasedParams.end()), spiGroup(spiGroup), availability(availability), spiModule(spiModule), targetFunction(target) { if (targetFunction) targetFunction->incrementRefCount(); } SILSpecializeAttr * SILSpecializeAttr::create(SILModule &M, GenericSignature specializedSig, ArrayRef typeErasedParams, bool exported, SpecializationKind kind, SILFunction *target, Identifier spiGroup, const ModuleDecl *spiModule, AvailabilityRange availability) { auto erasedSpecializedSig = SILSpecializeAttr::buildTypeErasedSignature(specializedSig, typeErasedParams); void *buf = M.allocate(sizeof(SILSpecializeAttr), alignof(SILSpecializeAttr)); return ::new (buf) SILSpecializeAttr(exported, kind, erasedSpecializedSig, specializedSig, typeErasedParams, target, spiGroup, spiModule, availability); } void SILFunction::addSpecializeAttr(SILSpecializeAttr *Attr) { if (getLoweredFunctionType()->getInvocationGenericSignature()) { Attr->F = this; SpecializeAttrSet.push_back(Attr); } } void SILFunction::removeSpecializeAttr(SILSpecializeAttr *attr) { // Drop the reference to the _specialize(target:) function. if (auto *targetFun = attr->getTargetFunction()) { targetFun->decrementRefCount(); } SpecializeAttrSet.erase(std::remove_if(SpecializeAttrSet.begin(), SpecializeAttrSet.end(), [attr](SILSpecializeAttr *member) { return member == attr; }), SpecializeAttrSet.end()); } SILFunction *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, SubclassScope classSubclassScope, Inline_t inlineStrategy, EffectsKind E, SILFunction *insertBefore, const SILDebugScope *debugScope) { // Get a StringMapEntry for the function. As a sop to error cases, // allow the name to have an empty string. llvm::StringMapEntry *entry = nullptr; if (!name.empty()) { entry = &*M.FunctionTable.insert(std::make_pair(name, nullptr)).first; PrettyStackTraceSILFunction trace("creating", entry->getValue()); assert(!entry->getValue() && "function already exists"); name = entry->getKey(); } SILFunction *fn = M.removeFromZombieList(name); if (fn) { // Resurrect a zombie function. // This happens for example if a specialized function gets dead and gets // deleted. And afterwards the same specialization is created again. fn->init(linkage, name, loweredType, genericEnv, isBareSILFunction, isTrans, serializedKind, entryCount, isThunk, classSubclassScope, inlineStrategy, E, debugScope, isDynamic, isExactSelfClass, isDistributed, isRuntimeAccessible); assert(fn->empty()); } else { fn = new (M) SILFunction( M, linkage, name, loweredType, genericEnv, isBareSILFunction, isTrans, serializedKind, entryCount, isThunk, classSubclassScope, inlineStrategy, E, debugScope, isDynamic, isExactSelfClass, isDistributed, isRuntimeAccessible); } if (entry) entry->setValue(fn); if (insertBefore) M.functions.insert(SILModule::iterator(insertBefore), fn); else M.functions.push_back(fn); auto iter = M.pendingSpecializeAttrs.find(name); if (iter != M.pendingSpecializeAttrs.end()) { for (auto *attr : iter->second) { fn->addSpecializeAttr(attr); } M.pendingSpecializeAttrs.erase(iter); } return fn; } static SwiftMetatype functionMetatype; static BridgedFunction::RegisterFn initFunction = nullptr; static BridgedFunction::RegisterFn destroyFunction = nullptr; static BridgedFunction::WriteFn writeFunction = nullptr; static BridgedFunction::ParseFn parseFunction = nullptr; static BridgedFunction::CopyEffectsFn copyEffectsFunction = nullptr; static BridgedFunction::GetEffectInfoFn getEffectInfoFunction = nullptr; static BridgedFunction::GetMemBehaviorFn getMemBehvaiorFunction = nullptr; static BridgedFunction::ArgumentMayReadFn argumentMayReadFunction = nullptr; static BridgedFunction::IsDeinitBarrierFn isDeinitBarrierFunction = nullptr; SILFunction::SILFunction( SILModule &Module, 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) : SwiftObjectHeader(functionMetatype), Module(Module), index(Module.getNewFunctionIndex()), Availability(AvailabilityRange::alwaysAvailable()) { init(Linkage, Name, LoweredType, genericEnv, isBareSILFunction, isTrans, serializedKind, entryCount, isThunk, classSubclassScope, inlineStrategy, E, DebugScope, isDynamic, isExactSelfClass, isDistributed, isRuntimeAccessible); // Set our BB list to have this function as its parent. This enables us to // splice efficiently basic blocks in between functions. BlockList.Parent = this; if (initFunction) initFunction({this}, &libswiftSpecificData, sizeof(libswiftSpecificData)); } void SILFunction::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) { setName(Name); assert(!LoweredType->hasTypeParameter() && "function type has open type parameters"); this->LoweredType = LoweredType; this->SpecializationInfo = nullptr; this->EntryCount = entryCount; this->Availability = AvailabilityRange::alwaysAvailable(); this->Bare = isBareSILFunction; this->Transparent = isTrans; this->SerializedKind = serializedKind; this->Thunk = isThunk; this->ClassSubclassScope = unsigned(classSubclassScope); this->GlobalInitFlag = false; this->InlineStrategy = inlineStrategy; this->Linkage = unsigned(Linkage); this->HasCReferences = false; this->MarkedAsUsed = false; this->IsAlwaysWeakImported = false; this->IsDynamicReplaceable = isDynamic; this->ExactSelfClass = isExactSelfClass; this->IsDistributed = isDistributed; this->IsRuntimeAccessible = isRuntimeAccessible; this->ForceEnableLexicalLifetimes = DoNotForceEnableLexicalLifetimes; this->UseStackForPackMetadata = DoUseStackForPackMetadata; this->HasUnsafeNonEscapableResult = false; this->IsPerformanceConstraint = false; this->stackProtection = false; this->Inlined = false; this->Zombie = false; this->HasOwnership = true, this->WasDeserializedCanonical = false; this->IsWithoutActuallyEscapingThunk = false; this->OptMode = unsigned(OptimizationMode::NotSet); this->perfConstraints = PerformanceConstraints::None; this->EffectsKindAttr = unsigned(E); assert(!Transparent || !IsDynamicReplaceable); validateSubclassScope(classSubclassScope, isThunk, nullptr); setDebugScope(DebugScope); setGenericEnvironment(genericEnv); } SILFunction::~SILFunction() { // If the function is recursive, a function_ref inst inside of the function // will give the function a non-zero ref count triggering the assertion. Thus // we drop all instruction references before we erase. // We also need to drop all references if instructions are allocated using // an allocator that may recycle freed memory. dropAllReferences(); if (snapshots) snapshots->~SILFunction(); if (ReplacedFunction) { ReplacedFunction->decrementRefCount(); ReplacedFunction = nullptr; } auto &M = getModule(); for (auto &BB : *this) { BB.eraseAllInstructions(M); } assert(RefCount == 0 && "Function cannot be deleted while function_ref's still exist"); assert(!newestAliveBlockBitfield && "Not all BasicBlockBitfields deleted at function destruction"); assert(!newestAliveNodeBitfield && "Not all NodeBitfields deleted at function destruction"); assert(!newestAliveOperandBitfield && "Not all OperandBitfields deleted at function destruction"); if (destroyFunction) destroyFunction({this}, &libswiftSpecificData, sizeof(libswiftSpecificData)); } void SILFunction::createSnapshot(int id) { assert(id != 0 && "invalid snapshot ID"); assert(!getSnapshot(id) && "duplicate snapshot"); SILFunction *newSnapshot = new (Module) SILFunction( Module, getLinkage(), getName(), getLoweredFunctionType(), getGenericEnvironment(), isBare(), isTransparent(), getSerializedKind(), getEntryCount(), isThunk(), getClassSubclassScope(), getInlineStrategy(), getEffectsKind(), getDebugScope(), isDynamicallyReplaceable(), isExactSelfClass(), isDistributed(), isRuntimeAccessible()); // Copy all relevant properties. // TODO: It's really unfortunate that this needs to be done manually. It would // be nice if all the properties are encapsulated into a single state, // which can be copied at once. newSnapshot->SpecializationInfo = SpecializationInfo; newSnapshot->ClangNodeOwner = ClangNodeOwner; newSnapshot->DeclCtxt = DeclCtxt; newSnapshot->Profiler = Profiler; newSnapshot->ReplacedFunction = ReplacedFunction; newSnapshot->RefAdHocRequirementFunction = RefAdHocRequirementFunction; newSnapshot->ObjCReplacementFor = ObjCReplacementFor; newSnapshot->SemanticsAttrSet = SemanticsAttrSet; newSnapshot->SpecializeAttrSet = SpecializeAttrSet; newSnapshot->Section = Section; newSnapshot->Availability = Availability; newSnapshot->specialPurpose = specialPurpose; newSnapshot->perfConstraints = perfConstraints; newSnapshot->GlobalInitFlag = GlobalInitFlag; newSnapshot->HasCReferences = HasCReferences; newSnapshot->MarkedAsUsed = MarkedAsUsed; newSnapshot->IsAlwaysWeakImported = IsAlwaysWeakImported; newSnapshot->HasOwnership = HasOwnership; newSnapshot->IsWithoutActuallyEscapingThunk = IsWithoutActuallyEscapingThunk; newSnapshot->OptMode = OptMode; newSnapshot->copyEffects(this); SILFunctionCloner cloner(newSnapshot); cloner.cloneFunction(this); newSnapshot->snapshotID = id; newSnapshot->snapshots = this->snapshots; this->snapshots = newSnapshot; // The cloner sometimes removes temporary instructions. getModule().flushDeletedInsts(); } SILFunction *SILFunction::getSnapshot(int ID) { SILFunction *sn = this; do { if (sn->snapshotID == ID) return sn; sn = sn->snapshots; } while (sn); return nullptr; } void SILFunction::restoreFromSnapshot(int ID) { SILFunction *sn = getSnapshot(ID); assert(sn && "no snapshot found"); clear(); SILFunctionCloner cloner(this); cloner.cloneFunction(sn); // Beside the function body, only restore those properties, which are/can be // modified by passes. // TODO: There should be a clear sepratation from initialize-once properties // (`let`) and properties which can be modified by passes (`var`). copyEffects(sn); // The cloner sometimes removes temporary instructions. getModule().flushDeletedInsts(); } void SILFunction::deleteSnapshot(int ID) { SILFunction *f = this; do { if (SILFunction *sn = f->snapshots) { if (sn->snapshotID == ID) { f->snapshots = sn->snapshots; sn->snapshots = nullptr; sn->~SILFunction(); getModule().flushDeletedInsts(); return; } } } while ((f = f->snapshots) != nullptr); } void SILFunction::createProfiler(SILDeclRef Ref) { assert(!Profiler && "Function already has a profiler"); assert(Ref && "Must have non-null SILDeclRef"); Profiler = SILProfiler::create(Module, Ref); if (!Profiler) return; // If we loaded a profile, set the entry counts for functions and closures // for PGO to use. if (Ref.isFunc()) { if (auto *Closure = Ref.getAbstractClosureExpr()) { setEntryCount(Profiler->getExecutionCount(Closure)); } else { auto *FD = Ref.getFuncDecl(); assert(FD); setEntryCount(Profiler->getExecutionCount(FD->getBody())); } } } bool SILFunction::hasForeignBody() const { if (!hasClangNode()) return false; return SILDeclRef::isClangGenerated(getClangNode()); } const SILFunction *SILFunction::getOriginOfSpecialization() const { if (!isSpecialization()) return nullptr; const SILFunction *p = getSpecializationInfo()->getParent(); while (p->isSpecialization()) { p = p->getSpecializationInfo()->getParent(); } return p; } GenericSignature SILFunction::getGenericSignature() const { return GenericEnv ? GenericEnv->getGenericSignature() : GenericSignature(); } void SILFunction::numberValues(llvm::DenseMap & ValueToNumberMap) const { unsigned idx = 0; for (auto &BB : *this) { for (auto I = BB.args_begin(), E = BB.args_end(); I != E; ++I) ValueToNumberMap[*I] = idx++; for (auto &I : BB) { auto results = I.getResults(); if (results.empty()) { ValueToNumberMap[I.asSILNode()] = idx++; } else { // Assign the instruction node the first result ID. ValueToNumberMap[I.asSILNode()] = idx; for (auto result : results) { ValueToNumberMap[result] = idx++; } } } } } ASTContext &SILFunction::getASTContext() const { return getModule().getASTContext(); } OptimizationMode SILFunction::getEffectiveOptimizationMode() const { if (OptimizationMode(OptMode) != OptimizationMode::NotSet) return OptimizationMode(OptMode); return getModule().getOptions().OptMode; } bool SILFunction::preserveDebugInfo() const { return getEffectiveOptimizationMode() <= OptimizationMode::NoOptimization; } bool SILFunction::shouldOptimize() const { return getEffectiveOptimizationMode() != OptimizationMode::NoOptimization; } Type SILFunction::mapTypeIntoContext(Type type) const { assert(!type->hasPrimaryArchetype()); if (GenericEnv) { // The complication here is that we sometimes call this with an AST interface // type, which might contain element archetypes, if it was the interface type // of a closure or local variable. if (type->hasElementArchetype()) return GenericEnv->mapTypeIntoContext(type); // Otherwise, assume we have an interface type for the "combined" captured // environment. return type.subst(MapIntoLocalArchetypeContext(GenericEnv, CapturedEnvs), LookUpConformanceInModule(), SubstFlags::PreservePackExpansionLevel); } assert(!type->hasTypeParameter()); return type; } SILType SILFunction::mapTypeIntoContext(SILType type) const { assert(!type.hasPrimaryArchetype()); if (GenericEnv) { auto genericSig = GenericEnv->getGenericSignature().getCanonicalSignature(); return type.subst(Module, MapIntoLocalArchetypeContext(GenericEnv, CapturedEnvs), LookUpConformanceInModule(), genericSig, SubstFlags::PreservePackExpansionLevel); } assert(!type.hasTypeParameter()); return type; } SILType GenericEnvironment::mapTypeIntoContext(SILModule &M, SILType type) const { assert(!type.hasPrimaryArchetype()); auto genericSig = getGenericSignature().getCanonicalSignature(); return type.subst(M, QueryInterfaceTypeSubstitutions(this), LookUpConformanceInModule(), genericSig, SubstFlags::PreservePackExpansionLevel); } bool SILFunction::isNoReturnFunction(TypeExpansionContext context) const { return SILType::getPrimitiveObjectType(getLoweredFunctionType()) .isNoReturnFunction(getModule(), context); } ResilienceExpansion SILFunction::getResilienceExpansion() const { // If a function definition is in another module, and // it was serialized due to package serialization opt, // a new attribute [serialized_for_package] is added // to the definition site. During deserialization, this // attribute is preserved if the current module is in // the same package, thus should be in the same resilience // domain. return (isSerialized() ? ResilienceExpansion::Minimal : ResilienceExpansion::Maximal); } const TypeLowering & SILFunction::getTypeLowering(AbstractionPattern orig, Type subst) { return getModule().Types.getTypeLowering(orig, subst, TypeExpansionContext(*this)); } const TypeLowering &SILFunction::getTypeLowering(Type t) const { return getModule().Types.getTypeLowering(t, TypeExpansionContext(*this)); } SILType SILFunction::getLoweredType(AbstractionPattern orig, Type subst) const { return getModule().Types.getLoweredType(orig, subst, TypeExpansionContext(*this)); } SILType SILFunction::getLoweredType(Type t) const { return getModule().Types.getLoweredType(t, TypeExpansionContext(*this)); } CanType SILFunction::getLoweredRValueType(AbstractionPattern orig, Type subst) const { return getModule().Types.getLoweredRValueType(TypeExpansionContext(*this), orig, subst); } CanType SILFunction::getLoweredRValueType(Type t) const { return getModule().Types.getLoweredRValueType(TypeExpansionContext(*this), t); } SILType SILFunction::getLoweredLoadableType(Type t) const { auto &M = getModule(); return M.Types.getLoweredLoadableType(t, TypeExpansionContext(*this), M); } const TypeLowering &SILFunction::getTypeLowering(SILType type) const { return getModule().Types.getTypeLowering(type, *this); } SILType SILFunction::getLoweredType(SILType t) const { return getTypeLowering(t).getLoweredType().getCategoryType(t.getCategory()); } bool SILFunction::isTypeABIAccessible(SILType type) const { return getModule().isTypeABIAccessible(type, TypeExpansionContext(*this)); } bool SILFunction::isWeakImported(ModuleDecl *module) const { if (auto *parent = getParentModule()) if (module->isImportedAsWeakLinked(parent)) return true; // For imported functions check the Clang declaration. if (ClangNodeOwner) return ClangNodeOwner->getClangDecl()->isWeakImported( getASTContext().LangOpts.getMinPlatformVersion()); // For native functions check a flag on the SILFunction // itself. if (!isAvailableExternally()) return false; if (isAlwaysWeakImported()) return true; if (Availability.isAlwaysAvailable()) return false; auto deploymentTarget = AvailabilityRange::forDeploymentTarget(getASTContext()); if (getASTContext().LangOpts.WeakLinkAtTarget) return !Availability.isSupersetOf(deploymentTarget); return !deploymentTarget.isContainedIn(Availability); } SILBasicBlock *SILFunction::createBasicBlock() { SILBasicBlock *newBlock = new (getModule()) SILBasicBlock(this); BlockList.push_back(newBlock); return newBlock; } SILBasicBlock *SILFunction::createBasicBlock(llvm::StringRef debugName) { SILBasicBlock *newBlock = new (getModule()) SILBasicBlock(this); newBlock->setDebugName(debugName); BlockList.push_back(newBlock); return newBlock; } SILBasicBlock *SILFunction::createBasicBlockAfter(SILBasicBlock *afterBB) { SILBasicBlock *newBlock = new (getModule()) SILBasicBlock(this); BlockList.insertAfter(afterBB->getIterator(), newBlock); return newBlock; } SILBasicBlock *SILFunction::createBasicBlockBefore(SILBasicBlock *beforeBB) { SILBasicBlock *newBlock = new (getModule()) SILBasicBlock(this); BlockList.insert(beforeBB->getIterator(), newBlock); return newBlock; } void SILFunction::moveAllBlocksFromOtherFunction(SILFunction *F) { BlockList.splice(begin(), F->BlockList); SILModule &mod = getModule(); for (SILBasicBlock &block : *this) { for (SILInstruction &inst : block) { mod.notifyMovedInstruction(&inst, F); } } } void SILFunction::moveBlockFromOtherFunction(SILBasicBlock *blockInOtherFunction, iterator insertPointInThisFunction) { SILFunction *otherFunc = blockInOtherFunction->getParent(); assert(otherFunc != this); BlockList.splice(insertPointInThisFunction, otherFunc->BlockList, blockInOtherFunction); SILModule &mod = getModule(); for (SILInstruction &inst : *blockInOtherFunction) { mod.notifyMovedInstruction(&inst, otherFunc); } } void SILFunction::moveBlockBefore(SILBasicBlock *BB, SILFunction::iterator IP) { assert(BB->getParent() == this); if (SILFunction::iterator(BB) == IP) return; BlockList.remove(BB); BlockList.insert(IP, BB); } //===----------------------------------------------------------------------===// // View CFG Implementation //===----------------------------------------------------------------------===// #ifndef NDEBUG static llvm::cl::opt MaxColumns("view-cfg-max-columns", llvm::cl::init(80), llvm::cl::desc("Maximum width of a printed node")); namespace { enum class LongLineBehavior { None, Truncate, Wrap }; } // end anonymous namespace static llvm::cl::opt LLBehavior("view-cfg-long-line-behavior", llvm::cl::init(LongLineBehavior::Truncate), llvm::cl::desc("Behavior when line width is greater than the " "value provided my -view-cfg-max-columns " "option"), llvm::cl::values( clEnumValN(LongLineBehavior::None, "none", "Print everything"), clEnumValN(LongLineBehavior::Truncate, "truncate", "Truncate long lines"), clEnumValN(LongLineBehavior::Wrap, "wrap", "Wrap long lines"))); static llvm::cl::opt RemoveUseListComments("view-cfg-remove-use-list-comments", llvm::cl::init(false), llvm::cl::desc("Should use list comments be removed")); template inline CaseValueTy getCaseValueForBB(const InstTy *Inst, const SILBasicBlock *BB) { for (unsigned i = 0, e = Inst->getNumCases(); i != e; ++i) { auto P = Inst->getCase(i); if (P.second != BB) continue; return P.first; } llvm_unreachable("Error! should never pass in BB that is not a successor"); } namespace llvm { template <> struct DOTGraphTraits : public DefaultDOTGraphTraits { DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} static std::string getGraphName(SILFunction *F) { return "CFG for '" + F->getName().str() + "' function"; } static std::string getSimpleNodeLabel(SILBasicBlock *Node, SILFunction *F) { std::string OutStr; raw_string_ostream OSS(OutStr); const_cast(Node)->printAsOperand(OSS, false); return OSS.str(); } static std::string getCompleteNodeLabel(SILBasicBlock *Node, SILFunction *F) { std::string Str; raw_string_ostream OS(Str); OS << *Node; std::string OutStr = OS.str(); if (OutStr[0] == '\n') OutStr.erase(OutStr.begin()); // Process string output to make it nicer... unsigned ColNum = 0; unsigned LastSpace = 0; for (unsigned i = 0; i != OutStr.length(); ++i) { if (OutStr[i] == '\n') { // Left justify OutStr[i] = '\\'; OutStr.insert(OutStr.begin() + i + 1, 'l'); ColNum = 0; LastSpace = 0; } else if (RemoveUseListComments && OutStr[i] == '/' && i != (OutStr.size() - 1) && OutStr[i + 1] == '/') { unsigned Idx = OutStr.find('\n', i + 1); // Find end of line OutStr.erase(OutStr.begin() + i, OutStr.begin() + Idx); --i; } else if (ColNum == MaxColumns) { // Handle long lines. if (LLBehavior == LongLineBehavior::Wrap) { if (!LastSpace) LastSpace = i; OutStr.insert(LastSpace, "\\l..."); ColNum = i - LastSpace; LastSpace = 0; i += 3; // The loop will advance 'i' again. } else if (LLBehavior == LongLineBehavior::Truncate) { unsigned Idx = OutStr.find('\n', i + 1); // Find end of line OutStr.erase(OutStr.begin() + i, OutStr.begin() + Idx); --i; } // Else keep trying to find a space. } else ++ColNum; if (OutStr[i] == ' ') LastSpace = i; } return OutStr; } std::string getNodeLabel(SILBasicBlock *Node, SILFunction *Graph) { if (isSimple()) return getSimpleNodeLabel(Node, Graph); else return getCompleteNodeLabel(Node, Graph); } static std::string getEdgeSourceLabel(SILBasicBlock *Node, SILBasicBlock::succblock_iterator I) { const SILBasicBlock *Succ = *I; const TermInst *Term = Node->getTerminator(); // Label source of conditional branches with "T" or "F" if (auto *CBI = dyn_cast(Term)) return (Succ == CBI->getTrueBB()) ? "T" : "F"; // Label source of switch edges with the associated value. if (auto *SI = dyn_cast(Term)) { if (SI->hasDefault() && SI->getDefaultBB() == Succ) return "def"; std::string Str; raw_string_ostream OS(Str); SILValue I = getCaseValueForBB(SI, Succ); OS << I; // TODO: or should we output the literal value of I? return OS.str(); } if (auto *SEIB = dyn_cast(Term)) { std::string Str; raw_string_ostream OS(Str); EnumElementDecl *E = getCaseValueForBB(SEIB, Succ); OS << E->getName(); return OS.str(); } if (auto *SEIB = dyn_cast(Term)) { std::string Str; raw_string_ostream OS(Str); EnumElementDecl *E = getCaseValueForBB(SEIB, Succ); OS << E->getName(); return OS.str(); } if (auto *DMBI = dyn_cast(Term)) return (Succ == DMBI->getHasMethodBB()) ? "T" : "F"; if (auto *CCBI = dyn_cast(Term)) return (Succ == CCBI->getSuccessBB()) ? "T" : "F"; if (auto *CCBI = dyn_cast(Term)) return (Succ == CCBI->getSuccessBB()) ? "T" : "F"; return ""; } }; } // namespace llvm #endif #ifndef NDEBUG static llvm::cl::opt TargetFunction("view-cfg-only-for-function", llvm::cl::init(""), llvm::cl::desc("Only print out the cfg for this function")); #endif static void viewCFGHelper(const SILFunction* f, bool skipBBContents) { /// When asserts are disabled, this should be a NoOp. #ifndef NDEBUG // If we have a target function, only print that function out. if (!TargetFunction.empty() && !(f->getName().str() == TargetFunction)) return; ViewGraph(const_cast(f), "cfg" + f->getName().str(), /*shortNames=*/skipBBContents); #endif } void SILFunction::viewCFG() const { viewCFGHelper(this, /*skipBBContents=*/false); } void SILFunction::viewCFGOnly() const { viewCFGHelper(this, /*skipBBContents=*/true); } bool SILFunction::hasDynamicSelfMetadata() const { auto paramTypes = getConventions().getParameterSILTypes(TypeExpansionContext::minimal()); if (paramTypes.empty()) return false; auto silTy = *std::prev(paramTypes.end()); if (!silTy.isObject()) return false; auto selfTy = silTy.getASTType(); if (auto metaTy = dyn_cast(selfTy)) { selfTy = metaTy.getInstanceType(); if (auto dynamicSelfTy = dyn_cast(selfTy)) selfTy = dynamicSelfTy.getSelfType(); } if (selfTy.isForeignReferenceType()) return false; return !!selfTy.getClassOrBoundGenericClass(); } bool SILFunction::hasName(const char *Name) const { return getName() == Name; } /* 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 */ bool SILFunction::canBeInlinedIntoCaller(SerializedKind_t callerSerializedKind) const { switch (getSerializedKind()) { // 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. case IsNotSerialized: return callerSerializedKind == IsNotSerialized; // If Package-CMO is enabled, we serialize package, public, // and @usableFromInline decls as [serialized_for_package]. // Their bodies must not, however, leak into @inlinable // functons (that are [serialized]) since they are inlined // outside of their defining module. // // If this callee is [serialized_for_package], the caller // must be either non-serialized or [serialized_for_package] // for this callee's body to be inlined into the caller. // It can however be referenced by [serialized] caller. case IsSerializedForPackage: return callerSerializedKind != IsSerialized; case IsSerialized: return true; } llvm_unreachable("Invalid serialized kind"); } /// Returns true if this function can be referenced from a fragile function /// body. bool SILFunction::hasValidLinkageForFragileRef(SerializedKind_t callerSerializedKind) const { // Fragile functions can reference 'static inline' functions imported // from C. if (hasForeignBody()) return true; // The call site of this function must have checked that // caller.isAnySerialized() is true, as indicated by the // function name itself (contains 'ForFragileRef'). assert(callerSerializedKind != IsNotSerialized); // If we can inline it, we can reference it. if (canBeInlinedIntoCaller(callerSerializedKind)) return true; // If the containing module has been serialized already, we no longer // enforce any invariants. if (getModule().isSerialized()) return true; // If the function has a subclass scope that limits its visibility outside // the module despite its linkage, we cannot reference it. if (getClassSubclassScope() == SubclassScope::Resilient && isAvailableExternally()) return false; // Otherwise, only public or package functions can be referenced. // If it has a package linkage at this point, package CMO must // have been enabled, so opt in for visibility. return hasPublicOrPackageVisibility(getLinkage(), /*includePackage*/ true); } bool SILFunction::isPossiblyUsedExternally() const { auto linkage = getLinkage(); // Hidden functions may be referenced by other C code in the linkage unit. if (linkage == SILLinkage::Hidden && hasCReferences()) return true; if (ReplacedFunction) return true; if (isDistributed() && isThunk()) return true; if (isRuntimeAccessible()) return true; if (markedAsUsed()) return true; if (shouldBePreservedForDebugger()) return true; // Declaration marked as `@_alwaysEmitIntoClient` that // returns opaque result type with availability conditions // has to be kept alive to emit opaque type metadata descriptor. if (markedAsAlwaysEmitIntoClient() && hasOpaqueResultTypeWithAvailabilityConditions()) return true; return swift::isPossiblyUsedExternally(linkage, getModule().isWholeModule()); } bool SILFunction::shouldBePreservedForDebugger() const { // Only preserve for the debugger at Onone. if (getEffectiveOptimizationMode() != OptimizationMode::NoOptimization) return false; if (!getModule().getOptions().ShouldFunctionsBePreservedToDebugger) return false; if (getModule().getASTContext().LangOpts.hasFeature(Feature::Embedded)) return false; if (isAvailableExternally()) return false; if (hasSemanticsAttr("no.preserve.debugger")) return false; // Only keep functions defined in this module. if (!isDefinition()) return false; if (getLinkage() == SILLinkage::Shared) return false; // Don't preserve anything markes as always emit into client. if (markedAsAlwaysEmitIntoClient()) return false; // Needed by lldb to print global variables which are propagated by the // mandatory GlobalOpt. if (isGlobalInit()) return true; // Preserve any user-written functions. if (auto declContext = getDeclContext()) if (auto decl = declContext->getAsDecl()) if (!decl->isImplicit()) return true; // Keep any setters/getters, even compiler generated ones. if (auto *accessorDecl = llvm::dyn_cast_or_null(getDeclContext())) if (accessorDecl->isGetterOrSetter()) return true; return false; } bool SILFunction::isExternallyUsedSymbol() const { return swift::isPossiblyUsedExternally(getEffectiveSymbolLinkage(), getModule().isWholeModule()); } void SILFunction::clear() { dropAllReferences(); eraseAllBlocks(); } void SILFunction::eraseAllBlocks() { BlockList.clear(); } void SILFunction::setGenericEnvironment(GenericEnvironment *env) { setGenericEnvironment(env, ArrayRef(), env ? env->getForwardingSubstitutionMap() : SubstitutionMap()); } bool SILFunction::shouldVerifyOwnership() const { return !hasSemanticsAttr("verify.ownership.sil.never"); } static Identifier getIdentifierForObjCSelector(ObjCSelector selector, ASTContext &Ctxt) { SmallVector buffer; auto str = selector.getString(buffer); return Ctxt.getIdentifier(str); } void SILFunction::setObjCReplacement(AbstractFunctionDecl *replacedFunc) { assert(ReplacedFunction == nullptr && ObjCReplacementFor.empty()); assert(replacedFunc != nullptr); ObjCReplacementFor = getIdentifierForObjCSelector( replacedFunc->getObjCSelector(), getASTContext()); } void SILFunction::setObjCReplacement(Identifier replacedFunc) { assert(ReplacedFunction == nullptr && ObjCReplacementFor.empty()); ObjCReplacementFor = replacedFunc; } // See swift/Basic/Statistic.h for declaration: this enables tracing // SILFunctions, is defined here to avoid too much layering violation / circular // linkage dependency. struct SILFunctionTraceFormatter : public UnifiedStatsReporter::TraceFormatter { void traceName(const void *Entity, raw_ostream &OS) const override { if (!Entity) return; const SILFunction *F = static_cast(Entity); F->printName(OS); } void traceLoc(const void *Entity, SourceManager *SM, clang::SourceManager *CSM, raw_ostream &OS) const override { if (!Entity) return; const SILFunction *F = static_cast(Entity); if (!F->hasLocation()) return; F->getLocation().getSourceRange().print(OS, *SM, false); } }; static SILFunctionTraceFormatter TF; template<> const UnifiedStatsReporter::TraceFormatter* FrontendStatsTracer::getTraceFormatter() { return &TF; } bool SILFunction::hasPrespecialization() const { for (auto *attr : getSpecializeAttrs()) { if (attr->isExported()) return true; } return false; } void SILFunction::forEachSpecializeAttrTargetFunction( llvm::function_ref action) { for (auto *attr : getSpecializeAttrs()) { if (auto *f = attr->getTargetFunction()) { action(f); } } } void BridgedFunction::registerBridging(SwiftMetatype metatype, RegisterFn initFn, RegisterFn destroyFn, WriteFn writeFn, ParseFn parseFn, CopyEffectsFn copyEffectsFn, GetEffectInfoFn effectInfoFn, GetMemBehaviorFn memBehaviorFn, ArgumentMayReadFn argumentMayReadFn, IsDeinitBarrierFn isDeinitBarrierFn) { functionMetatype = metatype; initFunction = initFn; destroyFunction = destroyFn; writeFunction = writeFn; parseFunction = parseFn; copyEffectsFunction = copyEffectsFn; getEffectInfoFunction = effectInfoFn; getMemBehvaiorFunction = memBehaviorFn; argumentMayReadFunction = argumentMayReadFn; isDeinitBarrierFunction = isDeinitBarrierFn; } std::pair SILFunction:: parseArgumentEffectsFromSource(StringRef effectStr, ArrayRef paramNames) { if (parseFunction) { llvm::SmallVector bridgedParamNames; for (StringRef paramName : paramNames) { bridgedParamNames.push_back(paramName); } ArrayRef bridgedParamNameArray = bridgedParamNames; auto error = parseFunction( {this}, effectStr, BridgedFunction::ParseEffectsMode::argumentEffectsFromSource, -1, {(const unsigned char *)bridgedParamNameArray.data(), bridgedParamNameArray.size()}); return {(const char *)error.message, (int)error.position}; } return {nullptr, 0}; } std::pair SILFunction:: parseArgumentEffectsFromSIL(StringRef effectStr, int argumentIndex) { if (parseFunction) { auto error = parseFunction( {this}, effectStr, BridgedFunction::ParseEffectsMode::argumentEffectsFromSIL, argumentIndex, {nullptr, 0}); return {(const char *)error.message, (int)error.position}; } return {nullptr, 0}; } std::pair SILFunction::parseGlobalEffectsFromSIL(StringRef effectStr) { if (parseFunction) { auto error = parseFunction( {this}, effectStr, BridgedFunction::ParseEffectsMode::globalEffectsFromSIL, -1, {nullptr, 0}); return {(const char *)error.message, (int)error.position}; } return {nullptr, 0}; } std::pair SILFunction:: parseMultipleEffectsFromSIL(StringRef effectStr) { if (parseFunction) { auto error = parseFunction( {this}, effectStr, BridgedFunction::ParseEffectsMode::multipleEffectsFromSIL, -1, {nullptr, 0}); return {(const char *)error.message, (int)error.position}; } return {nullptr, 0}; } void SILFunction::writeEffect(llvm::raw_ostream &OS, int effectIdx) const { if (writeFunction) { writeFunction({const_cast(this)}, {&OS}, effectIdx); } } void SILFunction::copyEffects(SILFunction *from) { if (copyEffectsFunction) { copyEffectsFunction({this}, {from}); } } bool SILFunction::hasArgumentEffects() const { if (getEffectInfoFunction) { BridgedFunction f = {const_cast(this)}; return getEffectInfoFunction(f, 0).isValid; } return false; } void SILFunction:: visitArgEffects(std::function c) const { if (!getEffectInfoFunction) return; int idx = 0; BridgedFunction bridgedFn = {const_cast(this)}; while (true) { BridgedFunction::EffectInfo ei = getEffectInfoFunction(bridgedFn, idx); if (!ei.isValid) return; if (!ei.isEmpty) { c(idx, ei.argumentIndex, ei.isDerived); } idx++; } } MemoryBehavior SILFunction::getMemoryBehavior(bool observeRetains) { if (!getMemBehvaiorFunction) return MemoryBehavior::MayHaveSideEffects; auto b = getMemBehvaiorFunction({this}, observeRetains); return (MemoryBehavior)b; } // Used by the MemoryLifetimeVerifier bool SILFunction::argumentMayRead(Operand *argOp, SILValue addr) { if (!argumentMayReadFunction) return true; return argumentMayReadFunction({this}, {argOp}, {addr}); } bool SILFunction::isDeinitBarrier() { if (!isDeinitBarrierFunction) return true; return isDeinitBarrierFunction({this}); } SourceFile *SILFunction::getSourceFile() const { auto declRef = getDeclRef(); if (!declRef) return nullptr; return declRef.getInnermostDeclContext()->getParentSourceFile(); }