Skip to content

Commit 15145cc

Browse files
authored
Merge pull request #35036 from CodaFi/acrosstic-poetry
Move The Last Pieces for Cross-Module Incremental Builds
2 parents cce0600 + 859b87f commit 15145cc

File tree

9 files changed

+175
-10
lines changed

9 files changed

+175
-10
lines changed

include/swift/Basic/FileTypes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ TYPE("llvm-bc", LLVM_BC, "bc", "")
6161
TYPE("diagnostics", SerializedDiagnostics, "dia", "")
6262
TYPE("objc-header", ObjCHeader, "h", "")
6363
TYPE("swift-dependencies", SwiftDeps, "swiftdeps", "")
64+
TYPE("external-swift-dependencies", ExternalSwiftDeps, "swiftdeps.external", "")
6465
TYPE("swift-ranges", SwiftRanges, "swiftranges", "")
6566
TYPE("compiled-source", CompiledSource, "compiledsource", "")
6667
TYPE("remap", Remapping, "remap", "")

include/swift/Basic/STLExtras.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <iterator>
2828
#include <numeric>
2929
#include <type_traits>
30+
#include <unordered_set>
3031

3132
namespace swift {
3233

@@ -752,6 +753,22 @@ using all_true =
752753
template <class... Ts>
753754
using are_all_compound = all_true<std::is_compound<Ts>::value...>;
754755

756+
/// Erase all elements in \p c that match the given predicate \p pred.
757+
// FIXME: Remove this when C++20 is the new baseline.
758+
template <class Key, class Hash, class KeyEqual, class Alloc, class Pred>
759+
typename std::unordered_set<Key, Hash, KeyEqual, Alloc>::size_type
760+
erase_if(std::unordered_set<Key, Hash, KeyEqual, Alloc> &c, Pred pred) {
761+
auto startingSize = c.size();
762+
for (auto i = c.begin(), last = c.end(); i != last;) {
763+
if (pred(*i)) {
764+
i = c.erase(i);
765+
} else {
766+
++i;
767+
}
768+
}
769+
return startingSize - c.size();
770+
}
771+
755772
} // end namespace swift
756773

757774
#endif // SWIFT_BASIC_INTERLEAVE_H

include/swift/Driver/Compilation.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,17 @@ class Compilation {
508508
/// \sa types::isPartOfSwiftCompilation
509509
const char *getAllSourcesPath() const;
510510

511+
/// Retrieve the path to the external swift deps file.
512+
///
513+
/// For cross-module incremental builds, this file contains the dependencies
514+
/// from all the modules integrated over the prior build.
515+
///
516+
/// Currently this patch is relative to the build record, but we may want
517+
/// to allow the output file map to customize this at some point.
518+
std::string getExternalSwiftDepsFilePath() const {
519+
return CompilationRecordPath + ".external";
520+
}
521+
511522
/// Asks the Compilation to perform the Jobs which it knows about.
512523
///
513524
/// \param TQ The TaskQueue used to schedule jobs for execution.

lib/AST/FrontendSourceFileDepGraphFactory.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,4 @@ void ModuleDepGraphFactory::addAllDefinedDecls() {
538538
declFinder.potentialMemberHolders);
539539
addAllDefinedDeclsOfAGivenType<NodeKind::member>(
540540
declFinder.valuesInExtensions);
541-
addAllDefinedDeclsOfAGivenType<NodeKind::dynamicLookup>(
542-
declFinder.classMembers);
543541
}

