mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
...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
178 lines
6.0 KiB
C++
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);
|
|
}
|
|
}
|