Files
swift-mirror/lib/Driver/DependencyGraph.cpp
Jordan Rose 8b5d763e39 [Driver] Add a new DependencyGraph class.
...and some basic unit tests for it.

The purpose of this class is to track dependencies between opaque nodes.
The dependency edges are (kind, string) pairs, where the "kind"
distinguishes different kinds of dependencies (currently "top-level names"
and "types that we do lookup on"). The step is to make use of it in
running compile commands.

The YAML-based file format is only for bring-up and testing purposes.
I intend to switch it to a bitcode-based format in the long run.

Part of rdar://problem/15353101

Swift SVN r23223
2014-11-11 00:43:29 +00:00

178 lines
6.0 KiB
C++

//===--- 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/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
parseDependencyFileImpl(llvm::MemoryBuffer &buffer, bool providesOnly,
std::function<void(StringRef, DependencyKind,
DependencyDirection)> 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())
return true;
auto *topLevelMap = dyn_cast_or_null<yaml::MappingNode>(I->getRoot());
if (!topLevelMap)
return true;
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<yaml::ScalarNode>(i->getKey());
using KindPair = std::pair<DependencyKind, DependencyDirection>;
KindPair dirAndKind = llvm::StringSwitch<KindPair>(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<yaml::SequenceNode>(i->getValue());
for (const yaml::Node &rawEntry : *entries) {
auto *entry = cast<yaml::ScalarNode>(&rawEntry);
callback(entry->getValue(scratch), dirAndKind.first, dirAndKind.second);
}
}
return false;
}
template <typename Fn>
static bool parseDependencyFile(llvm::MemoryBuffer &buffer, bool providesOnly,
Fn fn) {
return parseDependencyFileImpl(buffer, providesOnly, std::cref(fn));
}
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<DependencyMaskTy>(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<const void *> &newlyMarked,
const void *node) {
assert(Provides.count(node) && "node is not in the graph");
SmallVector<const void *, 16> 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);
}
}