mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Fix the non-deterministic .swiftdeps output. In contrary to the comments in the `FineGrainedDependencies.h`, the non-determinism is not because of the use of unordered_* data structure there. Those data structures are not traversed so removing all the unused traversing methods to avoid the confusion. The true reason for the non-determinism is all the DenseSet in the Evaluator dependency tracking, that causes the FineGrainedDependencies to see arbitrary ordering. Use `SetVector` instead to keep track of the insertion order to make dependency output to be deterministic. rdar://104876331
988 lines
36 KiB
C++
988 lines
36 KiB
C++
//===----- 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 <optional>
|
||
#include <string>
|
||
#include <unordered_map>
|
||
#include <unordered_set>
|
||
#include <vector>
|
||
|
||
// 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<std::string>;
|
||
|
||
template <typename Element> using ConstPtrVec = std::vector<const Element *>;
|
||
|
||
template <typename First, typename Second>
|
||
using PairVec = std::vector<std::pair<First, Second>>;
|
||
|
||
using StringPairVec = PairVec<std::string, std::string>;
|
||
|
||
template <typename First, typename Second>
|
||
using ConstPtrPairVec = std::vector<std::pair<const First *, const Second *>>;
|
||
|
||
//==============================================================================
|
||
// 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 <typename KeyT, typename ValueT> class Memoizer {
|
||
using Memos = typename std::unordered_map<KeyT, ValueT>;
|
||
/// Holding already-created objects.
|
||
Memos memos;
|
||
|
||
public:
|
||
Memoizer() = default;
|
||
|
||
std::optional<ValueT> 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<ValueT(const KeyT &)> 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 <typename Key1, typename Key2, typename Value> class TwoStageMap {
|
||
public:
|
||
// Define this here so it can be changed easily.
|
||
template <typename Key, typename MapValue>
|
||
using Map = std::unordered_map<Key, MapValue>;
|
||
|
||
using InnerMap = Map<Key2, Value>;
|
||
|
||
private:
|
||
Map<Key1, InnerMap> map;
|
||
|
||
public:
|
||
std::optional<Value> 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<Value>(iter2->second);
|
||
}
|
||
|
||
NullablePtr<const InnerMap> 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<Key2, Value> &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<void(const Key1 &k1, const Key2 &k2, Value v)>
|
||
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 <typename Key1, typename Key2, typename Value>
|
||
class BiIndexedTwoStageMap {
|
||
TwoStageMap<Key1, Key2, Value> map1;
|
||
TwoStageMap<Key2, Key1, Value> map2;
|
||
|
||
using KeyPair = std::pair<Key1, Key2>;
|
||
|
||
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<Value> 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<Value> 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<const Key2Map> find(const Key1 &k1) const {
|
||
return map1.find(k1);
|
||
}
|
||
NullablePtr<const Key1Map> 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<void(const Key1 &k1, const Key2 &k2, Value v,
|
||
unsigned index)>
|
||
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 <typename T> 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<const ModuleDecl *, const SourceFile *> MSF,
|
||
const DependencyTracker &depTracker, llvm::vfs::OutputBackend &backend,
|
||
StringRef outputPath, bool alsoEmitDotFile,
|
||
llvm::function_ref<bool(SourceFileDepGraph &&)>);
|
||
|
||
//==============================================================================
|
||
// 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 <typename FnT> 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 <typename NodeT> 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<DependencyKey>;
|
||
|
||
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<const NominalTypeDecl *, const ValueDecl *>
|
||
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<const NominalTypeDecl *, const ValueDecl *>
|
||
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<typename swift::fine_grained_dependencies::DependencyKey> {
|
||
size_t
|
||
operator()(const swift::fine_grained_dependencies::DependencyKey &key) const {
|
||
return key.hash();
|
||
}
|
||
};
|
||
template <>
|
||
struct std::hash<typename swift::fine_grained_dependencies::DeclAspect> {
|
||
size_t
|
||
operator()(const swift::fine_grained_dependencies::DeclAspect aspect) const {
|
||
return size_t(aspect);
|
||
}
|
||
};
|
||
template <>
|
||
struct std::hash<typename swift::fine_grained_dependencies::NodeKind> {
|
||
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<std::string, std::string, std::optional<std::string>>;
|
||
}
|
||
} // 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> fingerprint;
|
||
|
||
friend ::llvm::yaml::MappingTraits<DepGraphNode>;
|
||
|
||
public:
|
||
/// See \ref SourceFileDepGraphNode::SourceFileDepGraphNode().
|
||
DepGraphNode() : key(), fingerprint() {}
|
||
|
||
DepGraphNode(DependencyKey key, std::optional<Fingerprint> 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<Fingerprint> 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<Fingerprint> 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<size_t> 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<SourceFileDepGraphNode,
|
||
SourceFileDepGraph>;
|
||
|
||
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> 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<void(size_t)> 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<SourceFileDepGraphNode *> 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<DependencyKey, SourceFileDepGraphNode *> memoizedNodes;
|
||
|
||
public:
|
||
/// For templates such as DotFileEmitter.
|
||
using NodeType = SourceFileDepGraphNode;
|
||
|
||
friend ::llvm::yaml::MappingTraits<SourceFileDepGraph>;
|
||
|
||
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<SourceFileDepGraphNode>
|
||
getSourceFileNodePair() const;
|
||
|
||
StringRef getSwiftDepsOfJobThatProducedThisGraph() const;
|
||
|
||
std::string getGraphID() const {
|
||
return getSourceFileNodePair().getInterface()->getKey().humanReadableName();
|
||
}
|
||
|
||
void forEachNode(function_ref<void(SourceFileDepGraphNode *)> fn) const {
|
||
for (auto i : indices(allNodes))
|
||
fn(getNode(i));
|
||
}
|
||
|
||
void forEachArc(function_ref<void(const SourceFileDepGraphNode *def,
|
||
const SourceFileDepGraphNode *use)>
|
||
fn) const;
|
||
|
||
void forEachDefDependedUponBy(
|
||
const SourceFileDepGraphNode *n,
|
||
function_ref<void(SourceFileDepGraphNode *)> 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<SourceFileDepGraphNode>
|
||
findExistingNodePairOrCreateAndAddIfNew(
|
||
const DependencyKey &interfaceKey,
|
||
std::optional<Fingerprint> fingerprint);
|
||
|
||
NullablePtr<SourceFileDepGraphNode>
|
||
findExistingNode(const DependencyKey &key);
|
||
|
||
SourceFileDepGraphNode *
|
||
findExistingNodeOrCreateIfNew(const DependencyKey &key,
|
||
const std::optional<Fingerprint> 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<SourceFileDepGraph> static loadFromPath(
|
||
StringRef, bool allowSwiftModule = false);
|
||
|
||
/// Read a swiftdeps file from \p buffer and return a SourceFileDepGraph if
|
||
/// successful.
|
||
std::optional<SourceFileDepGraph> static loadFromBuffer(llvm::MemoryBuffer &);
|
||
std::optional<SourceFileDepGraph> 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 <typename GraphT> 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<std::string> 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
|