Skip to content

Commit 0901d79

Browse files
author
David Ungar
committed
Use wrapper types to ensure direct/transitive handled correctly.
1 parent 52feebc commit 0901d79

File tree

7 files changed

+183
-141
lines changed

7 files changed

+183
-141
lines changed

Sources/SwiftDriver/IncrementalCompilation/DirectAndTransitiveCollections.swift

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,34 +13,74 @@
1313
// Use the type system to ensure that dependencies are transitively closed
1414
// without doing too much work at the leaves of the call tree
1515

16-
public struct DirectlyInvalidatedNodes {
17-
typealias Node = ModuleDependencyGraph.Node
18-
var contents = Set<Node>()
16+
public struct Transitively {}
17+
public struct Directly {}
1918

20-
init(_ s: Set<Node> = Set()) {
19+
public struct InvalidatedSet<ClosureLevel, Element: Hashable>: Sequence {
20+
var contents: Set<Element>
21+
22+
init(_ s: Set<Element> = Set()) {
2123
self.contents = s
2224
}
23-
24-
init<Nodes: Sequence>(_ nodes: Nodes)
25-
where Nodes.Element == Node
25+
init<Elements: Sequence>(_ elements: Elements)
26+
where Elements.Element == Element
2627
{
27-
self.init(Set(nodes))
28+
self.init(Set(elements))
2829
}
29-
30-
mutating func insert(_ e: Node) {
30+
mutating func insert(_ e: Element) {
3131
contents.insert(e)
3232
}
33-
mutating func formUnion(_ nodes: DirectlyInvalidatedNodes) {
34-
contents.formUnion(nodes.contents)
33+
mutating func formUnion<Elements: Sequence>(_ elements: Elements)
34+
where Elements.Element == Element{
35+
contents.formUnion(elements)
36+
}
37+
public func makeIterator() -> Set<Element>.Iterator {
38+
contents.makeIterator()
39+
}
40+
public func map<R>(_ transform: (Element) -> R) -> InvalidatedArray<ClosureLevel, R> {
41+
InvalidatedArray(contents.map(transform))
42+
}
43+
public func compactMap<R>(_ transform: (Element) -> R? ) -> InvalidatedArray<ClosureLevel, R> {
44+
InvalidatedArray(contents.compactMap(transform))
45+
}
46+
}
47+
48+
extension InvalidatedSet where Element: Comparable {
49+
func sorted() -> InvalidatedArray<ClosureLevel, Element> {
50+
InvalidatedArray(contents.sorted())
51+
}
52+
}
53+
54+
public struct InvalidatedArray<ClosureLevel, Element>: Sequence {
55+
var contents: [Element]
56+
57+
init(_ s: [Element] = []) {
58+
self.contents = s
59+
}
60+
init<Elements: Sequence>(_ elements: Elements)
61+
where Elements.Element == Element
62+
{
63+
self.init(Array(elements))
64+
}
65+
public func makeIterator() -> Array<Element>.Iterator {
66+
contents.makeIterator()
67+
}
68+
public mutating func append(_ e: Element) {
69+
contents.append(e)
70+
}
71+
public func reduce<R: Hashable>(
72+
into initialResult: InvalidatedSet<ClosureLevel, R>,
73+
_ updateAccumulatingResult: (inout Set<R>, Element) -> ()
74+
) -> InvalidatedSet<ClosureLevel, R> {
75+
InvalidatedSet(
76+
contents.reduce(into: initialResult.contents, updateAccumulatingResult))
3577
}
78+
public var count: Int { contents.count }
3679
}
3780

38-
//extension Sequence {
39-
// func reduce(into initialResult: DirectlyInvalidatedNodes,
40-
// _ updateAccumulatingResult: (inout: DirectlyInvalidatedNodes, Element) -> Void) {
41-
// var r = initialResult
42-
// reduce(into: r.contents, updateAccumulatingResult)
43-
// return r
44-
// }
45-
// }
46-
//}
81+
public typealias TransitivelyInvalidatedNodeArray = InvalidatedArray<Transitively, ModuleDependencyGraph.Node>
82+
public typealias TransitivelyInvalidatedSourceSet = InvalidatedSet<Transitively, DependencySource>
83+
public typealias TransitivelyInvalidatedInputArray = InvalidatedArray<Transitively, TypedVirtualPath>
84+
public typealias TransitivelyInvalidatedInputSet = InvalidatedSet<Transitively, TypedVirtualPath>
85+
public typealias DirectlyInvalidatedNodeArray = InvalidatedArray<Directly, ModuleDependencyGraph.Node>
86+
public typealias DirectlyInvalidatedNodeSet = InvalidatedSet<Directly, ModuleDependencyGraph.Node>

Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,13 @@ extension IncrementalCompilationState {
249249
}
250250

251251
private func collectInputsInvalidated(byCompiling input: TypedVirtualPath)
252-
-> Set<TypedVirtualPath> {
252+
-> TransitivelyInvalidatedInputSet {
253253
if let found = moduleDependencyGraph.collectInputsRequiringCompilation(byCompiling: input) {
254254
return found
255255
}
256256
self.reporter?.report(
257257
"Failed to read some dependencies source; compiling everything", input)
258-
return Set<TypedVirtualPath>(skippedCompileGroups.keys)
258+
return TransitivelyInvalidatedInputSet(skippedCompileGroups.keys)
259259
}
260260

261261
/// Find the jobs that now must be run that were not originally known to be needed.

Sources/SwiftDriver/IncrementalCompilation/InitialStateComputer.swift

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ extension IncrementalCompilationState.InitialStateComputer {
116116
/// For inputs with swiftDeps in OFM, but no readable file, puts input in graph map, but no nodes in graph:
117117
/// caller must ensure scheduling of those
118118
private func computeGraphAndInputsInvalidatedByExternals()
119-
-> (ModuleDependencyGraph, Set<TypedVirtualPath>)? {
119+
-> (ModuleDependencyGraph, TransitivelyInvalidatedInputSet)? {
120120
precondition(sourceFiles.disappeared.isEmpty,
121121
"Would have to remove nodes from the graph if reading prior")
122122
if readPriorsFromModuleDependencyGraph {
@@ -128,7 +128,7 @@ extension IncrementalCompilationState.InitialStateComputer {
128128
}
129129

130130
private func readPriorGraphAndCollectInputsInvalidatedByChangedOrAddedExternals(
131-
) -> (ModuleDependencyGraph, Set<TypedVirtualPath>)?
131+
) -> (ModuleDependencyGraph, TransitivelyInvalidatedInputSet)?
132132
{
133133
let dependencyGraphPath = buildRecordInfo.dependencyGraphPath
134134
let graphIfPresent: ModuleDependencyGraph?
@@ -154,9 +154,9 @@ extension IncrementalCompilationState.InitialStateComputer {
154154
// recompilation. Thus, `ChangedOrAdded`.
155155
let nodesDirectlyInvalidatedByExternals = graph.collectNodesInvalidatedByChangedOrAddedExternals()
156156
// Wait till the last minute to do the transitive closure as an optimization.
157-
let inputsTransitivelyInvalidatedByExternals = graph.collectInputsUsingTransitivelyInvalidated(
157+
let inputsInvalidatedByExternals = graph.collectInputsUsingInvalidated(
158158
nodes: nodesDirectlyInvalidatedByExternals)
159-
return (graph, inputsTransitivelyInvalidatedByExternals)
159+
return (graph, inputsInvalidatedByExternals)
160160
}
161161

162162
/// Builds a graph
@@ -167,15 +167,15 @@ extension IncrementalCompilationState.InitialStateComputer {
167167
/// For externalDependencies, puts then in graph.fingerprintedExternalDependencies, but otherwise
168168
/// does nothing special.
169169
private func buildInitialGraphFromSwiftDepsAndCollectInputsInvalidatedByChangedExternals(
170-
) -> (ModuleDependencyGraph, Set<TypedVirtualPath>)?
170+
) -> (ModuleDependencyGraph, TransitivelyInvalidatedInputSet)?
171171
{
172172
let graph = ModuleDependencyGraph(self, .buildingWithoutAPrior)
173173
assert(outputFileMap.onlySourceFilesHaveSwiftDeps())
174174
guard graph.populateInputDependencySourceMap() else {
175175
return nil
176176
}
177177

178-
var inputsInvalidatedByChangedExternals = Set<TypedVirtualPath>()
178+
var inputsInvalidatedByChangedExternals = TransitivelyInvalidatedInputSet()
179179
for input in sourceFiles.currentInOrder {
180180
guard let invalidatedInputs = graph.collectInputsRequiringCompilationFromExternalsFoundByCompiling(input: input)
181181
else {
@@ -195,7 +195,7 @@ extension IncrementalCompilationState.InitialStateComputer {
195195
/// listed in fingerprintExternalDependencies.
196196
private func computeInputsAndGroups(
197197
_ moduleDependencyGraph: ModuleDependencyGraph,
198-
_ inputsInvalidatedByExternals: Set<TypedVirtualPath>,
198+
_ inputsInvalidatedByExternals: TransitivelyInvalidatedInputSet,
199199
batchJobFormer: inout Driver
200200
) throws -> (skippedCompileGroups: [TypedVirtualPath: CompileJobGroup],
201201
mandatoryJobsInOrder: [Job])
@@ -249,7 +249,7 @@ extension IncrementalCompilationState.InitialStateComputer {
249249

250250
/// Figure out which compilation inputs are *not* mandatory
251251
private func computeSkippedCompilationInputs(
252-
inputsInvalidatedByExternals: Set<TypedVirtualPath>,
252+
inputsInvalidatedByExternals: TransitivelyInvalidatedInputSet,
253253
_ moduleDependencyGraph: ModuleDependencyGraph,
254254
_ buildRecord: BuildRecord
255255
) -> Set<TypedVirtualPath> {
@@ -285,7 +285,7 @@ extension IncrementalCompilationState.InitialStateComputer {
285285
// as each first wave job finished.
286286
let speculativeInputs = collectInputsToBeSpeculativelyRecompiled(
287287
changedInputs: changedInputs,
288-
externalDependents: Array(inputsInvalidatedByExternals),
288+
externalDependents: inputsInvalidatedByExternals,
289289
inputsMissingOutputs: Set(inputsMissingOutputs),
290290
moduleDependencyGraph)
291291
.subtracting(definitelyRequiredInputs)
@@ -371,28 +371,28 @@ extension IncrementalCompilationState.InitialStateComputer {
371371
/// before the whole frontend job finished.
372372
private func collectInputsToBeSpeculativelyRecompiled(
373373
changedInputs: [ChangedInput],
374-
externalDependents: [TypedVirtualPath],
374+
externalDependents: TransitivelyInvalidatedInputSet,
375375
inputsMissingOutputs: Set<TypedVirtualPath>,
376376
_ moduleDependencyGraph: ModuleDependencyGraph
377377
) -> Set<TypedVirtualPath> {
378378
let cascadingChangedInputs = computeCascadingChangedInputs(
379379
from: changedInputs,
380380
inputsMissingOutputs: inputsMissingOutputs)
381-
let cascadingExternalDependents = alwaysRebuildDependents ? externalDependents : []
382-
// Collect the dependent files to speculatively schedule
383-
var dependentFiles = Set<TypedVirtualPath>()
384-
let cascadingFileSet = Set(cascadingChangedInputs).union(cascadingExternalDependents)
385-
for cascadingFile in cascadingFileSet {
386-
let dependentsOfOneFile = moduleDependencyGraph
387-
.collectInputsTransitivelyInvalidatedBy(input: cascadingFile)
388-
for dep in dependentsOfOneFile where !cascadingFileSet.contains(dep) {
389-
if dependentFiles.insert(dep).0 {
381+
382+
var inputsToBeCertainlyRecompiled = alwaysRebuildDependents ? externalDependents : TransitivelyInvalidatedInputSet()
383+
inputsToBeCertainlyRecompiled.formUnion(cascadingChangedInputs)
384+
385+
return inputsToBeCertainlyRecompiled.reduce(into: Set()) {
386+
speculativelyRecompiledInputs, certainlyRecompiledInput in
387+
let speculativeDependents = moduleDependencyGraph.collectInputsInvalidatedBy(input: certainlyRecompiledInput)
388+
for speculativeDependent in speculativeDependents
389+
where !inputsToBeCertainlyRecompiled.contains(speculativeDependent) {
390+
if speculativelyRecompiledInputs.insert(speculativeDependent).inserted {
390391
reporter?.report(
391-
"Immediately scheduling dependent on \(cascadingFile.file.basename)", dep)
392+
"Immediately scheduling dependent on \(certainlyRecompiledInput.file.basename)", speculativeDependent)
392393
}
393394
}
394395
}
395-
return dependentFiles
396396
}
397397

398398
// Collect the files that will be compiled whose dependents should be schedule

0 commit comments

Comments
 (0)