Skip to content

Commit 1b6baf6

Browse files
authored
Merge pull request swiftlang#34075 from CodaFi/crosstabs
Differentiate Cross-Module Incremental Dependencies
2 parents 5dc969f + 3ba33b9 commit 1b6baf6

15 files changed

+284
-7
lines changed

include/swift/AST/FineGrainedDependencies.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,10 @@ class BiIndexedTwoStageMap {
346346
// MARK: Start of fine-grained-dependency-specific code
347347
//==============================================================================
348348

349+
/// Uses the provided module or source file to construct a dependency graph,
350+
/// which is provided back to the caller in the continuation callback.
351+
///
352+
/// \Note The returned graph should not be escaped from the callback.
349353
bool withReferenceDependencies(
350354
llvm::PointerUnion<ModuleDecl *, SourceFile *> MSF,
351355
const DependencyTracker &depTracker, StringRef outputPath,

include/swift/AST/ModuleLoader.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ enum class IntermoduleDepTrackingMode {
7575
/// implemented in terms of a wrapped clang::DependencyCollector.
7676
class DependencyTracker {
7777
std::shared_ptr<clang::DependencyCollector> clangCollector;
78+
SmallVector<std::string, 8> incrementalDeps;
79+
llvm::StringSet<> incrementalDepsUniquer;
80+
7881
public:
7982
explicit DependencyTracker(
8083
IntermoduleDepTrackingMode Mode,
@@ -87,9 +90,19 @@ class DependencyTracker {
8790
/// No path canonicalization is done.
8891
void addDependency(StringRef File, bool IsSystem);
8992

93+
/// Adds a file as an incremental dependency.
94+
///
95+
/// No additional canonicalization or adulteration of the file path in
96+
/// \p File is performed.
97+
void addIncrementalDependency(StringRef File);
98+
9099
/// Fetches the list of dependencies.
91100
ArrayRef<std::string> getDependencies() const;
92101

102+
/// Fetches the list of dependencies that are known to have incremental swift
103+
/// dependency information embedded inside of them.
104+
ArrayRef<std::string> getIncrementalDependencies() const;
105+
93106
/// Return the underlying clang::DependencyCollector that this
94107
/// class wraps.
95108
std::shared_ptr<clang::DependencyCollector> getClangCollector();

include/swift/Basic/ReferenceDependencyKeys.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,18 @@ enum class NodeKind {
5353
dynamicLookup,
5454
externalDepend,
5555
sourceFileProvide,
56+
incrementalExternalDepend,
5657
/// For iterating through the NodeKinds.
5758
kindCount
5859
};
5960

6061
/// Used for printing out NodeKinds to dot files, and dumping nodes for
6162
/// debugging.
6263
const std::string NodeKindNames[]{
63-
"topLevel", "nominal", "potentialMember", "member",
64-
"dynamicLookup", "externalDepend", "sourceFileProvide"};
64+
"topLevel", "nominal",
65+
"potentialMember", "member",
66+
"dynamicLookup", "externalDepend",
67+
"sourceFileProvide", "incrementalExternalDepend"};
6568
} // end namespace fine_grained_dependencies
6669
} // end namespace swift
6770

include/swift/Basic/Statistics.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ FRONTEND_STATISTIC(AST, NumASTBytesAllocated)
114114
/// Number of file-level dependencies of this frontend job, as tracked in the
115115
/// AST context's dependency collector.
116116
FRONTEND_STATISTIC(AST, NumDependencies)
117+
FRONTEND_STATISTIC(AST, NumIncrementalDependencies)
117118

118119
/// Number of top-level, dynamic, and member names referenced in this frontend
119120
/// job's source file, as tracked by the AST context's referenced-name tracker.

include/swift/Driver/FineGrainedDependencyDriverGraph.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ class ModuleDepGraph {
168168

169169
// Supports requests from the driver to getExternalDependencies.
170170
std::unordered_set<std::string> externalDependencies;
171+
std::unordered_set<std::string> incrementalExternalDependencies;
171172

172173
/// Keyed by swiftdeps filename, so we can get back to Jobs.
173174
std::unordered_map<std::string, const driver::Job *> jobsBySwiftDeps;
@@ -512,15 +513,28 @@ class ModuleDepGraph {
512513
std::vector<const driver::Job *>
513514
findExternallyDependentUntracedJobs(StringRef externalDependency);
514515

516+
/// Find jobs that were previously not known to need compilation but that
517+
/// depend on \c incrementalExternalDependency.
518+
///
519+
/// This code path should only act as a fallback to the status-quo behavior.
520+
/// Otherwise it acts to pessimize the behavior of cross-module incremental
521+
/// builds.
522+
std::vector<const driver::Job *>
523+
findIncrementalExternallyDependentUntracedJobs(StringRef externalDependency);
524+
515525
//============================================================================
516526
// MARK: ModuleDepGraph - External dependencies
517527
//============================================================================
518528

519529
public:
520530
std::vector<StringRef> getExternalDependencies() const;
531+
std::vector<StringRef> getIncrementalExternalDependencies() const;
521532

522533
void forEachUntracedJobDirectlyDependentOnExternalSwiftDeps(
523534
StringRef externalDependency, function_ref<void(const driver::Job *)> fn);
535+
void forEachUntracedJobDirectlyDependentOnExternalIncrementalSwiftDeps(
536+
StringRef externalDependency, function_ref<void(const driver::Job *)> fn);
537+
524538
//============================================================================
525539
// MARK: ModuleDepGraph - verification
526540
//============================================================================

lib/AST/FineGrainedDependencies.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ std::string DependencyKey::humanReadableName() const {
231231
switch (kind) {
232232
case NodeKind::member:
233233
return demangleTypeAsContext(context) + "." + name;
234+
case NodeKind::incrementalExternalDepend:
234235
case NodeKind::externalDepend:
235236
case NodeKind::sourceFileProvide:
236237
return llvm::sys::path::filename(name).str();
@@ -261,9 +262,12 @@ raw_ostream &fine_grained_dependencies::operator<<(raw_ostream &out,
261262
bool DependencyKey::verify() const {
262263
assert((getKind() != NodeKind::externalDepend || isInterface()) &&
263264
"All external dependencies must be interfaces.");
265+
assert((getKind() != NodeKind::incrementalExternalDepend || isInterface()) &&
266+
"All incremental external dependencies must be interfaces.");
264267
switch (getKind()) {
265268
case NodeKind::topLevel:
266269
case NodeKind::dynamicLookup:
270+
case NodeKind::incrementalExternalDepend:
267271
case NodeKind::externalDepend:
268272
case NodeKind::sourceFileProvide:
269273
assert(context.empty() && !name.empty() && "Must only have a name");
@@ -294,6 +298,7 @@ void DependencyKey::verifyNodeKindNames() {
294298
CHECK_NAME(potentialMember)
295299
CHECK_NAME(member)
296300
CHECK_NAME(dynamicLookup)
301+
CHECK_NAME(incrementalExternalDepend)
297302
CHECK_NAME(externalDepend)
298303
CHECK_NAME(sourceFileProvide)
299304
case NodeKind::kindCount:

lib/AST/FrontendSourceFileDepGraphFactory.cpp

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,10 @@ bool fine_grained_dependencies::withReferenceDependencies(
241241
const DependencyTracker &depTracker, StringRef outputPath,
242242
bool alsoEmitDotFile,
243243
llvm::function_ref<bool(SourceFileDepGraph &&)> cont) {
244-
if (MSF.dyn_cast<ModuleDecl *>()) {
245-
llvm_unreachable("Cannot construct dependency graph for modules!");
244+
if (auto *MD = MSF.dyn_cast<ModuleDecl *>()) {
245+
SourceFileDepGraph g =
246+
ModuleDepGraphFactory(MD, alsoEmitDotFile).construct();
247+
return cont(std::move(g));
246248
} else {
247249
auto *SF = MSF.get<SourceFile *>();
248250
SourceFileDepGraph g = FrontendSourceFileDepGraphFactory(
@@ -592,6 +594,9 @@ class UsedDeclEnumerator {
592594
}
593595

594596
void enumerateExternalUses() {
597+
for (StringRef s : depTracker.getIncrementalDependencies())
598+
enumerateUse<NodeKind::incrementalExternalDepend>("", s);
599+
595600
for (StringRef s : depTracker.getDependencies())
596601
enumerateUse<NodeKind::externalDepend>("", s);
597602
}
@@ -632,3 +637,75 @@ FrontendSourceFileDepGraphFactory::getFingerprintIfAny(const Decl *d) {
632637
}
633638
return None;
634639
}
640+
641+
//==============================================================================
642+
// MARK: ModuleDepGraphFactory
643+
//==============================================================================
644+
645+
ModuleDepGraphFactory::ModuleDepGraphFactory(ModuleDecl *Mod, bool emitDot)
646+
: AbstractSourceFileDepGraphFactory(
647+
/*include private*/ true, Mod->getASTContext().hadError(),
648+
Mod->getNameStr(), "0xBADBEEF", emitDot, Mod->getASTContext().Diags),
649+
Mod(Mod) {}
650+
651+
void ModuleDepGraphFactory::addAllDefinedDecls() {
652+
// TODO: express the multiple provides and depends streams with variadic
653+
// templates
654+
655+
// Many kinds of Decls become top-level depends.
656+
657+
SmallVector<Decl *, 32> TopLevelDecls;
658+
Mod->getTopLevelDecls(TopLevelDecls);
659+
DeclFinder declFinder(TopLevelDecls, includePrivateDeps,
660+
[this](VisibleDeclConsumer &consumer) {
661+
return Mod->lookupClassMembers({}, consumer);
662+
});
663+
664+
addAllDefinedDeclsOfAGivenType<NodeKind::topLevel>(
665+
declFinder.precedenceGroups);
666+
addAllDefinedDeclsOfAGivenType<NodeKind::topLevel>(
667+
declFinder.memberOperatorDecls);
668+
addAllDefinedDeclsOfAGivenType<NodeKind::topLevel>(declFinder.operators);
669+
addAllDefinedDeclsOfAGivenType<NodeKind::topLevel>(declFinder.topNominals);
670+
addAllDefinedDeclsOfAGivenType<NodeKind::topLevel>(declFinder.topValues);
671+
addAllDefinedDeclsOfAGivenType<NodeKind::nominal>(declFinder.allNominals);
672+
addAllDefinedDeclsOfAGivenType<NodeKind::potentialMember>(
673+
declFinder.potentialMemberHolders);
674+
addAllDefinedDeclsOfAGivenType<NodeKind::member>(
675+
declFinder.valuesInExtensions);
676+
addAllDefinedDeclsOfAGivenType<NodeKind::dynamicLookup>(
677+
declFinder.classMembers);
678+
}
679+
680+
/// Given an array of Decls or pairs of them in \p declsOrPairs
681+
/// create node pairs for context and name
682+
template <NodeKind kind, typename ContentsT>
683+
void ModuleDepGraphFactory::addAllDefinedDeclsOfAGivenType(
684+
std::vector<ContentsT> &contentsVec) {
685+
for (const auto &declOrPair : contentsVec) {
686+
Optional<std::string> fp = getFingerprintIfAny(declOrPair);
687+
addADefinedDecl(
688+
DependencyKey::createForProvidedEntityInterface<kind>(declOrPair),
689+
fp ? StringRef(fp.getValue()) : Optional<StringRef>());
690+
}
691+
}
692+
693+
//==============================================================================
694+
// MARK: ModuleDepGraphFactory - adding individual defined Decls
695+
//==============================================================================
696+
697+
/// At present, only \c NominalTypeDecls have (body) fingerprints
698+
Optional<std::string> ModuleDepGraphFactory::getFingerprintIfAny(
699+
std::pair<const NominalTypeDecl *, const ValueDecl *>) {
700+
return None;
701+
}
702+
Optional<std::string>
703+
ModuleDepGraphFactory::getFingerprintIfAny(const Decl *d) {
704+
if (const auto *idc = dyn_cast<IterableDeclContext>(d)) {
705+
auto result = idc->getBodyFingerprint();
706+
assert((!result || !result->empty()) &&
707+
"Fingerprint should never be empty");
708+
return result;
709+
}
710+
return None;
711+
}

lib/AST/FrontendSourceFileDepGraphFactory.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,30 @@ class FrontendSourceFileDepGraphFactory
5454
static Optional<std::string> getFingerprintIfAny(const Decl *d);
5555
};
5656

57+
class ModuleDepGraphFactory : public AbstractSourceFileDepGraphFactory {
58+
ModuleDecl *const Mod;
59+
60+
public:
61+
ModuleDepGraphFactory(ModuleDecl *Mod, bool emitDot);
62+
63+
~ModuleDepGraphFactory() override = default;
64+
65+
private:
66+
void addAllDefinedDecls() override;
67+
void addAllUsedDecls() override {}
68+
69+
/// Given an array of Decls or pairs of them in \p declsOrPairs
70+
/// create node pairs for context and name
71+
template <NodeKind kind, typename ContentsT>
72+
void addAllDefinedDeclsOfAGivenType(std::vector<ContentsT> &contentsVec);
73+
74+
/// At present, only nominals, protocols, and extensions have (body)
75+
/// fingerprints
76+
static Optional<std::string> getFingerprintIfAny(
77+
std::pair<const NominalTypeDecl *, const ValueDecl *>);
78+
static Optional<std::string> getFingerprintIfAny(const Decl *d);
79+
};
80+
5781
} // namespace fine_grained_dependencies
5882
} // namespace swift
5983

lib/AST/ModuleLoader.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,22 @@ DependencyTracker::addDependency(StringRef File, bool IsSystem) {
5050
/*IsMissing=*/false);
5151
}
5252

53+
void DependencyTracker::addIncrementalDependency(StringRef File) {
54+
if (incrementalDepsUniquer.insert(File).second) {
55+
incrementalDeps.emplace_back(File.str());
56+
}
57+
}
58+
5359
ArrayRef<std::string>
5460
DependencyTracker::getDependencies() const {
5561
return clangCollector->getDependencies();
5662
}
5763

64+
ArrayRef<std::string>
65+
DependencyTracker::getIncrementalDependencies() const {
66+
return incrementalDeps;
67+
}
68+
5869
std::shared_ptr<clang::DependencyCollector>
5970
DependencyTracker::getClangCollector() {
6071
return clangCollector;

lib/Driver/Compilation.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,10 @@ namespace driver {
935935
for (const auto cmd :
936936
collectExternallyDependentJobsFromDependencyGraph(forRanges))
937937
jobsToSchedule.insert(cmd);
938+
for (const auto cmd :
939+
collectIncrementalExternallyDependentJobsFromDependencyGraph(
940+
forRanges))
941+
jobsToSchedule.insert(cmd);
938942
return jobsToSchedule;
939943
}
940944

@@ -1109,6 +1113,21 @@ namespace driver {
11091113
}
11101114
}
11111115

1116+
void forEachOutOfDateIncrementalExternalDependency(
1117+
const bool forRanges,
1118+
function_ref<void(StringRef)> consumeExternalSwiftDeps) {
1119+
for (StringRef dependency :
1120+
getIncrementalExternalDependencies(forRanges)) {
1121+
// If the dependency has been modified since the oldest built file,
1122+
// or if we can't stat it for some reason (perhaps it's been
1123+
// deleted?), trigger rebuilds through the dependency graph.
1124+
llvm::sys::fs::file_status depStatus;
1125+
if (llvm::sys::fs::status(dependency, depStatus) ||
1126+
Comp.getLastBuildTime() < depStatus.getLastModificationTime())
1127+
consumeExternalSwiftDeps(dependency);
1128+
}
1129+
}
1130+
11121131
CommandSet collectCascadedJobsFromDependencyGraph(
11131132
const CommandSet &InitialCascadingCommands, const bool forRanges) {
11141133
CommandSet CascadedJobs;
@@ -1141,6 +1160,25 @@ namespace driver {
11411160
return ExternallyDependentJobs;
11421161
}
11431162

1163+
SmallVector<const Job *, 16>
1164+
collectIncrementalExternallyDependentJobsFromDependencyGraph(
1165+
const bool forRanges) {
1166+
SmallVector<const Job *, 16> ExternallyDependentJobs;
1167+
// Check all cross-module dependencies as well.
1168+
forEachOutOfDateIncrementalExternalDependency(
1169+
forRanges, [&](StringRef dependency) {
1170+
// If the dependency has been modified since the oldest built file,
1171+
// or if we can't stat it for some reason (perhaps it's been
1172+
// deleted?), trigger rebuilds through the dependency graph.
1173+
for (const Job *marked :
1174+
markExternalInDepGraph(dependency, forRanges))
1175+
ExternallyDependentJobs.push_back(marked);
1176+
});
1177+
noteBuildingJobs(ExternallyDependentJobs, forRanges,
1178+
"because of external dependencies");
1179+
return ExternallyDependentJobs;
1180+
}
1181+
11441182
/// Insert all jobs in \p Cmds (of descriptive name \p Kind) to the \c
11451183
/// TaskQueue, and clear \p Cmds.
11461184
template <typename Container>
@@ -1594,6 +1632,12 @@ namespace driver {
15941632
return getFineGrainedDepGraph(forRanges).getExternalDependencies();
15951633
}
15961634

1635+
std::vector<StringRef>
1636+
getIncrementalExternalDependencies(const bool forRanges) const {
1637+
return getFineGrainedDepGraph(forRanges)
1638+
.getIncrementalExternalDependencies();
1639+
}
1640+
15971641
std::vector<const Job*>
15981642
markExternalInDepGraph(StringRef externalDependency,
15991643
const bool forRanges) {

0 commit comments

Comments
 (0)