10
10
11
11
import Foundation
12
12
13
- enum BitcodeElement {
14
- struct Block {
15
- var id : UInt64
16
- var elements : [ BitcodeElement ]
13
+ public enum BitcodeElement {
14
+ public struct Block {
15
+ public var id : UInt64
16
+ public var elements : [ BitcodeElement ]
17
17
}
18
18
19
- struct Record {
20
- enum Payload {
19
+ public struct Record {
20
+ public enum Payload {
21
21
case none
22
22
case array( [ UInt64 ] )
23
23
case char6String( String )
24
24
case blob( Data )
25
25
}
26
26
27
- var id : UInt64
28
- var fields : [ UInt64 ]
29
- var payload : Payload
27
+ public var id : UInt64
28
+ public var fields : [ UInt64 ]
29
+ public var payload : Payload
30
30
}
31
31
32
32
case block( Block )
33
33
case record( Record )
34
34
}
35
35
36
- struct BlockInfo {
37
- var name : String = " "
38
- var recordNames : [ UInt64 : String ] = [ : ]
36
+ public struct BlockInfo {
37
+ public var name : String = " "
38
+ public var recordNames : [ UInt64 : String ] = [ : ]
39
39
}
40
40
41
41
extension Bitcode {
42
- struct Signature : Equatable {
42
+ public struct Signature : Equatable {
43
43
private var value : UInt32
44
44
45
- init ( value: UInt32 ) {
45
+ public init ( value: UInt32 ) {
46
46
self . value = value
47
47
}
48
48
49
- init ( string: String ) {
49
+ public init ( string: String ) {
50
50
precondition ( string. utf8. count == 4 )
51
51
var result : UInt32 = 0
52
52
for byte in string. utf8. reversed ( ) {
@@ -58,10 +58,12 @@ extension Bitcode {
58
58
}
59
59
}
60
60
61
- struct Bitcode {
62
- let signature : Signature
63
- let elements : [ BitcodeElement ]
64
- let blockInfo : [ UInt64 : BlockInfo ]
61
+ /// Represents the contents of a file encoded using the
62
+ /// [LLVM bitstream container format](https://llvm.org/docs/BitCodeFormat.html#bitstream-container-format)
63
+ public struct Bitcode {
64
+ public let signature : Signature
65
+ public let elements : [ BitcodeElement ]
66
+ public let blockInfo : [ UInt64 : BlockInfo ]
65
67
}
66
68
67
69
private extension Bits . Cursor {
@@ -297,22 +299,22 @@ private struct BitstreamReader {
297
299
}
298
300
}
299
301
300
- mutating func readBlock( id: UInt64 , abbrevWidth: Int , abbrevInfo: [ Abbrev ] ) throws -> [ BitcodeElement ] {
302
+ mutating func readBlock< Visitor : BitstreamVisitor > ( id: UInt64 , abbrevWidth: Int , abbrevInfo: [ Abbrev ] , visitor : inout Visitor ) throws {
301
303
var abbrevInfo = abbrevInfo
302
- var elements = [ BitcodeElement] ( )
303
304
304
305
while !cursor. isAtEnd {
305
306
switch try cursor. read ( abbrevWidth) {
306
307
case 0 : // END_BLOCK
307
308
try cursor. advance ( toBitAlignment: 32 )
308
309
// FIXME: check expected length
309
- return elements
310
+ try visitor. didExitBlock ( )
311
+ return
310
312
311
313
case 1 : // ENTER_SUBBLOCK
312
314
let blockID = try cursor. readVBR ( 8 )
313
315
let newAbbrevWidth = Int ( try cursor. readVBR ( 4 ) )
314
316
try cursor. advance ( toBitAlignment: 32 )
315
- _ = try cursor. read ( 32 ) // FIXME: use expected length
317
+ let blockLength = try cursor. read ( 32 ) * 4
316
318
317
319
switch blockID {
318
320
case 0 :
@@ -321,9 +323,13 @@ private struct BitstreamReader {
321
323
// Metadata blocks we don't understand yet
322
324
fallthrough
323
325
default :
324
- let innerElements = try readBlock (
325
- id: blockID, abbrevWidth: newAbbrevWidth, abbrevInfo: globalAbbrevs [ blockID] ?? [ ] )
326
- elements. append ( . block( . init( id: blockID, elements: innerElements) ) )
326
+ guard try visitor. shouldEnterBlock ( id: blockID) else {
327
+ try cursor. skip ( bytes: Int ( blockLength) )
328
+ break
329
+ }
330
+ try readBlock (
331
+ id: blockID, abbrevWidth: newAbbrevWidth,
332
+ abbrevInfo: globalAbbrevs [ blockID] ?? [ ] , visitor: & visitor)
327
333
}
328
334
329
335
case 2 : // DEFINE_ABBREV
@@ -337,33 +343,97 @@ private struct BitstreamReader {
337
343
for _ in 0 ..< numOps {
338
344
operands. append ( try cursor. readVBR ( 6 ) )
339
345
}
340
- elements . append ( . record( . init( id: code, fields: operands, payload: . none) ) )
346
+ try visitor . visit ( record: . init( id: code, fields: operands, payload: . none) )
341
347
342
348
case let abbrevID:
343
349
guard Int ( abbrevID) - 4 < abbrevInfo. count else {
344
350
throw Error . noSuchAbbrev ( blockID: id, abbrevID: Int ( abbrevID) )
345
351
}
346
- elements . append ( . record( try readAbbreviatedRecord ( abbrevInfo [ Int ( abbrevID) - 4 ] ) ) )
352
+ try visitor . visit ( record: try readAbbreviatedRecord ( abbrevInfo [ Int ( abbrevID) - 4 ] ) )
347
353
}
348
354
}
349
355
350
356
guard id == Self . fakeTopLevelBlockID else {
351
357
throw Error . missingEndBlock ( blockID: id)
352
358
}
353
- return elements
354
359
}
355
360
356
361
static let fakeTopLevelBlockID : UInt64 = ~ 0
357
362
}
358
363
364
+ /// A visitor which receives callbacks while reading a bitstream.
365
+ public protocol BitstreamVisitor {
366
+ /// Customization point to validate a bitstream's signature or "magic number".
367
+ func validate( signature: Bitcode . Signature ) throws
368
+ /// Called when a new block is encountered. Return `true` to enter the block
369
+ /// and read its contents, or `false` to skip it.
370
+ mutating func shouldEnterBlock( id: UInt64 ) throws -> Bool
371
+ /// Called when a block is exited.
372
+ mutating func didExitBlock( ) throws
373
+ /// Called whenever a record is encountered.
374
+ mutating func visit( record: BitcodeElement . Record ) throws
375
+ }
376
+
377
+ /// A basic visitor that collects all the blocks and records in a stream.
378
+ private struct CollectingVisitor : BitstreamVisitor {
379
+ var stack : [ ( UInt64 , [ BitcodeElement ] ) ] = [ ( BitstreamReader . fakeTopLevelBlockID, [ ] ) ]
380
+
381
+ func validate( signature: Bitcode . Signature ) throws { }
382
+
383
+ mutating func shouldEnterBlock( id: UInt64 ) throws -> Bool {
384
+ stack. append ( ( id, [ ] ) )
385
+ return true
386
+ }
387
+
388
+ mutating func didExitBlock( ) throws {
389
+ guard let ( id, elements) = stack. popLast ( ) else {
390
+ fatalError ( " Unbalanced calls to shouldEnterBlock/didExitBlock " )
391
+ }
392
+
393
+ let block = BitcodeElement . Block ( id: id, elements: elements)
394
+ stack [ stack. endIndex- 1 ] . 1 . append ( . block( block) )
395
+ }
396
+
397
+ mutating func visit( record: BitcodeElement . Record ) throws {
398
+ stack [ stack. endIndex- 1 ] . 1 . append ( . record( record) )
399
+ }
400
+
401
+ func finalizeTopLevelElements( ) -> [ BitcodeElement ] {
402
+ assert ( stack. count == 1 )
403
+ return stack [ 0 ] . 1
404
+ }
405
+ }
406
+
359
407
extension Bitcode {
360
- init ( data: Data ) throws {
408
+ /// Parse a bitstream from data.
409
+ public init ( data: Data ) throws {
361
410
precondition ( data. count > 4 )
362
411
let signatureValue = UInt32 ( Bits ( buffer: data) . readBits ( atOffset: 0 , count: 32 ) )
363
412
let bitstreamData = data [ 4 ..< data. count]
364
413
365
414
var reader = BitstreamReader ( buffer: bitstreamData)
366
- let topLevelElements = try reader. readBlock ( id: BitstreamReader . fakeTopLevelBlockID, abbrevWidth: 2 , abbrevInfo: [ ] )
367
- self . init ( signature: . init( value: signatureValue) , elements: topLevelElements, blockInfo: reader. blockInfo)
415
+ var visitor = CollectingVisitor ( )
416
+ try reader. readBlock ( id: BitstreamReader . fakeTopLevelBlockID,
417
+ abbrevWidth: 2 ,
418
+ abbrevInfo: [ ] ,
419
+ visitor: & visitor)
420
+ self . init ( signature: . init( value: signatureValue) ,
421
+ elements: visitor. finalizeTopLevelElements ( ) ,
422
+ blockInfo: reader. blockInfo)
423
+ }
424
+
425
+ /// Traverse a bitstream using the specified `visitor`, which will receive
426
+ /// callbacks when blocks and records are encountered.
427
+ public static func read< Visitor: BitstreamVisitor > ( stream data: Data , using visitor: inout Visitor ) throws {
428
+ precondition ( data. count > 4 )
429
+ let signatureValue = UInt32 ( Bits ( buffer: data) . readBits ( atOffset: 0 , count: 32 ) )
430
+ try visitor. validate ( signature: . init( value: signatureValue) )
431
+
432
+ let bitstreamData = data [ 4 ..< data. count]
433
+ var reader = BitstreamReader ( buffer: bitstreamData)
434
+ try reader. readBlock ( id: BitstreamReader . fakeTopLevelBlockID,
435
+ abbrevWidth: 2 ,
436
+ abbrevInfo: [ ] ,
437
+ visitor: & visitor)
368
438
}
369
439
}
0 commit comments