Skip to content

Commit 4df7faa

Browse files
author
David Ungar
authored
Merge pull request #685 from davidungar/again3
[NFC, Incremental] Refactoring to facilitate replacing the inputDependencySourceMap with the right thing
2 parents cdd18c5 + 2cedf21 commit 4df7faa

File tree

5 files changed

+133
-77
lines changed

5 files changed

+133
-77
lines changed

Sources/SwiftDriver/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ add_library(SwiftDriver
3333
Execution/ParsableOutput.swift
3434
Execution/ProcessProtocol.swift
3535

36+
"IncrementalCompilation/ModuleDependencyGraphParts/InputDependencySourceMap.swift"
3637
"IncrementalCompilation/ModuleDependencyGraphParts/Integrator.swift"
3738
"IncrementalCompilation/ModuleDependencyGraphParts/Node.swift"
3839
"IncrementalCompilation/ModuleDependencyGraphParts/NodeFinder.swift"

Sources/SwiftDriver/IncrementalCompilation/IncrementalDependencyAndInputSetup.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
226226
else {
227227
return buildInitialGraphFromSwiftDepsAndCollectInputsInvalidatedByChangedExternals()
228228
}
229-
guard graph.populateInputDependencySourceMap() else {
229+
guard graph.populateInputDependencySourceMap(for: .inputsAddedSincePriors) else {
230230
return nil
231231
}
232232
graph.dotFileWriter?.write(graph)
@@ -256,7 +256,8 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
256256
{
257257
let graph = ModuleDependencyGraph(self, .buildingWithoutAPrior)
258258
assert(outputFileMap.onlySourceFilesHaveSwiftDeps())
259-
guard graph.populateInputDependencySourceMap() else {
259+
260+
guard graph.populateInputDependencySourceMap(for: .buildingFromSwiftDeps) else {
260261
return nil
261262
}
262263

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift

Lines changed: 63 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,7 @@ import SwiftOptions
2424
@_spi(Testing) public var nodeFinder = NodeFinder()
2525

2626
/// Maps input files (e.g. .swift) to and from the DependencySource object.
27-
///
28-
// FIXME: The map between swiftdeps and swift files is absolutely *not*
29-
// a bijection. In particular, more than one swiftdeps file can be encountered
30-
// in the course of deserializing priors *and* reading the output file map
31-
// *and* re-reading swiftdeps files after frontends complete
32-
// that correspond to the same swift file. These cause two problems:
33-
// - overwrites in this data structure that lose data and
34-
// - cache misses in `getInput(for:)` that cause the incremental build to
35-
// turn over when e.g. entries in the output file map change. This should be
36-
// replaced by a multi-map from swift files to dependency sources,
37-
// and a regular map from dependency sources to swift files -
38-
// since that direction really is one-to-one.
39-
@_spi(Testing) public private(set) var inputDependencySourceMap = BidirectionalMap<TypedVirtualPath, DependencySource>()
27+
@_spi(Testing) public private(set) var inputDependencySourceMap = InputDependencySourceMap()
4028

4129
// The set of paths to external dependencies known to be in the graph
4230
public internal(set) var fingerprintedExternalDependencies = Set<FingerprintedExternalDependency>()
@@ -66,27 +54,16 @@ import SwiftOptions
6654
self.creationPhase = phase
6755
}
6856

69-
private func addMapEntry(_ input: TypedVirtualPath, _ dependencySource: DependencySource) {
70-
assert(input.type == .swift && dependencySource.typedFile.type == .swiftDeps)
71-
inputDependencySourceMap[input] = dependencySource
72-
}
73-
74-
@_spi(Testing) public func getSource(for input: TypedVirtualPath,
75-
function: String = #function,
76-
file: String = #file,
77-
line: Int = #line) -> DependencySource {
78-
guard let source = inputDependencySourceMap[input] else {
57+
@_spi(Testing) public func sourceRequired(for input: TypedVirtualPath,
58+
function: String = #function,
59+
file: String = #file,
60+
line: Int = #line) -> DependencySource {
61+
guard let source = inputDependencySourceMap.sourceIfKnown(for: input)
62+
else {
7963
fatalError("\(input.file.basename) not found in inputDependencySourceMap, \(file):\(line) in \(function)")
8064
}
8165
return source
8266
}
83-
@_spi(Testing) public func getInput(for source: DependencySource) -> TypedVirtualPath? {
84-
guard let input = inputDependencySourceMap[source] else {
85-
info.diagnosticEngine.emit(warning: "Failed to find source file for '\(source.file.basename)', recovering with a full rebuild. Next build will be incremental.")
86-
return nil
87-
}
88-
return input
89-
}
9067
}
9168

9269
extension ModuleDependencyGraph {
@@ -155,7 +132,7 @@ extension ModuleDependencyGraph {
155132
return TransitivelyInvalidatedInputSet()
156133
}
157134
return collectInputsRequiringCompilationAfterProcessing(
158-
dependencySource: getSource(for: input))
135+
dependencySource: sourceRequired(for: input))
159136
}
160137
}
161138

@@ -175,17 +152,16 @@ extension ModuleDependencyGraph {
175152
/// speculatively scheduled in the first wave.
176153
func collectInputsInvalidatedBy(input: TypedVirtualPath
177154
) -> TransitivelyInvalidatedInputArray {
178-
let changedSource = getSource(for: input)
155+
let changedSource = sourceRequired(for: input)
179156
let allDependencySourcesToRecompile =
180157
collectSwiftDepsUsing(dependencySource: changedSource)
181158

182159
return allDependencySourcesToRecompile.compactMap {
183160
dependencySource in
184161
guard dependencySource != changedSource else {return nil}
185-
let dependentSource = inputDependencySourceMap[dependencySource]
186-
info.reporter?.report(
187-
"Found dependent of \(input.file.basename):", dependentSource)
188-
return dependentSource
162+
let inputToRecompile = inputDependencySourceMap.inputIfKnown(for: dependencySource)
163+
info.reporter?.report("Found dependent of \(input.file.basename):", inputToRecompile)
164+
return inputToRecompile
189165
}
190166
}
191167

@@ -202,7 +178,7 @@ extension ModuleDependencyGraph {
202178
/// Does the graph contain any dependency nodes for a given source-code file?
203179
func containsNodes(forSourceFile file: TypedVirtualPath) -> Bool {
204180
precondition(file.type == .swift)
205-
guard let source = inputDependencySourceMap[file] else {
181+
guard let source = inputDependencySourceMap.sourceIfKnown(for: file) else {
206182
return false
207183
}
208184
return containsNodes(forDependencySource: source)
@@ -212,17 +188,46 @@ extension ModuleDependencyGraph {
212188
return nodeFinder.findNodes(for: source).map {!$0.isEmpty}
213189
?? false
214190
}
215-
216-
/// Return true on success
217-
func populateInputDependencySourceMap() -> Bool {
191+
192+
/// Returns: false on error
193+
func populateInputDependencySourceMap(
194+
`for` purpose: InputDependencySourceMap.AdditionPurpose
195+
) -> Bool {
218196
let ofm = info.outputFileMap
219-
let de = info.diagnosticEngine
220-
return info.inputFiles.reduce(true) { okSoFar, input in
221-
ofm.getDependencySource(for: input, diagnosticEngine: de)
222-
.map {source in addMapEntry(input, source); return okSoFar } ?? false
197+
let diags = info.diagnosticEngine
198+
var allFound = true
199+
for input in info.inputFiles {
200+
if let source = ofm.dependencySource(for: input, diagnosticEngine: diags) {
201+
inputDependencySourceMap.addEntry(input, source, for: purpose)
202+
} else {
203+
// Don't break in order to report all failures.
204+
allFound = false
205+
}
223206
}
207+
return allFound
224208
}
225209
}
210+
extension OutputFileMap {
211+
fileprivate func dependencySource(
212+
for sourceFile: TypedVirtualPath,
213+
diagnosticEngine: DiagnosticsEngine
214+
) -> DependencySource? {
215+
assert(sourceFile.type == FileType.swift)
216+
guard let swiftDepsPath = existingOutput(inputFile: sourceFile.fileHandle,
217+
outputType: .swiftDeps)
218+
else {
219+
// The legacy driver fails silently here.
220+
diagnosticEngine.emit(
221+
.remarkDisabled("\(sourceFile.file.basename) has no swiftDeps file")
222+
)
223+
return nil
224+
}
225+
assert(VirtualPath.lookup(swiftDepsPath).extension == FileType.swiftDeps.rawValue)
226+
let typedSwiftDepsFile = TypedVirtualPath(file: swiftDepsPath, type: .swiftDeps)
227+
return DependencySource(typedSwiftDepsFile)
228+
}
229+
}
230+
226231
// MARK: - Scheduling the 2nd wave
227232
extension ModuleDependencyGraph {
228233
/// After `source` has been compiled, figure out what other source files need compiling.
@@ -232,7 +237,7 @@ extension ModuleDependencyGraph {
232237
func collectInputsRequiringCompilation(byCompiling input: TypedVirtualPath
233238
) -> TransitivelyInvalidatedInputSet? {
234239
precondition(input.type == .swift)
235-
let dependencySource = getSource(for: input)
240+
let dependencySource = sourceRequired(for: input)
236241
return collectInputsRequiringCompilationAfterProcessing(
237242
dependencySource: dependencySource)
238243
}
@@ -327,7 +332,10 @@ extension ModuleDependencyGraph {
327332
) -> TransitivelyInvalidatedInputSet? {
328333
var invalidatedInputs = TransitivelyInvalidatedInputSet()
329334
for invalidatedSwiftDeps in collectSwiftDepsUsingInvalidated(nodes: directlyInvalidatedNodes) {
330-
guard let invalidatedInput = getInput(for: invalidatedSwiftDeps) else {
335+
guard let invalidatedInput = inputDependencySourceMap.inputIfKnown(for: invalidatedSwiftDeps)
336+
else {
337+
info.diagnosticEngine.emit(
338+
warning: "Failed to find source file for '\(invalidatedSwiftDeps.file.basename)', recovering with a full rebuild. Next build will be incremental.")
331339
return nil
332340
}
333341
invalidatedInputs.insert(invalidatedInput)
@@ -451,27 +459,6 @@ extension ModuleDependencyGraph {
451459
}
452460
}
453461

454-
extension OutputFileMap {
455-
fileprivate func getDependencySource(
456-
for sourceFile: TypedVirtualPath,
457-
diagnosticEngine: DiagnosticsEngine
458-
) -> DependencySource? {
459-
assert(sourceFile.type == FileType.swift)
460-
guard let swiftDepsPath = existingOutput(inputFile: sourceFile.fileHandle,
461-
outputType: .swiftDeps)
462-
else {
463-
// The legacy driver fails silently here.
464-
diagnosticEngine.emit(
465-
.remarkDisabled("\(sourceFile.file.basename) has no swiftDeps file")
466-
)
467-
return nil
468-
}
469-
assert(VirtualPath.lookup(swiftDepsPath).extension == FileType.swiftDeps.rawValue)
470-
let typedSwiftDepsFile = TypedVirtualPath(file: swiftDepsPath, type: .swiftDeps)
471-
return DependencySource(typedSwiftDepsFile)
472-
}
473-
}
474-
475462
// MARK: - tracking traced nodes
476463
extension ModuleDependencyGraph {
477464

@@ -596,10 +583,11 @@ extension ModuleDependencyGraph {
596583
.record(def: dependencyKey, use: self.allNodes[useID])
597584
assert(isNewUse, "Duplicate use def-use arc in graph?")
598585
}
599-
for (input, source) in inputDependencySourceMap {
600-
graph.addMapEntry(input, source)
586+
for (input, dependencySource) in inputDependencySourceMap {
587+
graph.inputDependencySourceMap.addEntry(input,
588+
dependencySource,
589+
for: .readingPriors)
601590
}
602-
603591
return self.graph
604592
}
605593

@@ -895,7 +883,7 @@ extension ModuleDependencyGraph {
895883
}
896884
}
897885

898-
for (input, dependencySource) in graph.inputDependencySourceMap {
886+
graph.inputDependencySourceMap.enumerateToSerializePriors { input, dependencySource in
899887
self.addIdentifier(input.file.name)
900888
self.addIdentifier(dependencySource.file.name)
901889
}
@@ -1040,7 +1028,8 @@ extension ModuleDependencyGraph {
10401028
}
10411029
}
10421030
}
1043-
for (input, dependencySource) in graph.inputDependencySourceMap {
1031+
graph.inputDependencySourceMap.enumerateToSerializePriors {
1032+
input, dependencySource in
10441033
serializer.stream.writeRecord(serializer.abbreviations[.mapNode]!) {
10451034
$0.append(RecordID.mapNode)
10461035
$0.append(serializer.lookupIdentifierCode(for: input.file.name))
@@ -1158,7 +1147,7 @@ extension Set where Element == ModuleDependencyGraph.Node {
11581147
}
11591148
}
11601149

1161-
extension BidirectionalMap where T1 == TypedVirtualPath, T2 == DependencySource {
1150+
extension InputDependencySourceMap {
11621151
fileprivate func matches(_ other: Self) -> Bool {
11631152
self == other
11641153
}
@@ -1176,6 +1165,6 @@ extension ModuleDependencyGraph {
11761165
_ mockInput: TypedVirtualPath,
11771166
_ mockDependencySource: DependencySource
11781167
) {
1179-
addMapEntry(mockInput, mockDependencySource)
1168+
inputDependencySourceMap.addEntry(mockInput, mockDependencySource, for: .mocking)
11801169
}
11811170
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//===------------- InputDependencySourceMap.swift ---------------- --------===//
2+
// This source file is part of the Swift.org open source project
3+
//
4+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
5+
// Licensed under Apache License v2.0 with Runtime Library Exception
6+
//
7+
// See https://swift.org/LICENSE.txt for license information
8+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
9+
//
10+
//===----------------------------------------------------------------------===//
11+
import Foundation
12+
import TSCBasic
13+
14+
@_spi(Testing) public struct InputDependencySourceMap: Equatable {
15+
16+
/// Maps input files (e.g. .swift) to and from the DependencySource object.
17+
///
18+
// FIXME: The map between swiftdeps and swift files is absolutely *not*
19+
// a bijection. In particular, more than one swiftdeps file can be encountered
20+
// in the course of deserializing priors *and* reading the output file map
21+
// *and* re-reading swiftdeps files after frontends complete
22+
// that correspond to the same swift file. These cause two problems:
23+
// - overwrites in this data structure that lose data and
24+
// - cache misses in `getInput(for:)` that cause the incremental build to
25+
// turn over when e.g. entries in the output file map change. This should be
26+
// replaced by a multi-map from swift files to dependency sources,
27+
// and a regular map from dependency sources to swift files -
28+
// since that direction really is one-to-one.
29+
30+
public typealias BiMap = BidirectionalMap<TypedVirtualPath, DependencySource>
31+
@_spi(Testing) public var biMap = BiMap()
32+
}
33+
34+
// MARK: - Accessing
35+
extension InputDependencySourceMap {
36+
@_spi(Testing) public func sourceIfKnown(for input: TypedVirtualPath) -> DependencySource? {
37+
biMap[input]
38+
}
39+
40+
@_spi(Testing) public func inputIfKnown(for source: DependencySource) -> TypedVirtualPath? {
41+
biMap[source]
42+
}
43+
44+
@_spi(Testing) public func enumerateToSerializePriors(
45+
_ eachFn: (TypedVirtualPath, DependencySource) -> Void
46+
) {
47+
biMap.forEach(eachFn)
48+
}
49+
}
50+
51+
// MARK: - Populating
52+
extension InputDependencySourceMap {
53+
public enum AdditionPurpose {
54+
case mocking,
55+
buildingFromSwiftDeps,
56+
readingPriors,
57+
inputsAddedSincePriors }
58+
@_spi(Testing) public mutating func addEntry(_ input: TypedVirtualPath,
59+
_ dependencySource: DependencySource,
60+
`for` _ : AdditionPurpose) {
61+
assert(input.type == .swift && dependencySource.typedFile.type == .swiftDeps)
62+
biMap[input] = dependencySource
63+
}
64+
}

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraphParts/Tracer.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ extension ModuleDependencyGraph.Tracer {
121121
path.compactMap { node in
122122
node.dependencySource.map {
123123
source in
124-
graph.inputDependencySourceMap[source].map { input in
124+
graph.inputDependencySourceMap.inputIfKnown(for: source).map {
125+
input in
125126
"\(node.key) in \(input.file.basename)"
126127
}
127128
?? "\(node.key)"

0 commit comments

Comments
 (0)