Skip to content

Commit 5f4a479

Browse files
committed
Serialize The Uses-By-Defs Map
This completes the serialization of the dependency graph. The idea is to notice that the dependency key for a def may not always appear in the node map, but its uses certainly will (there are scattered invariants, especially in the tracer, that this is true). That means we'll be redundantly serializing nodes if we just naively write out the contents of the uses-by-defs map. Therefore, we establish a linear ordering of dependency nodes - in this case, serialization order - and record memoized node IDs. These are recorded instead, saving 6x the space of a regular serialized node.
1 parent 9c75e72 commit 5f4a479

File tree

2 files changed

+116
-10
lines changed

2 files changed

+116
-10
lines changed

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift

Lines changed: 112 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,10 @@ extension ModuleDependencyGraph {
279279
fileprivate enum RecordID: UInt64 {
280280
case metadata = 1
281281
case moduleDepGraphNode = 2
282-
case identifierNode = 3
282+
case dependsOnNode = 3
283+
case useIDNode = 4
283284
case externalDepNode = 5
285+
case identifierNode = 6
284286

285287
/// The human-readable name of this record.
286288
///
@@ -293,6 +295,10 @@ extension ModuleDependencyGraph {
293295
return "METADATA"
294296
case .moduleDepGraphNode:
295297
return "MODULE_DEP_GRAPH_NODE"
298+
case .dependsOnNode:
299+
return "DEPENDS_ON_NODE"
300+
case .useIDNode:
301+
return "USE_ID_NODE"
296302
case .externalDepNode:
297303
return "EXTERNAL_DEP_NODE"
298304
case .identifierNode:
@@ -309,6 +315,7 @@ extension ModuleDependencyGraph {
309315
case malformedFingerprintRecord
310316
case malformedIdentifierRecord
311317
case malformedModuleDepGraphNodeRecord
318+
case malformedDependsOnRecord
312319
case malformedExternalDepNodeRecord
313320
case unknownRecord
314321
case unexpectedSubblock
@@ -334,13 +341,16 @@ extension ModuleDependencyGraph {
334341
let data = try fileSystem.readFileContents(path)
335342

336343
struct Visitor: BitstreamVisitor {
337-
let graph: ModuleDependencyGraph
344+
private let graph: ModuleDependencyGraph
338345
var majorVersion: UInt64?
339346
var minorVersion: UInt64?
340347
var compilerVersionString: String?
341348

342349
// The empty string is hardcoded as identifiers[0]
343350
private var identifiers: [String] = [""]
351+
private var currentDefKey: DependencyKey? = nil
352+
private var nodeUses: [DependencyKey: [Int]] = [:]
353+
private var allNodes: [Node] = []
344354

345355
init(
346356
diagnosticEngine: DiagnosticsEngine,
@@ -352,8 +362,21 @@ extension ModuleDependencyGraph {
352362
verifyDependencyGraphAfterEveryImport: false)
353363
}
354364

365+
func finalizeGraph() -> ModuleDependencyGraph {
366+
for (dependencyKey, useIDs) in self.nodeUses {
367+
for useID in useIDs {
368+
let isNewUse = self.graph.nodeFinder
369+
.record(def: dependencyKey, use: self.allNodes[useID])
370+
assert(isNewUse, "Duplicate use def-use arc in graph?")
371+
}
372+
}
373+
return self.graph
374+
}
375+
355376
func validate(signature: Bitcode.Signature) throws {
356-
guard signature == .init(string: ModuleDependencyGraph.signature) else { throw ReadError.badMagic }
377+
guard signature == .init(string: ModuleDependencyGraph.signature) else {
378+
throw ReadError.badMagic
379+
}
357380
}
358381

359382
mutating func shouldEnterBlock(id: UInt64) throws -> Bool {
@@ -362,7 +385,8 @@ extension ModuleDependencyGraph {
362385

363386
mutating func didExitBlock() throws {}
364387

365-
private func finalize(node newNode: Node) {
388+
private mutating func finalize(node newNode: Node) {
389+
self.allNodes.append(newNode)
366390
let oldNode = self.graph.nodeFinder.insert(newNode)
367391
assert(oldNode == nil,
368392
"Integrated the same node twice: \(oldNode!), \(newNode)")
@@ -388,9 +412,6 @@ extension ModuleDependencyGraph {
388412
self.minorVersion = record.fields[1]
389413
self.compilerVersionString = compilerVersionString
390414
case .moduleDepGraphNode:
391-
if let node = node {
392-
self.finalize(node: node)
393-
}
394415
let kindCode = record.fields[0]
395416
guard record.fields.count == 7,
396417
let declAspect = DependencyKey.DeclAspect(record.fields[1]),
@@ -413,6 +434,28 @@ extension ModuleDependencyGraph {
413434
let swiftDeps = try swiftDepsStr
414435
.map({ try VirtualPath(path: $0) })
415436
.map(ModuleDependencyGraph.SwiftDeps.init)
437+
self.finalize(node: Node(key: key,
438+
fingerprint: fingerprint,
439+
swiftDeps: swiftDeps))
440+
case .dependsOnNode:
441+
let kindCode = record.fields[0]
442+
guard record.fields.count == 4,
443+
let declAspect = DependencyKey.DeclAspect(record.fields[1]),
444+
record.fields[2] < identifiers.count,
445+
record.fields[3] < identifiers.count
446+
else {
447+
throw ReadError.malformedDependsOnRecord
448+
}
449+
let context = identifiers[Int(record.fields[2])]
450+
let identifier = identifiers[Int(record.fields[3])]
451+
let designator = try DependencyKey.Designator(
452+
kindCode: kindCode, context: context, name: identifier)
453+
self.currentDefKey = DependencyKey(aspect: declAspect, designator: designator)
454+
case .useIDNode:
455+
guard let key = self.currentDefKey, record.fields.count == 1 else {
456+
throw ReadError.malformedDependsOnRecord
457+
}
458+
self.nodeUses[key, default: []].append(Int(record.fields[0]))
416459
case .externalDepNode:
417460
guard record.fields.count == 1,
418461
record.fields[0] < identifiers.count
@@ -448,7 +491,7 @@ extension ModuleDependencyGraph {
448491
else {
449492
throw ReadError.malformedMetadataRecord
450493
}
451-
return visitor.graph
494+
return visitor.finalizeGraph()
452495
}
453496
}
454497
}
@@ -491,6 +534,8 @@ extension ModuleDependencyGraph {
491534
private var identifiersToWrite = [String]()
492535
private var identifierIDs = [String: Int]()
493536
private var lastIdentifierID: Int = 1
537+
fileprivate private(set) var nodeIDs = [Node: Int]()
538+
private var lastNodeID: Int = 0
494539

495540
private init(compilerVersion: String) {
496541
self.compilerVersion = compilerVersion
@@ -529,6 +574,8 @@ extension ModuleDependencyGraph {
529574
self.emitBlockID(.firstApplicationID, "RECORD_BLOCK")
530575
self.emitRecordID(.metadata)
531576
self.emitRecordID(.moduleDepGraphNode)
577+
self.emitRecordID(.useIDNode)
578+
self.emitRecordID(.externalDepNode)
532579
self.emitRecordID(.identifierNode)
533580
}
534581
}
@@ -562,8 +609,15 @@ extension ModuleDependencyGraph {
562609
return UInt32(self.identifierIDs[string]!)
563610
}
564611

565-
private func writeStrings(in graph: ModuleDependencyGraph) {
612+
private func cacheNodeID(for node: Node) {
613+
defer { self.lastNodeID += 1 }
614+
nodeIDs[node] = self.lastNodeID
615+
}
616+
617+
private func populateCaches(from graph: ModuleDependencyGraph) {
566618
graph.nodeFinder.forEachNode { node in
619+
self.cacheNodeID(for: node)
620+
567621
if let swiftDeps = node.swiftDeps?.file.name {
568622
self.addIdentifier(swiftDeps)
569623
}
@@ -575,6 +629,15 @@ extension ModuleDependencyGraph {
575629
}
576630
}
577631

632+
for key in graph.nodeFinder.usesByDef.keys {
633+
if let context = key.designator.context {
634+
self.addIdentifier(context)
635+
}
636+
if let name = key.designator.name {
637+
self.addIdentifier(name)
638+
}
639+
}
640+
578641
for path in graph.externalDependencies {
579642
self.addIdentifier(path.fileName)
580643
}
@@ -615,6 +678,23 @@ extension ModuleDependencyGraph {
615678
// fingerprint bytes
616679
.blob,
617680
])
681+
self.abbreviate(.dependsOnNode, [
682+
.literal(RecordID.dependsOnNode.rawValue),
683+
// dependency kind discriminator
684+
.fixed(bitWidth: 3),
685+
// dependency decl aspect discriminator
686+
.fixed(bitWidth: 1),
687+
// dependency context
688+
.vbr(chunkBitWidth: 13),
689+
// dependency name
690+
.vbr(chunkBitWidth: 13),
691+
])
692+
693+
self.abbreviate(.useIDNode, [
694+
.literal(RecordID.useIDNode.rawValue),
695+
// node ID
696+
.vbr(chunkBitWidth: 13),
697+
])
618698
self.abbreviate(.externalDepNode, [
619699
.literal(RecordID.externalDepNode.rawValue),
620700
// path ID
@@ -648,7 +728,7 @@ extension ModuleDependencyGraph {
648728

649729
serializer.writeMetadata()
650730

651-
serializer.writeStrings(in: graph)
731+
serializer.populateCaches(from: graph)
652732

653733
graph.nodeFinder.forEachNode { node in
654734
serializer.stream.writeRecord(serializer.abbreviations[.moduleDepGraphNode]!, {
@@ -666,6 +746,28 @@ extension ModuleDependencyGraph {
666746
}, blob: node.fingerprint ?? "")
667747
}
668748

749+
for key in graph.nodeFinder.usesByDef.keys {
750+
serializer.stream.writeRecord(serializer.abbreviations[.dependsOnNode]!) {
751+
$0.append(RecordID.dependsOnNode)
752+
$0.append(key.designator.code)
753+
$0.append(key.aspect.code)
754+
$0.append(serializer.lookupIdentifierCode(
755+
for: key.designator.context ?? ""))
756+
$0.append(serializer.lookupIdentifierCode(
757+
for: key.designator.name ?? ""))
758+
}
759+
for use in graph.nodeFinder.usesByDef[key]?.values ?? [] {
760+
guard let useID = serializer.nodeIDs[use] else {
761+
fatalError("Node ID was not registered! \(use)")
762+
}
763+
764+
serializer.stream.writeRecord(serializer.abbreviations[.useIDNode]!) {
765+
$0.append(RecordID.useIDNode)
766+
$0.append(UInt32(useID))
767+
}
768+
}
769+
}
770+
669771
for dep in graph.externalDependencies {
670772
serializer.stream.writeRecord(serializer.abbreviations[.externalDepNode]!) {
671773
$0.append(RecordID.externalDepNode)

Tests/SwiftDriverTests/DependencyGraphSerializationTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ class DependencyGraphSerializationTests: XCTestCase {
3737

3838
XCTAssertTrue(originalNodes == deserializedNodes,
3939
"Round trip failed! Symmetric difference - \(originalNodes.symmetricDifference(deserializedNodes))")
40+
41+
XCTAssertEqual(graph.nodeFinder.usesByDef, deserializedGraph.nodeFinder.usesByDef)
42+
XCTAssertEqual(graph.sourceSwiftDepsMap, deserializedGraph.sourceSwiftDepsMap)
43+
XCTAssertEqual(graph.externalDependencies, deserializedGraph.externalDependencies)
4044
}
4145

4246
func testRoundTripFixtures() throws {

0 commit comments

Comments
 (0)