//===--- 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 // //===----------------------------------------------------------------------===// #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILProfiler.h" #include "swift/SIL/CFG.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/AST/GenericEnvironment.h" #include "swift/Basic/OptimizationMode.h" #include "swift/Basic/Statistic.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/GraphWriter.h" using namespace swift; using namespace Lowering; ArrayRef SILSpecializeAttr::getRequirements() const { return {const_cast(this)->getRequirementsData(), numRequirements}; } SILSpecializeAttr::SILSpecializeAttr(ArrayRef requirements, bool exported, SpecializationKind kind) : numRequirements(requirements.size()), kind(kind), exported(exported) { std::copy(requirements.begin(), requirements.end(), getRequirementsData()); } SILSpecializeAttr *SILSpecializeAttr::create(SILModule &M, ArrayRef requirements, bool exported, SpecializationKind kind) { unsigned size = sizeof(SILSpecializeAttr) + sizeof(Requirement) * requirements.size(); void *buf = M.allocate(size, alignof(SILSpecializeAttr)); return ::new (buf) SILSpecializeAttr(requirements, exported, kind); } void SILFunction::addSpecializeAttr(SILSpecializeAttr *Attr) { if (getLoweredFunctionType()->getGenericSignature()) { Attr->F = this; SpecializeAttrSet.push_back(Attr); } } SILFunction * SILFunction::create(SILModule &M, SILLinkage linkage, StringRef name, CanSILFunctionType loweredType, GenericEnvironment *genericEnv, Optional loc, IsBare_t isBareSILFunction, IsTransparent_t isTrans, IsSerialized_t isSerialized, ProfileCounter entryCount, IsDynamicallyReplaceable_t isDynamic, 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(); } auto fn = new (M) SILFunction(M, linkage, name, loweredType, genericEnv, loc, isBareSILFunction, isTrans, isSerialized, entryCount, isThunk, classSubclassScope, inlineStrategy, E, insertBefore, debugScope, isDynamic); if (entry) entry->setValue(fn); return fn; } SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name, CanSILFunctionType LoweredType, GenericEnvironment *genericEnv, Optional Loc, IsBare_t isBareSILFunction, IsTransparent_t isTrans, IsSerialized_t isSerialized, ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope classSubclassScope, Inline_t inlineStrategy, EffectsKind E, SILFunction *InsertBefore, const SILDebugScope *DebugScope, IsDynamicallyReplaceable_t isDynamic) : Module(Module), Name(Name), LoweredType(LoweredType), GenericEnv(genericEnv), SpecializationInfo(nullptr), DebugScope(DebugScope), Bare(isBareSILFunction), Transparent(isTrans), Serialized(isSerialized), Thunk(isThunk), ClassSubclassScope(unsigned(classSubclassScope)), GlobalInitFlag(false), InlineStrategy(inlineStrategy), Linkage(unsigned(Linkage)), HasCReferences(false), IsWeakLinked(false), IsDynamicReplaceable(isDynamic), OptMode(OptimizationMode::NotSet), EffectsKindAttr(E), EntryCount(entryCount) { assert(!Transparent || !IsDynamicReplaceable); validateSubclassScope(classSubclassScope, isThunk, nullptr); if (InsertBefore) Module.functions.insert(SILModule::iterator(InsertBefore), this); else Module.functions.push_back(this); Module.removeFromZombieList(Name); // 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; } 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 (ReplacedFunction) { ReplacedFunction->decrementRefCount(); ReplacedFunction = nullptr; } auto &M = getModule(); for (auto &BB : *this) { for (auto I = BB.begin(), E = BB.end(); I != E;) { auto Inst = &*I; ++I; SILInstruction::destroy(Inst); // TODO: It is only safe to directly deallocate an // instruction if this BB is being removed in scope // of destructing a SILFunction. M.deallocateInst(Inst); } BB.InstList.clearAndLeakNodesUnsafely(); } assert(RefCount == 0 && "Function cannot be deleted while function_ref's still exist"); } void SILFunction::createProfiler(ASTNode Root, ForDefinition_t forDefinition) { assert(!Profiler && "Function already has a profiler"); Profiler = SILProfiler::create(Module, forDefinition, Root); } bool SILFunction::hasForeignBody() const { if (!hasClangNode()) return false; return SILDeclRef::isClangGenerated(getClangNode()); } 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] = idx++; } else { // Assign the instruction node the first result ID. ValueToNumberMap[&I] = idx; for (auto result : results) { ValueToNumberMap[result] = idx++; } } } } } ASTContext &SILFunction::getASTContext() const { return getModule().getASTContext(); } OptimizationMode SILFunction::getEffectiveOptimizationMode() const { if (OptMode != OptimizationMode::NotSet) return OptMode; return getModule().getOptions().OptMode; } bool SILFunction::shouldOptimize() const { return getEffectiveOptimizationMode() != OptimizationMode::NoOptimization; } Type SILFunction::mapTypeIntoContext(Type type) const { return GenericEnvironment::mapTypeIntoContext( getGenericEnvironment(), type); } SILType SILFunction::mapTypeIntoContext(SILType type) const { if (auto *genericEnv = getGenericEnvironment()) return genericEnv->mapTypeIntoContext(getModule(), type); return type; } SILType GenericEnvironment::mapTypeIntoContext(SILModule &M, SILType type) const { assert(!type.hasArchetype()); auto genericSig = getGenericSignature()->getCanonicalSignature(); return type.subst(M, QueryInterfaceTypeSubstitutions(this), LookUpConformanceInSignature(*genericSig), genericSig); } bool SILFunction::isNoReturnFunction() const { return SILType::getPrimitiveObjectType(getLoweredFunctionType()) .isNoReturnFunction(); } const TypeLowering & SILFunction::getTypeLowering(AbstractionPattern orig, Type subst) { return getModule().Types.getTypeLowering(orig, subst, getResilienceExpansion()); } const TypeLowering &SILFunction::getTypeLowering(Type t) const { return getModule().Types.getTypeLowering(t, getResilienceExpansion()); } SILType SILFunction::getLoweredType(AbstractionPattern orig, Type subst) const { return getModule().Types.getLoweredType(orig, subst, getResilienceExpansion()); } SILType SILFunction::getLoweredType(Type t) const { return getModule().Types.getLoweredType(t, getResilienceExpansion()); } SILType SILFunction::getLoweredLoadableType(Type t) const { return getModule().Types.getLoweredLoadableType(t, getResilienceExpansion()); } const TypeLowering &SILFunction::getTypeLowering(SILType type) const { return getModule().Types.getTypeLowering(type, getResilienceExpansion()); } bool SILFunction::isTypeABIAccessible(SILType type) const { return getModule().isTypeABIAccessible(type, getResilienceExpansion()); } SILBasicBlock *SILFunction::createBasicBlock() { return new (getModule()) SILBasicBlock(this, nullptr, false); } SILBasicBlock *SILFunction::createBasicBlockAfter(SILBasicBlock *afterBB) { assert(afterBB); return new (getModule()) SILBasicBlock(this, afterBB, /*after*/ true); } SILBasicBlock *SILFunction::createBasicBlockBefore(SILBasicBlock *beforeBB) { assert(beforeBB); return new (getModule()) SILBasicBlock(this, beforeBB, /*after*/ false); } //===----------------------------------------------------------------------===// // 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->getFullName(); return OS.str(); } if (auto *SEIB = dyn_cast(Term)) { std::string Str; raw_string_ostream OS(Str); EnumElementDecl *E = getCaseValueForBB(SEIB, Succ); OS << E->getFullName(); 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"; 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::hasSelfMetadataParam() const { auto paramTypes = getConventions().getParameterSILTypes(); 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(); } return !!selfTy.getClassOrBoundGenericClass(); } bool SILFunction::hasName(const char *Name) const { return getName() == Name; } /// Returns true if this function can be referenced from a fragile function /// body. bool SILFunction::hasValidLinkageForFragileRef() const { // Fragile functions can reference 'static inline' functions imported // from C. if (hasForeignBody()) return true; // If we can inline it, we can reference it. if (hasValidLinkageForFragileInline()) 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 functions can be referenced. return hasPublicVisibility(getLinkage()); } 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; return swift::isPossiblyUsedExternally(linkage, getModule().isWholeModule()); } bool SILFunction::isExternallyUsedSymbol() const { return swift::isPossiblyUsedExternally(getEffectiveSymbolLinkage(), getModule().isWholeModule()); } void SILFunction::convertToDeclaration() { assert(isDefinition() && "Can only convert definitions to declarations"); dropAllReferences(); getBlocks().clear(); } SubstitutionMap SILFunction::getForwardingSubstitutionMap() { if (ForwardingSubMap) return ForwardingSubMap; if (auto *env = getGenericEnvironment()) ForwardingSubMap = env->getForwardingSubstitutionMap(); return ForwardingSubMap; } 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 { 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 { 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; }