Skip to content

Commit eab8926

Browse files
owenvbelkadan
andcommitted
Add utilities for reading the LLVM bitstream container format
Originally authored by Jordan Rose Modified to drop some printing and logging code that's not needed for the driver's use cases Co-authored-by: Jordan Rose <[email protected]>
1 parent fd3f559 commit eab8926

File tree

4 files changed

+566
-0
lines changed

4 files changed

+566
-0
lines changed

Sources/SwiftDriver/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ add_library(SwiftDriver
5959
Toolchains/GenericUnixToolchain.swift
6060
Toolchains/Toolchain.swift
6161

62+
Utilities/Bits.swift
63+
Utilities/Bitstream.swift
6264
Utilities/DOTJobGraphSerializer.swift
6365
Utilities/DateAdditions.swift
6466
Utilities/Diagnostics.swift

Sources/SwiftDriver/Incremental Compilation/SourceFileDependencyGraph.swift

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,109 @@ extension SourceFileDependencyGraph {
111111
}
112112
}
113113
}
114+
115+
extension SourceFileDependencyGraph {
116+
private static let recordBlockId = 8
117+
118+
private enum RecordKind: UInt64 {
119+
case metadata = 1
120+
case sourceFileDepGraphNode
121+
case fingerprintNode
122+
case dependsOnDefinitionNode
123+
case identifierNode
124+
}
125+
126+
private enum ReadError: Error {
127+
case badMagic
128+
case noRecordBlock
129+
case malformedMetadataRecord
130+
case unexpectedMetadataRecord
131+
case malformedFingerprintRecord
132+
case malformedDependsOnDefinitionRecord
133+
case malformedIdentifierRecord
134+
case malformedSourceFileDepGraphNodeRecord
135+
case unknownRecord
136+
case unexpectedSubblock
137+
}
138+
139+
public init(data: Data) throws {
140+
let bitcode = try Bitcode(data: data)
141+
guard bitcode.signature == 0x53_50_45_44 else { throw ReadError.badMagic } // DEPS
142+
143+
guard bitcode.elements.count == 1,
144+
case .block(let recordBlock) = bitcode.elements.first,
145+
recordBlock.id == Self.recordBlockId else { throw ReadError.noRecordBlock }
146+
147+
guard case .record(let metadataRecord) = recordBlock.elements.first,
148+
RecordKind(rawValue: metadataRecord.id) == .metadata,
149+
metadataRecord.fields.count == 2,
150+
case .blob(let compilerVersionBlob) = metadataRecord.payload,
151+
let compilerVersionString = String(data: compilerVersionBlob, encoding: .utf8)
152+
else { throw ReadError.malformedMetadataRecord }
153+
154+
self.majorVersion = metadataRecord.fields[0]
155+
self.minorVersion = metadataRecord.fields[1]
156+
self.compilerVersionString = compilerVersionString
157+
158+
var nodes: [Node] = []
159+
var node: Node? = nil
160+
var identifiers: [String] = [""] // The empty string is hardcoded as identifiers[0]
161+
var sequenceNumber = 0
162+
for element in recordBlock.elements.dropFirst() {
163+
guard case .record(let record) = element else { throw ReadError.unexpectedSubblock }
164+
guard let kind = RecordKind(rawValue: record.id) else { throw ReadError.unknownRecord }
165+
switch kind {
166+
case .metadata:
167+
throw ReadError.unexpectedMetadataRecord
168+
case .sourceFileDepGraphNode:
169+
if let node = node {
170+
nodes.append(node)
171+
}
172+
guard record.fields.count == 5,
173+
let nodeKind = DependencyKey.Kind(rawValue: record.fields[0]),
174+
let declAspect = DeclAspect(rawValue: record.fields[1]),
175+
record.fields[2] < identifiers.count,
176+
record.fields[3] < identifiers.count else {
177+
throw ReadError.malformedSourceFileDepGraphNodeRecord
178+
}
179+
let context = identifiers[Int(record.fields[2])]
180+
let identifier = identifiers[Int(record.fields[3])]
181+
let isProvides = record.fields[4] != 0
182+
node = Node(key: .init(kind: nodeKind,
183+
aspect: declAspect,
184+
context: context,
185+
name: identifier),
186+
fingerprint: nil,
187+
sequenceNumber: sequenceNumber,
188+
defsIDependUpon: [],
189+
isProvides: isProvides)
190+
sequenceNumber += 1
191+
case .fingerprintNode:
192+
guard node != nil,
193+
record.fields.count == 0,
194+
case .blob(let fingerprintBlob) = record.payload,
195+
let fingerprint = String(data: fingerprintBlob, encoding: .utf8) else {
196+
throw ReadError.malformedFingerprintRecord
197+
}
198+
node?.fingerprint = fingerprint
199+
case .dependsOnDefinitionNode:
200+
guard node != nil,
201+
record.fields.count == 1 else { throw ReadError.malformedDependsOnDefinitionRecord }
202+
node?.defsIDependUpon.append(Int(record.fields[0]))
203+
case .identifierNode:
204+
guard record.fields.count == 0,
205+
case .blob(let identifierBlob) = record.payload,
206+
let identifier = String(data: identifierBlob, encoding: .utf8) else {
207+
throw ReadError.malformedIdentifierRecord
208+
}
209+
identifiers.append(identifier)
210+
}
211+
}
212+
213+
if let node = node {
214+
nodes.append(node)
215+
}
216+
217+
self.allNodes = nodes
218+
}
219+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//===----------------- Bits.swift - Collection of bits --------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Foundation
14+
15+
// FIXME: Should be RandomAccessCollection
16+
struct Bits: BidirectionalCollection {
17+
typealias Index = Int
18+
19+
var buffer: Data
20+
21+
var startIndex: Int { return 0 }
22+
var endIndex: Int { return buffer.count * 8 }
23+
24+
subscript(index: Int) -> UInt8 {
25+
let byte = buffer[index / 8]
26+
return (byte >> UInt8(index % 8)) & 1
27+
}
28+
29+
func index(after i: Int) -> Int {
30+
return i + 1
31+
}
32+
func index(before i: Int) -> Int {
33+
return i - 1
34+
}
35+
36+
subscript(extractAt offset: Int, count count: Int) -> UInt64 {
37+
precondition(count >= 0 && count <= 64)
38+
precondition(offset >= 0)
39+
precondition(offset &+ count >= offset)
40+
precondition(offset &+ count <= self.endIndex)
41+
42+
return buffer.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
43+
let upperBound = offset &+ count
44+
let topByteIndex = upperBound >> 3
45+
var result: UInt64 = 0
46+
if upperBound & 7 != 0 {
47+
let mask: UInt8 = (1 << UInt8(upperBound & 7)) &- 1
48+
result = UInt64(bytes[topByteIndex] & mask)
49+
}
50+
for i in ((offset >> 3)..<(upperBound >> 3)).reversed() {
51+
result <<= 8
52+
result |= UInt64(bytes[i])
53+
}
54+
if offset & 7 != 0 {
55+
result >>= UInt64(offset & 7)
56+
}
57+
return result
58+
}
59+
}
60+
61+
struct Cursor {
62+
enum Error: Swift.Error { case bufferOverflow }
63+
64+
let buffer: Bits
65+
private var offset: Int = 0
66+
67+
init(buffer: Bits) {
68+
self.buffer = buffer
69+
}
70+
71+
init(buffer: Data) {
72+
self.init(buffer: Bits(buffer: buffer))
73+
}
74+
75+
var isAtEnd: Bool {
76+
return offset == buffer.count
77+
}
78+
79+
func peek(_ count: Int) throws -> UInt64 {
80+
if buffer.count - offset < count { throw Error.bufferOverflow }
81+
return buffer[extractAt: offset, count: count]
82+
}
83+
84+
mutating func read(_ count: Int) throws -> UInt64 {
85+
defer { offset += count }
86+
return try peek(count)
87+
}
88+
89+
mutating func read(bytes count: Int) throws -> Data {
90+
precondition(count >= 0)
91+
precondition(offset & 0b111 == 0)
92+
let newOffset = offset &+ (count << 3)
93+
precondition(newOffset >= offset)
94+
if newOffset > buffer.count { throw Error.bufferOverflow }
95+
defer { offset = newOffset }
96+
return buffer.buffer.subdata(in: (offset >> 3)..<(newOffset >> 3))
97+
}
98+
99+
mutating func advance(toBitAlignment align: Int) throws {
100+
precondition(align > 0)
101+
precondition(offset &+ (align&-1) >= offset)
102+
if offset % align == 0 { return }
103+
offset = ((offset / align) &+ 1) &* align
104+
if offset > buffer.count { throw Error.bufferOverflow }
105+
}
106+
}
107+
}

0 commit comments

Comments
 (0)