//===----- FineGrainedDependencies.h ----------------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #ifndef SWIFT_AST_FINE_GRAINED_DEPENDENCIES_H #define SWIFT_AST_FINE_GRAINED_DEPENDENCIES_H #include "swift/AST/EvaluatorDependencies.h" #include "swift/Basic/Debug.h" #include "swift/Basic/Fingerprint.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/NullablePtr.h" #include "swift/Basic/Range.h" #include "swift/Basic/ReferenceDependencyKeys.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/VirtualOutputBackend.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include // Summary: The FineGrainedDependency* files contain infrastructure for a // dependency system that, in the future, will be finer-grained than the current // dependency system. At present--12/5/18--they are using the same input // information as the current system and expected to produce the same results. // In future, we'll gather more information, feed it into this dependency // framework and get more selective recompilation. // // The frontend uses the information from the compiler to built a // SourceFileDepGraph consisting of SourceFileDepGraphNodes. // FineGrainedDependencies.* define these structures, and // FineGrainedDependenciesProducer has the frontend-unique code used to build // the SourceFileDepGraph. // // The driver reads the SourceFileDepGraph and integrates it into its dependency // graph, a ModuleDepGraph consisting of ModuleDepGraphNodes. // This file holds the declarations for the fine-grained dependency system // that are used by both the driver and frontend. // These include the graph structures common to both programs and also // the frontend graph, which must be read by the driver. //============================================================================== // MARK: Shorthands //============================================================================== namespace swift { class Decl; class DependencyTracker; class DiagnosticEngine; class FrontendOptions; class ModuleDecl; class SourceFile; class NominalTypeDecl; class ValueDecl; /// Use a new namespace to help keep the experimental code from clashing. namespace fine_grained_dependencies { class SourceFileDepGraph; using StringVec = std::vector; template using ConstPtrVec = std::vector; template using PairVec = std::vector>; using StringPairVec = PairVec; template using ConstPtrPairVec = std::vector>; //============================================================================== // MARK: General Utility classes //============================================================================== /// operator<< is needed for TwoStageMap::verify: class DependencyKey; raw_ostream &operator<<(raw_ostream &out, const DependencyKey &key); /// A general class to reuse objects that are keyed by a subset of their /// information. Used for \ref SourceFileDepGraph::Memoizer. template class Memoizer { using Memos = typename std::unordered_map; /// Holding already-created objects. Memos memos; public: Memoizer() = default; std::optional findExisting(KeyT key) { auto iter = memos.find(key); if (iter != memos.end()) return iter->second; return std::nullopt; } /// \p createFn must create a \ref ValueT that corresponds to the \ref KeyT /// passed into it. ValueT findExistingOrCreateIfNew(KeyT key, function_ref createFn) { if (auto existing = findExisting(key)) return existing.value(); ValueT v = createFn(key); (void)insert(key, v); return v; } /// Remember a new object (if differing from an existing one). /// \returns true iff the object was inserted. /// See \ref SourceFileDepGraphNode::addNode. bool insert(KeyT key, ValueT value) { return memos.insert(std::make_pair(key, value)).second; } }; /// A general container for double-indexing, used (indirectly) in the /// ModuleDepGraph. template class TwoStageMap { public: // Define this here so it can be changed easily. template using Map = std::unordered_map; using InnerMap = Map; private: Map map; public: std::optional find(const Key1 &k1, const Key2 &k2) const { auto iter = map.find(k1); if (iter == map.end()) return std::nullopt; auto iter2 = iter->second.find(k2); return iter2 == iter->second.end() ? std::nullopt : std::optional(iter2->second); } NullablePtr find(const Key1 &k1) const { auto iter = map.find(k1); return iter == map.end() ? nullptr : &iter->second; } /// The sought value must be present. Value findAndErase(const Key1 &k1, const Key2 &k2) { auto &submap = map[k1]; auto iter = submap.find(k2); assert(iter != submap.end() && "Cannot erase nonexistent element."); Value v = iter->second; submap.erase(iter); return v; } bool insert(const Key1 &k1, const Key2 &k2, Value &v) { return map[k1].insert(std::make_pair(k2, v)).second; } /// Move value from (old1, old2) to (new1, new2) bool move(const Key1 &old1, const Key2 &old2, const Key1 &new1, const Key2 &new2) { Value v = findAndErase(old1, old2); insert(old1, old2, v); } /// Returns the submap at \p k1. May create one if not present. Map &operator[](const Key1 &k1) { return map[k1]; } /// Check integrity and call \p verifyFn for each element, so that element can /// be verified. /// /// Requirements: // raw_ostream &operator<<(raw_ostream &out, const Key1 &), and // raw_ostream &operator<<(raw_ostream &out, const Key2 &) void verify(function_ref verifyFn) const { for (const auto &p1 : map) for (const auto &p2 : p1.second) { const Key1 &k1 = p1.first; const Key2 &k2 = p2.first; Value const v = p2.second; verifyFn(k1, k2, v); } } }; /// Double-indexing in either order; symmetric about key order. /// The ModuleDepGraph needs this structure. template class BiIndexedTwoStageMap { TwoStageMap map1; TwoStageMap map2; using KeyPair = std::pair; public: using Key2Map = typename decltype(map1)::InnerMap; using Key1Map = typename decltype(map2)::InnerMap; bool insert(const Key1 &k1, const Key2 &k2, Value &v) { const bool r1 = map1.insert(k1, k2, v); const bool r2 = map2.insert(k2, k1, v); (void)r2; assertConsistent(r1, r2); return r1; } bool insert(const Key2 &k2, const Key1 &k1, Value &v) { return insert(k1, k2, v); } std::optional find(const Key1 &k1, const Key2 &k2) const { auto v = map1.find(k1, k2); assert(assertConsistent(v, map2.find(k2, k1))); return v; } std::optional find(const Key2 &k2, Key1 &k1) const { return find(k1, k2); } /// Return the submap for a given Key1. May create one, after the fashion of /// the standard library. const Key2Map &operator[](const Key1 &k1) { return map1[k1]; } /// Return the submap for a given Key2. May create one, after the fashion of /// the standard library. const Key1Map &operator[](const Key2 &k2) { return map2[k2]; } NullablePtr find(const Key1 &k1) const { return map1.find(k1); } NullablePtr find(const Key2 &k2) const { return map2.find(k2); } /// Element must be present. /// Return the erased value. Value findAndErase(const Key1 &k1, const Key2 &k2) { Value v1 = map1.findAndErase(k1, k2); Value v2 = map2.findAndErase(k2, k1); assertConsistent(v1, v2); return v1; } /// Element must be present. /// Return the erased value. Value findAndErase(const Key2 &k2, const Key1 &k1) { return findAndErase(k1, k2); } /// Verify the integrity of each map and the cross-map consistency. /// Then call \p verifyFn for each entry found in each of the two maps, /// passing an index so that the verifyFn knows which map is being tested. void verify(function_ref verifyFn) const { map1.verify([&](const Key1 &k1, const Key2 &k2, Value v) { assertConsistent(map2.find(k2, k1).value(), v); }); map2.verify([&](const Key2 &k2, const Key1 &k1, Value v) { assertConsistent(map1.find(k1, k2).value(), v); }); map1.verify([&](const Key1 &k1, const Key2 &k2, Value v) { verifyFn(k1, k2, v, 0); }); map2.verify([&](const Key2 &k2, const Key1 &k1, Value v) { verifyFn(k1, k2, v, 1); }); } private: /// Helper function to ensure correspondence between \p v1 and \v2. template static bool assertConsistent(const T v1, const T v2) { assert(v1 == v2 && "Map1 and map2 should have the same elements."); return true; } }; // End of general declarations //============================================================================== // MARK: Start of fine-grained-dependency-specific code //============================================================================== /// Uses the provided module or source file to construct a dependency graph, /// which is provided back to the caller in the continuation callback. /// /// \Note The returned graph should not be escaped from the callback. bool withReferenceDependencies( llvm::PointerUnion MSF, const DependencyTracker &depTracker, llvm::vfs::OutputBackend &backend, StringRef outputPath, bool alsoEmitDotFile, llvm::function_ref); //============================================================================== // MARK: Enums //============================================================================== /// Instead of the status quo scheme of two kinds of "Depends", cascading and /// non-cascading this code represents each entity ("Provides" in the status /// quo), by a pair of nodes. One node represents the "implementation." If the /// implementation changes, users of the entity need not be recompiled. The /// other node represents the "interface." If the interface changes, any uses of /// that definition will need to be recompiled. The implementation always /// depends on the interface, since any change that alters the interface will /// require the implementation to be rebuilt. The interface does not depend on /// the implementation. In the dot files, interfaces are yellow and /// implementations white. Each node holds an instance variable describing which /// aspect of the entity it represents. enum class DeclAspect { interface, implementation, aspectCount }; const std::string DeclAspectNames[]{"interface", "implementation"}; template void forEachAspect(FnT fn) { for (size_t i = 0; i < size_t(DeclAspect::aspectCount); ++i) fn(DeclAspect(i)); } /// A pair of nodes that represent the two aspects of a given entity. /// Templated in order to serve for either SourceFileDepGraphNodes or /// ModuleDepGraphNodes. template class InterfaceAndImplementationPair { NodeT *interface; NodeT *implementation; public: InterfaceAndImplementationPair() : interface(nullptr), implementation(nullptr) {} InterfaceAndImplementationPair(NodeT *interface, NodeT *implementation) : interface(interface), implementation(implementation) { assert( interface->getKey().isInterface() && implementation->getKey().isImplementation() && "Interface must be interface, implementation must be implementation."); } NodeT *getInterface() const { return interface; } NodeT *getImplementation() const { return implementation; } }; //============================================================================== // MARK: DependencyKey //============================================================================== /// The dependency system loses some precision by lumping entities together for /// the sake of simplicity. In the future, it might be finer-grained. The /// DependencyKey carries the information needed to find all uses from a def /// because the data structures in the graph map the key of an entity to all the /// nodes representing uses of that entity, even though the node may not really /// use the entity. For example, argument names of functions are ignored. class DependencyKey { // For import/export friend ::llvm::yaml::MappingTraits; public: class Builder { private: const NodeKind kind; const DeclAspect aspect; const DeclContext *context; StringRef name; private: // A private copy constructor so our clients are forced to use the // move-only builder interface. explicit Builder(NodeKind kind, DeclAspect aspect, const DeclContext *context, StringRef name) : kind(kind), aspect(aspect), context(context), name(name) {} public: /// Creates a DependencyKey::Builder from the given \p kind and \p aspect /// with a \c null context and empty name. explicit Builder(NodeKind kind, DeclAspect aspect) : kind(kind), aspect(aspect), context(nullptr), name("") {} public: /// Consumes this builder and returns a dependency key created from its /// data. DependencyKey build() &&; public: /// Extracts the data from the given \p ref into a this builder. Builder fromReference(const evaluator::DependencyCollector::Reference &ref); public: /// Extracts the context data from the given declaration, if any. Builder withContext(const Decl *D) &&; /// Extracts the context data from the given decl-member pair, if any. Builder withContext(std::pair holderAndMember) &&; public: /// Copies the name data for the given swiftdeps file into this builder. Builder withName(StringRef swiftDeps) &&; /// Copies the name of the given declaration into this builder, if any. Builder withName(const Decl *decl) &&; /// Extracts the name from the given decl-member pair, if any. Builder withName(std::pair holderAndMember) &&; private: static StringRef getTopLevelName(const Decl *decl); }; private: NodeKind kind; DeclAspect aspect; /// The mangled context type name of the holder for \ref potentialMember, \ref /// member, and \ref nominal kinds. Otherwise unused. std::string context; /// The basic name of the entity. Unused for \ref potentialMember and \ref /// nominal kinds. std::string name; public: /// See \ref SourceFileDepGraphNode::SourceFileDepGraphNode(). DependencyKey() : kind(NodeKind::kindCount), aspect(DeclAspect::aspectCount), context(), name() {} /// For constructing a key in the frontend. DependencyKey(NodeKind kind, DeclAspect aspect, const std::string &context, const std::string &name) : kind(kind), aspect(aspect), context(context), name(name) { assert(verify()); } NodeKind getKind() const { return kind; } DeclAspect getAspect() const { return aspect; } StringRef getContext() const { return context; } StringRef getName() const { return name; } StringRef getSwiftDepsFromASourceFileProvideNodeKey() const { assert(getKind() == NodeKind::sourceFileProvide && "Receiver must be sourceFileProvide."); return getName(); } bool operator==(const DependencyKey &rhs) const { return getKind() == rhs.getKind() && getAspect() == rhs.getAspect() && getContext() == rhs.getContext() && getName() == rhs.getName(); } bool operator!=(const DependencyKey &rhs) const { return !(*this == rhs); } /// Return true if this key can be recorded as a use of def. /// If everything is the same except for aspect, it's tricky: /// The implementation does not depend on the interface; it's the other way /// around. bool canDependUpon(const DependencyKey &def) const { if (getKind() != def.getKind() || getContext() != def.getContext() || getName() != def.getName()) return true; if (getAspect() == def.getAspect()) return false; if (getAspect() == DeclAspect::implementation) return false; return true; } size_t hash() const { return llvm::hash_combine(kind, aspect, name, context); } bool isImplementation() const { return getAspect() == DeclAspect::implementation; } bool isInterface() const { return getAspect() == DeclAspect::interface; } DependencyKey correspondingImplementation() const { return withAspect(DeclAspect::implementation); } DependencyKey withAspect(DeclAspect aspect) const { return DependencyKey(kind, aspect, context, name); } static DependencyKey createKeyForWholeSourceFile(DeclAspect, StringRef swiftDeps); std::string humanReadableName() const; StringRef aspectName() const { return DeclAspectNames[size_t(aspect)]; } void dump(llvm::raw_ostream &os) const { os << asString() << "\n"; } SWIFT_DEBUG_DUMP { dump(llvm::errs()); } /// For debugging, needed for \ref TwoStageMap::verify std::string asString() const; bool verify() const; /// Since I don't have Swift enums, ensure name correspondence here. static void verifyNodeKindNames(); /// Since I don't have Swift enums, ensure name correspondence here. static void verifyDeclAspectNames(); private: // Name conversion helpers static std::string demangleTypeAsContext(StringRef); }; } // namespace fine_grained_dependencies } // namespace swift template <> struct std::hash { size_t operator()(const swift::fine_grained_dependencies::DependencyKey &key) const { return key.hash(); } }; template <> struct std::hash { size_t operator()(const swift::fine_grained_dependencies::DeclAspect aspect) const { return size_t(aspect); } }; template <> struct std::hash { size_t operator()(const swift::fine_grained_dependencies::NodeKind kind) const { return size_t(kind); } }; namespace swift { namespace fine_grained_dependencies { using ContextNameFingerprint = std::tuple>; } } // namespace swift //============================================================================== // MARK: DepGraphNode //============================================================================== /// Part of an experimental, new, infrastructure that can handle fine-grained /// dependencies. The basic idea is a graph, where each node represents the /// definition of entity in the program (a Decl or a source file/swiftdeps /// file). Each node will (eventually) have a fingerprint so that we can tell /// when an entity has changed. Arcs in the graph connect a definition that /// provides information to a definition that uses the information, so that when /// something changes, a traversal of the arc reveals the entities needing to be /// rebuilt. /// /// Some changes are transitive (i.e. “cascading”): given A -> B -> C, if the /// link from A to B cascades then C must be rebuilt even if B does not change. /// Rather than having two kinds of arcs, I am representing this distinction by /// splitting the nodes: each entity has two nodes: one for its interface and /// another for its implementation. A cascading dependency translates into one /// that goes to the interface, while a non-cascading one goes to the /// implementation. These graphs reflect what can be known from today’s /// dependency information. Once the infrastructure is in place, we can work on /// improving it. namespace swift { namespace fine_grained_dependencies { class DepGraphNode { /// Def->use arcs go by DependencyKey. There may be >1 node for a given key. DependencyKey key; /// The frontend records in the fingerprint, all of the information about an /// entity, such that any uses need be rebuilt only if the fingerprint /// changes. /// When the driver reloads a dependency graph (after a frontend job has run), /// it can use the fingerprint to determine if the entity has changed and thus /// if uses need to be recompiled. /// /// However, at present, the frontend does not record this information for /// every Decl; it only records it for the source-file-as-a-whole in the /// interface hash. The interface hash is a product of all the tokens that are /// not inside of function bodies. Thus, if there is no fingerprint, when the /// frontend creates an interface node, // it adds a dependency to it from the implementation source file node (which // has the interfaceHash as its fingerprint). std::optional fingerprint; friend ::llvm::yaml::MappingTraits; public: /// See \ref SourceFileDepGraphNode::SourceFileDepGraphNode(). DepGraphNode() : key(), fingerprint() {} DepGraphNode(DependencyKey key, std::optional fingerprint) : key(key), fingerprint(fingerprint) {} DepGraphNode(const DepGraphNode &other) = default; bool operator==(const DepGraphNode &other) const { return getKey() == other.getKey() && getFingerprint() == other.getFingerprint(); } const DependencyKey &getKey() const { return key; } /// Only used when the driver is reading a SourceFileDepGraphNode. void setKey(const DependencyKey &key) { this->key = key; } const std::optional getFingerprint() const { return fingerprint; } /// When driver reads a SourceFileDepGraphNode, it may be a node that was /// created to represent a name-lookup (a.k.a a "depend") in the frontend. In /// that case, the node represents an entity that resides in some other file /// whose swiftdeps file has not been read by the driver. Later, when the /// driver does read the node corresponding to the actual Decl, that node may /// (someday) have a fingerprint. In order to preserve the /// ModuleDepGraphNode's identity but bring its fingerprint up to date, it /// needs to set the fingerprint *after* the node has been created. void setFingerprint(std::optional fp) { fingerprint = fp; } SWIFT_DEBUG_DUMP; void dump(llvm::raw_ostream &os) const; std::string humanReadableName(StringRef where) const; bool verify() const { key.verify(); return true; } }; //============================================================================== // MARK: SourceFileDepGraphNode //============================================================================== class SourceFileDepGraph; /// A node in a graph that represents the dependencies computed when compiling a /// single primary source file. Each such node represents a definition. Such a /// graph is always constructed monotonically; it never shrinks or changes once /// finished. The frontend does not need to be able to remove nodes from the /// graph, so it can represent arcs with a simple format relying on sequence /// numbers. class SourceFileDepGraphNode : public DepGraphNode { /// To represent Arcs in a serializable fashion, the code puts all nodes in /// the graph in a vector (`allNodes`), and uses the index in that vector to /// refer to the node. size_t sequenceNumber = ~0; /// Holds the sequence numbers of definitions I depend upon. llvm::SetVector defsIDependUpon; /// True iff a Decl exists for this node. /// If a provides and a depends in the existing system both have the same key, /// only one SourceFileDepGraphNode is emitted. bool isProvides = false; friend ::llvm::yaml::MappingContextTraits; public: /// When the driver imports a node create an uninitialized instance for /// deserializing. SourceFileDepGraphNode() : DepGraphNode() {} /// Used by the frontend to build nodes. SourceFileDepGraphNode(DependencyKey key, std::optional fingerprint, bool isProvides) : DepGraphNode(key, fingerprint), isProvides(isProvides) { assert(key.verify()); } bool isDepends() const { return !getIsProvides(); } bool getIsProvides() const { return isProvides; } void setIsProvides() { isProvides = true; } bool operator==(const SourceFileDepGraphNode &other) const { return DepGraphNode::operator==(other) && sequenceNumber == other.sequenceNumber && defsIDependUpon == other.defsIDependUpon && isProvides == other.isProvides; } size_t getSequenceNumber() const { return sequenceNumber; } void setSequenceNumber(size_t n) { sequenceNumber = n; } /// In the frontend, def-use links are kept in the def node. /// Call \p fn with the sequence number of each use. void forEachDefIDependUpon(function_ref fn) const { std::for_each(defsIDependUpon.begin(), defsIDependUpon.end(), fn); } /// Record the sequence number, \p n, of another use. /// The relationship between an interface and its implementation is NOT /// included here. See \c /// SourceFileDepGraph::findExistingNodePairOrCreateAndAddIfNew. void addDefIDependUpon(size_t n) { if (n != getSequenceNumber()) defsIDependUpon.insert(n); } std::string humanReadableName() const { return DepGraphNode::humanReadableName(getIsProvides() ? "here" : "somewhere else"); } SWIFT_DEBUG_DUMP; void dump(llvm::raw_ostream &os) const; bool verify() const { DepGraphNode::verify(); assert(getIsProvides() || isDepends()); assert(verifySequenceNumber()); return true; } bool verifySequenceNumber() const { const auto &k = getKey(); if (k.getKind() != NodeKind::sourceFileProvide) return true; switch (k.getAspect()) { case DeclAspect::interface: assert(getSequenceNumber() == sourceFileProvidesInterfaceSequenceNumber); return true; case DeclAspect::implementation: assert(getSequenceNumber() == sourceFileProvidesImplementationSequenceNumber); return true; default: llvm_unreachable("neither interface nor implementation"); } } static constexpr const size_t sourceFileProvidesInterfaceSequenceNumber = 0; static constexpr const size_t sourceFileProvidesImplementationSequenceNumber = 1; }; //============================================================================== // MARK: SourceFileDepGraph //============================================================================== /// The dependency graph produced by the frontend and consumed by the driver. /// See \ref Node in FineGrainedDependencies.h class SourceFileDepGraph { /// Every node in the graph. Indices used for serialization. /// Use addNode instead of adding directly. std::vector allNodes; /// When the frontend constructs the SourceFileDepGraph, it might encounter a /// name-lookup ("Depend") or a Decl ("Provide") whose node would be /// indistinguishable from a node it has already constructed. So it memoizes /// those nodes, reusing an existing node rather than creating a new one. Memoizer memoizedNodes; public: /// For templates such as DotFileEmitter. using NodeType = SourceFileDepGraphNode; friend ::llvm::yaml::MappingTraits; SourceFileDepGraph() = default; SourceFileDepGraph(const SourceFileDepGraph &g) = delete; SourceFileDepGraph(SourceFileDepGraph &&g) = default; /// Nodes are owned by the graph. ~SourceFileDepGraph() { forEachNode([&](SourceFileDepGraphNode *n) { delete n; }); } SourceFileDepGraphNode *getNode(size_t sequenceNumber) const; InterfaceAndImplementationPair getSourceFileNodePair() const; StringRef getSwiftDepsOfJobThatProducedThisGraph() const; std::string getGraphID() const { return getSourceFileNodePair().getInterface()->getKey().humanReadableName(); } void forEachNode(function_ref fn) const { for (auto i : indices(allNodes)) fn(getNode(i)); } void forEachArc(function_ref fn) const; void forEachDefDependedUponBy( const SourceFileDepGraphNode *n, function_ref fn) const { n->forEachDefIDependUpon([&](size_t useIndex) { fn(getNode(useIndex)); }); } /// The frontend creates a pair of nodes for every tracked Decl and the source /// file itself. InterfaceAndImplementationPair findExistingNodePairOrCreateAndAddIfNew( const DependencyKey &interfaceKey, std::optional fingerprint); NullablePtr findExistingNode(const DependencyKey &key); SourceFileDepGraphNode * findExistingNodeOrCreateIfNew(const DependencyKey &key, const std::optional fingerprint, bool isProvides); /// \p Use is the Node that must be rebuilt when \p def changes. /// Record that fact in the graph. void addArc(SourceFileDepGraphNode *def, SourceFileDepGraphNode *use) { getNode(use->getSequenceNumber()) ->addDefIDependUpon(def->getSequenceNumber()); } /// Read a swiftdeps file at \p path and return a SourceFileDepGraph if /// successful. If \p allowSwiftModule is true, try to load the information /// from a swiftmodule file if appropriate. std::optional static loadFromPath( StringRef, bool allowSwiftModule = false); /// Read a swiftdeps file from \p buffer and return a SourceFileDepGraph if /// successful. std::optional static loadFromBuffer(llvm::MemoryBuffer &); std::optional static loadFromSwiftModuleBuffer( llvm::MemoryBuffer &); void verifySame(const SourceFileDepGraph &other) const; // Fail with a message instead of returning false. bool verify() const; /// Ensure that when read, the graph is the same as what was written. bool verifyReadsWhatIsWritten(StringRef path) const; bool verifySequenceNumber() const; void emitDotFile(llvm::vfs::OutputBackend &outputBackend, StringRef outputPath, DiagnosticEngine &diags); void addNode(SourceFileDepGraphNode *n) { n->setSequenceNumber(allNodes.size()); allNodes.push_back(n); assert(n->verifySequenceNumber() && "Certain nodes must be in certain places"); } }; //============================================================================== // MARK: DotFileEmitter //============================================================================== /// To aid in debugging, both the SourceFileDepGraph and the ModuleDepGraph can /// be written out as dot files, which can be read into graphfiz and OmniGraffle /// to display the graphs. template class DotFileEmitter { using NodeT = typename GraphT::NodeType; /// Stream to write to. llvm::raw_ostream &out; /// Human-readable graph identifier. std::string graphID; /// For the sake of clarity, we commonly exclude these. const bool includeExternals, includeAPINotes; /// The graph to write out. const GraphT &g; /// Since ModuleDepGraphNodes have no sequence numbers, use the stringified /// pointer value for an nodeID. Memoize the nodes here. std::unordered_set nodeIDs; public: DotFileEmitter(llvm::raw_ostream &out, GraphT &g, const bool includeExternals, const bool includeAPINotes) : out(out), graphID(g.getGraphID()), includeExternals(includeExternals), includeAPINotes(includeAPINotes), g(g) {} void emit() { emitPrelude(); emitLegend(); emitNodes(); emitArcs(); emitPostlude(); } private: void emitPrelude() const { out << "digraph \"" << graphID << "\" {\n"; } void emitPostlude() const { out << "\n}\n"; } void emitNodes() { g.forEachNode([&](const NodeT *n) { emitGraphNode(n); }); } static std::string nodeID(const NodeT *n) { return std::to_string(size_t(n)); } void emitGraphNode(const NodeT *n) { if (includeGraphNode(n)) { emitDotNode(nodeID(n), nodeLabel(n), shape(n), fillColor(n), style(n)); } } void emitDotNode(StringRef id, StringRef label, StringRef shape, StringRef fillColor, StringRef style = StringRef()) { auto inserted = nodeIDs.insert(id.str()); (void)inserted; assert(inserted.second && "NodeIDs must be unique."); out << "\"" << id << "\" [ " << "label = \"" << label << "\", " << "shape = " << shape << " , " << "fillcolor = " << fillColor; if (!style.empty()) out << ", " << "style = " << style; out << " ];\n"; } bool includeGraphNode(const NodeT *n) const { bool externalPredicate = includeExternals || n->getKey().getKind() != NodeKind::externalDepend; bool apiPredicate = includeAPINotes || !StringRef(n->getKey().humanReadableName()).ends_with(".apinotes"); return externalPredicate && apiPredicate; } bool includeGraphArc(const NodeT *def, const NodeT *use) const { return includeGraphNode(def) && includeGraphNode(use); } void emitArcs() { g.forEachArc([&](const NodeT *def, const NodeT *use) { if (includeGraphArc(def, use)) emitGraphArc(def, use); }); } /// show arc from def to use void emitGraphArc(const NodeT *def, const NodeT *use) const { auto defID = nodeID(def); auto useID = nodeID(use); assert(nodeIDs.count(defID) && "Definition must exist."); assert(nodeIDs.count(useID) && "Use must exist."); emitDotArc(defID, useID); } void emitDotArc(StringRef from, StringRef to) const { out << from << " -> " << to << ";\n"; } static StringRef shape(const NodeT *n) { return shape(n->getKey().getKind()); } static StringRef style(const NodeT *n) { return !n->getIsProvides() ? "dotted" : "solid"; } static const std::string &shape(const NodeKind kind) { static const std::string shapes[]{"box", "parallelogram", "ellipse", "triangle", "diamond", "house", "hexagon"}; return shapes[size_t(kind)]; } static std::string fillColor(const NodeT *n) { return !n->getIsProvides() ? "azure" : n->getKey().isInterface() ? "yellow" : "white"; } /// Emit sample types of dependencies with their corresponding shapes. void emitLegend() { for (size_t i = 0; i < size_t(NodeKind::kindCount); ++i) { const auto s = shape(NodeKind(i)); emitDotNode(s, NodeKindNames[i], s, "azure"); } } static std::string nodeLabel(const NodeT *n) { return llvm::yaml::escape(n->humanReadableName()); } }; } // end namespace fine_grained_dependencies } // end namespace swift #endif // SWIFT_AST_FINE_GRAINED_DEPENDENCIES_H