lib/Basic/FileTypes.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ bool file_types::isTextual(ID Id) {
100100
case file_types::TY_SerializedDiagnostics:
101101
case file_types::TY_ClangModuleFile:
102102
case file_types::TY_SwiftDeps:
103+
case file_types::TY_ExternalSwiftDeps:
103104
case file_types::TY_SwiftRanges:
104105
case file_types::TY_CompiledSource:
105106
case file_types::TY_Nothing:
@@ -145,6 +146,7 @@ bool file_types::isAfterLLVM(ID Id) {
145146
case file_types::TY_SerializedDiagnostics:
146147
case file_types::TY_ClangModuleFile:
147148
case file_types::TY_SwiftDeps:
149+
case file_types::TY_ExternalSwiftDeps:
148150
case file_types::TY_SwiftRanges:
149151
case file_types::TY_CompiledSource:
150152
case file_types::TY_Nothing:
@@ -197,6 +199,7 @@ bool file_types::isPartOfSwiftCompilation(ID Id) {
197199
case file_types::TY_SerializedDiagnostics:
198200
case file_types::TY_ClangModuleFile:
199201
case file_types::TY_SwiftDeps:
202+
case file_types::TY_ExternalSwiftDeps:
200203
case file_types::TY_SwiftRanges:
201204
case file_types::TY_CompiledSource:
202205
case file_types::TY_Nothing:

lib/Driver/Compilation.cpp

Lines changed: 123 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "swift/AST/DiagnosticEngine.h"
1616
#include "swift/AST/DiagnosticsDriver.h"
1717
#include "swift/AST/FineGrainedDependencies.h"
18+
#include "swift/AST/FineGrainedDependencyFormat.h"
1819
#include "swift/Basic/OutputFileMap.h"
1920
#include "swift/Basic/Program.h"
2021
#include "swift/Basic/STLExtras.h"
@@ -1185,6 +1186,30 @@ namespace driver {
11851186
return ExternallyDependentJobs;
11861187
}
11871188

1189+
using ChangeSet = fine_grained_dependencies::ModuleDepGraph::Changes::value_type;
1190+
static void
1191+
pruneChangeSetFromExternalDependency(ChangeSet &changes) {
1192+
// The changeset includes detritus from the graph that gets consed up
1193+
// in \c writePriorDependencyGraph. We need to ignore the fake
1194+
// source file provides nodes and the fake incremental external
1195+
// dependencies linked to them.
1196+
swift::erase_if(
1197+
changes, [&](fine_grained_dependencies::ModuleDepGraphNode *node) {
1198+
if (node->getKey().getKind() ==
1199+
fine_grained_dependencies::NodeKind::sourceFileProvide ||
1200+
node->getKey().getKind() ==
1201+
fine_grained_dependencies::NodeKind::
1202+
incrementalExternalDepend) {
1203+
return true;
1204+
}
1205+
if (node->getKey().getAspect() ==
1206+
fine_grained_dependencies::DeclAspect::implementation) {
1207+
return true;
1208+
}
1209+
return !node->getIsProvides();
1210+
});
1211+
}
1212+
11881213
SmallVector<const Job *, 16>
11891214
collectIncrementalExternallyDependentJobsFromDependencyGraph(
11901215
const bool forRanges) {
@@ -1196,6 +1221,17 @@ namespace driver {
11961221
}
11971222
};
11981223

1224+
// Load our priors, which are always adjacent to the build record. We
1225+
// don't care if this load succeeds or not. If it fails, and we succeed at
1226+
// integrating one of the external files below, the changeset will be the
1227+
// entire module!
1228+
const auto *externalPriorJob = Comp.addExternalJob(
1229+
std::make_unique<Job>(Comp.getDerivedOutputFileMap(),
1230+
Comp.getExternalSwiftDepsFilePath()));
1231+
getFineGrainedDepGraph(forRanges).loadFromPath(
1232+
externalPriorJob, Comp.getExternalSwiftDepsFilePath(),
1233+
Comp.getDiags());
1234+
11991235
for (auto external : getFineGrainedDepGraph(forRanges)
12001236
.getIncrementalExternalDependencies()) {
12011237
llvm::sys::fs::file_status depStatus;
@@ -1231,20 +1267,23 @@ namespace driver {
12311267
// code's internal invariants.
12321268
const auto *externalJob = Comp.addExternalJob(
12331269
std::make_unique<Job>(Comp.getDerivedOutputFileMap(), external));
1234-
auto subChanges =
1270+
auto maybeChanges =
12351271
getFineGrainedDepGraph(forRanges).loadFromSwiftModuleBuffer(
12361272
externalJob, *buffer.get(), Comp.getDiags());
12371273

12381274
// If the incremental dependency graph failed to load, fall back to
12391275
// treating this as plain external job.
1240-
if (!subChanges.hasValue()) {
1276+
if (!maybeChanges.hasValue()) {
12411277
fallbackToExternalBehavior(external);
12421278
continue;
12431279
}
12441280

1245-
for (auto *CMD :
1246-
getFineGrainedDepGraph(forRanges)
1247-
.findJobsToRecompileWhenNodesChange(subChanges.getValue())) {
1281+
// Prune away the detritus from the build record.
1282+
auto &changes = maybeChanges.getValue();
1283+
pruneChangeSetFromExternalDependency(changes);
1284+
1285+
for (auto *CMD : getFineGrainedDepGraph(forRanges)
1286+
.findJobsToRecompileWhenNodesChange(changes)) {
12481287
if (CMD == externalJob) {
12491288
continue;
12501289
}
@@ -1861,6 +1900,76 @@ static void writeCompilationRecord(StringRef path, StringRef argsHash,
18611900
}
18621901
}
18631902

1903+
using SourceFileDepGraph = swift::fine_grained_dependencies::SourceFileDepGraph;
1904+
1905+
/// Render out the unified module dependency graph to the given \p path, which
1906+
/// is expected to be a path relative to the build record.
1907+
static void withPriorDependencyGraph(StringRef path,
1908+
const Compilation::Result &result,
1909+
llvm::function_ref<void(SourceFileDepGraph &&)> cont) {
1910+
// Building a source file dependency graph from the module dependency graph
1911+
// is a strange task on its face because a source file dependency graph is
1912+
// usually built for exactly one file. However, the driver is going to use
1913+
// some encoding tricks to get the dependencies for each incremental external
1914+
// dependency into one big file. Note that these tricks
1915+
// are undone in \c pruneChangeSetFromExternalDependency, so if you modify
1916+
// this you need to go fix that algorithm up as well. This is a diagrammatic
1917+
// view of the structure of the dependencies this function builds:
1918+
//
1919+
// SourceFile => interface <BUILD_RECORD>.external
1920+
// | - Incremetal External Dependency => interface <MODULE_1>.swiftmodule
1921+
// | | - <dependency> ...
1922+
// | | - <dependency> ...
1923+
// | | - <dependency> ...
1924+
// | - Incremetal External Dependency => interface <MODULE_2>.swiftmodule
1925+
// | | - <dependency> ...
1926+
// | | - <dependency> ...
1927+
// | - Incremetal External Dependency => interface <MODULE_3>.swiftmodule
1928+
// | - ...
1929+
//
1930+
// Where each <dependency> node has an arc back to its parent swiftmodule.
1931+
// That swiftmodule, in turn, takes the form of as an incremental external
1932+
// dependency. This formulation allows us to easily discern the original
1933+
// swiftmodule that a <dependency> came from just by examining that arc. This
1934+
// is used in integrate to "move" the <dependency> from the build record to
1935+
// the swiftmodule by swapping the key it uses.
1936+
using namespace swift::fine_grained_dependencies;
1937+
SourceFileDepGraph g;
1938+
const auto &resultModuleGraph = result.depGraph;
1939+
// Create the key for the entire external build record.
1940+
auto fileKey =
1941+
DependencyKey::createKeyForWholeSourceFile(DeclAspect::interface, path);
1942+
auto fileNodePair = g.findExistingNodePairOrCreateAndAddIfNew(fileKey, None);
1943+
for (StringRef incrExternalDep :
1944+
resultModuleGraph.getIncrementalExternalDependencies()) {
1945+
// Now make a node for each incremental external dependency.
1946+
auto interfaceKey =
1947+
DependencyKey(NodeKind::incrementalExternalDepend,
1948+
DeclAspect::interface, "", incrExternalDep.str());
1949+
auto ifaceNode = g.findExistingNodeOrCreateIfNew(interfaceKey, None,
1950+
false /* = !isProvides */);
1951+
resultModuleGraph.forEachNodeInJob(incrExternalDep, [&](const auto *node) {
1952+
// Reject
1953+
// 1) Implementation nodes: We don't care about the interface nodes
1954+
// for cross-module dependencies because the client cannot observe it
1955+
// by definition.
1956+
// 2) Source file nodes: we're about to define our own.
1957+
if (!node->getKey().isInterface() ||
1958+
node->getKey().getKind() == NodeKind::sourceFileProvide) {
1959+
return;
1960+
}
1961+
assert(node->getIsProvides() &&
1962+
"Found a node in module depdendencies that is not a provides!");
1963+
auto *newNode = new SourceFileDepGraphNode(
1964+
node->getKey(), node->getFingerprint(), /*isProvides*/ true);
1965+
g.addNode(newNode);
1966+
g.addArc(ifaceNode, newNode);
1967+
});
1968+
g.addArc(fileNodePair.getInterface(), ifaceNode);
1969+
}
1970+
return cont(std::move(g));
1971+
}
1972+
18641973
static void writeInputJobsToFilelist(llvm::raw_fd_ostream &out, const Job *job,
18651974
const file_types::ID infoType) {
18661975
// FIXME: Duplicated from ToolChains.cpp.
@@ -1960,6 +2069,15 @@ Compilation::performJobsImpl(std::unique_ptr<TaskQueue> &&TQ) {
19602069
auto result = std::move(State).takeResult();
19612070
writeCompilationRecord(CompilationRecordPath, ArgsHash, BuildStartTime,
19622071
InputInfo);
2072+
if (EnableCrossModuleIncrementalBuild) {
2073+
// Write out our priors adjacent to the build record so we can pick
2074+
// the up in a subsequent build.
2075+
withPriorDependencyGraph(getExternalSwiftDepsFilePath(), result,
2076+
[&](SourceFileDepGraph &&g) {
2077+
writeFineGrainedDependencyGraphToPath(
2078+
Diags, getExternalSwiftDepsFilePath(), g);
2079+
});
2080+
}
19632081
return result;
19642082
} else {
19652083
return std::move(State).takeResult();

lib/Driver/Driver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2039,6 +2039,7 @@ void Driver::buildActions(SmallVectorImpl<const Action *> &TopLevelActions,
20392039
case file_types::TY_ObjCHeader:
20402040
case file_types::TY_ClangModuleFile:
20412041
case file_types::TY_SwiftDeps:
2042+
case file_types::TY_ExternalSwiftDeps:
20422043
case file_types::TY_SwiftRanges:
20432044
case file_types::TY_CompiledSource:
20442045
case file_types::TY_Remapping:

lib/Driver/FineGrainedDependencyDriverGraph.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,15 +287,29 @@ ModuleDepGraph::Changes ModuleDepGraph::integrate(const SourceFileDepGraph &g,
287287

288288
g.forEachNode([&](const SourceFileDepGraphNode *integrand) {
289289
const auto &key = integrand->getKey();
290-
291-
auto preexistingMatch = findPreexistingMatch(swiftDepsOfJob, integrand);
290+
StringRef realSwiftDepsPath = swiftDepsOfJob;
291+
// If we're doing a cross-module incremental build, we'll see these
292+
// `.external` "swiftdeps" files. See \c writePriorDependencyGraph for
293+
// the structure of the graph we're traversing here. Essentially, follow
294+
// the arc laid down there to discover the file path for the swiftmodule
295+
// where this dependency node originally came from.
296+
if (swiftDepsOfJob.endswith(file_types::getExtension(file_types::TY_ExternalSwiftDeps)) &&
297+
integrand->getKey().getKind() != NodeKind::sourceFileProvide) {
298+
integrand->forEachDefIDependUpon([&](size_t seqNum) {
299+
auto &external = g.getNode(seqNum)->getKey();
300+
if (external.getKind() == NodeKind::incrementalExternalDepend) {
301+
realSwiftDepsPath = external.getName();
302+
}
303+
});
304+
}
305+
auto preexistingMatch = findPreexistingMatch(realSwiftDepsPath, integrand);
292306
if (preexistingMatch.hasValue() &&
293307
preexistingMatch.getValue().first == LocationOfPreexistingNode::here)
294308
disappearedNodes.erase(key); // Node was and still is. Do not erase it.
295309

296310
Optional<NullablePtr<ModuleDepGraphNode>> newNodeOrChangedNode =
297311
integrateSourceFileDepGraphNode(g, integrand, preexistingMatch,
298-
swiftDepsOfJob);
312+
realSwiftDepsPath);
299313

300314
if (!newNodeOrChangedNode)
301315
changedNodes = None;

lib/Driver/ToolChains.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const {
619619
case file_types::TY_ObjCHeader:
620620
case file_types::TY_Image:
621621
case file_types::TY_SwiftDeps:
622+
case file_types::TY_ExternalSwiftDeps:
622623
case file_types::TY_SwiftRanges:
623624
case file_types::TY_CompiledSource:
624625
case file_types::TY_ModuleTrace:
@@ -881,6 +882,7 @@ ToolChain::constructInvocation(const BackendJobAction &job,
881882
case file_types::TY_ObjCHeader:
882883
case file_types::TY_Image:
883884
case file_types::TY_SwiftDeps:
885+
case file_types::TY_ExternalSwiftDeps:
884886
case file_types::TY_SwiftRanges:
885887
case file_types::TY_CompiledSource:
886888
case file_types::TY_Remapping:

0 commit comments

Comments
 (0)