//===--- DependencyGraph.cpp - Track intra-module dependencies ------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/Driver/DependencyGraph.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/YAMLParser.h" using namespace swift; namespace { using DependencyMaskTy = DependencyGraphImpl::DependencyMaskTy; enum class DependencyKind : DependencyMaskTy { Name = 1 << 0, Type = 1 << 1 }; enum class DependencyDirection : bool { Depends, Provides }; } // end anonymous namespace static bool parseDependencyFile(llvm::MemoryBuffer &buffer, bool providesOnly, llvm::function_ref callback) { using namespace llvm; // FIXME: Switch to a format other than YAML. llvm::SourceMgr SM; yaml::Stream stream(buffer.getMemBufferRef(), SM); auto I = stream.begin(); if (I == stream.end() || !I->getRoot()) return true; auto *topLevelMap = dyn_cast(I->getRoot()); if (!topLevelMap) return !isa(I->getRoot()); SmallString<64> scratch; // FIXME: LLVM's YAML support does incremental parsing in such a way that // for-range loops break. for (auto i = topLevelMap->begin(), e = topLevelMap->end(); i != e; ++i) { auto *key = cast(i->getKey()); using KindPair = std::pair; KindPair dirAndKind = llvm::StringSwitch(key->getValue(scratch)) .Case("top-level", std::make_pair(DependencyKind::Name, DependencyDirection::Depends)) .Case("member-access", std::make_pair(DependencyKind::Type, DependencyDirection::Depends)) .Case("provides", std::make_pair(DependencyKind::Name, DependencyDirection::Provides)) .Case("nominals", std::make_pair(DependencyKind::Type, DependencyDirection::Provides)); if (providesOnly && dirAndKind.second != DependencyDirection::Provides) continue; auto *entries = cast(i->getValue()); for (const yaml::Node &rawEntry : *entries) { auto *entry = cast(&rawEntry); callback(entry->getValue(scratch), dirAndKind.first, dirAndKind.second); } } return false; } bool DependencyGraphImpl::loadFromPath(const void *node, StringRef path) { auto buffer = llvm::MemoryBuffer::getFile(path); if (!buffer) return true; return loadFromBuffer(node, *buffer.get()); } bool DependencyGraphImpl::loadFromString(const void *node, StringRef data) { auto buffer = llvm::MemoryBuffer::getMemBuffer(data); return loadFromBuffer(node, *buffer); } bool DependencyGraphImpl::loadFromBuffer(const void *node, llvm::MemoryBuffer &buffer) { auto &provides = Provides[node]; provides.clear(); return parseDependencyFile(buffer, /*providesOnly=*/isMarked(node), [this, node, &provides](StringRef name, DependencyKind kind, DependencyDirection dir) { auto kindAsMask = static_cast(kind); switch (dir) { case DependencyDirection::Depends: { auto &entries = Dependencies[name]; auto iter = std::find_if(entries.begin(), entries.end(), [node](const DependencyPairTy &entry) -> bool { return node == entry.second; }); if (iter == entries.end()) entries.emplace_back(kindAsMask, node); else iter->first |= kindAsMask; break; } case DependencyDirection::Provides: { auto iter = std::find_if(provides.begin(), provides.end(), [name](const ProvidesPairTy &entry) -> bool { return name == entry.second; }); if (iter == provides.end()) provides.emplace_back(kindAsMask, name); else iter->first |= kindAsMask; break; } } }); } void DependencyGraphImpl::markTransitive(SmallVectorImpl &newlyMarked, const void *node) { assert(Provides.count(node) && "node is not in the graph"); SmallVector worklist; auto addDependentsToWorklist = [&](const void *next) { auto allProvided = Provides.find(next); if (allProvided == Provides.end()) return; for (const auto &provided : allProvided->second) { auto allDependents = Dependencies.find(provided.second); if (allDependents == Dependencies.end()) continue; for (const auto &dependent : allDependents->second) { if ((provided.first & dependent.first) == 0) continue; if (isMarked(dependent.second)) continue; worklist.push_back(dependent.second); } } }; // Always mark through the starting node, even if it's already marked. Marked.insert(node); addDependentsToWorklist(node); while (!worklist.empty()) { const void *next = worklist.pop_back_val(); if (!Marked.insert(next)) continue; newlyMarked.push_back(next); addDependentsToWorklist(next); } }