Skip to content

Commit 7298fe8

Browse files
committed
[SyntaxVisitor] Adopt SyntaxNodeFactory
Use 'SyntaxNodeFactory' in 'SyntaxVisitor' too. Thanks to the simplified implementation, it improves the performance a bit.
1 parent b67e899 commit 7298fe8

File tree

2 files changed

+12
-146
lines changed

2 files changed

+12
-146
lines changed

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxVisitorFile.swift

Lines changed: 6 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -34,27 +34,8 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
3434

3535
DeclSyntax(
3636
"""
37-
/// `Syntax.Info` objects created in `visitChildren` but whose `Syntax` nodes were not retained by the `visit`
38-
/// functions implemented by a subclass of `SyntaxVisitor`.
39-
///
40-
/// Instead of deallocating them and allocating memory for new syntax nodes, store the allocated memory in an array.
41-
/// We can then re-use them to create new syntax nodes.
42-
///
43-
/// The array's size should be a typical nesting depth of a Swift file. That way we can store all allocated syntax
44-
/// nodes when unwinding the visitation stack.
45-
///
46-
/// The actual `info` stored in the `Syntax.Info` objects is garbage. It needs to be set when any of the `Syntax.Info`
47-
/// objects get re-used.
48-
private var recyclableNodeInfos: ContiguousArray<Syntax.Info?> = ContiguousArray(repeating: nil, count: 64)
49-
"""
50-
)
51-
52-
DeclSyntax(
53-
"""
54-
/// A bit is set to 1 if the corresponding index in `recyclableNodeInfos` is occupied and ready to be reused.
55-
///
56-
/// The last bit in this UInt64 corresponds to index 0 in `recyclableNodeInfos`.
57-
private var recyclableNodeInfosUsageBitmap: UInt64 = 0
37+
/// 'Syntax' object factory recycling 'Syntax.Info' instances.
38+
private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory()
5839
"""
5940
)
6041

@@ -261,65 +242,14 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
261242
DeclSyntax(
262243
"""
263244
/// - Note: `node` is `inout` to avoid reference counting. See comment in `visitImpl`.
264-
private func visitChildren(_ syntaxNode: inout Syntax) {
265-
for childRaw in NonNilRawSyntaxChildren(syntaxNode, viewMode: viewMode) {
266-
// syntaxNode gets retained here. That seems unnecessary but I don't know how to remove it.
267-
var childNode: Syntax
268-
if let recycledInfoIndex = recyclableNodeInfosUsageBitmap.indexOfRightmostOne {
269-
var recycledInfo: Syntax.Info? = nil
270-
// Use `swap` to extract the recyclable syntax node without incurring ref-counting.
271-
swap(&recycledInfo, &recyclableNodeInfos[recycledInfoIndex])
272-
assert(recycledInfo != nil, "Slot indicated by the bitmap did not contain a value")
273-
recyclableNodeInfosUsageBitmap.setBitToZero(at: recycledInfoIndex)
274-
// syntaxNode.info gets retained here. This is necessary because we build up the parent tree.
275-
recycledInfo!.info = .nonRoot(.init(parent: syntaxNode, absoluteInfo: childRaw.info))
276-
childNode = Syntax(childRaw.raw, info: recycledInfo!)
277-
} else {
278-
childNode = Syntax(childRaw, parent: syntaxNode)
279-
}
245+
private func visitChildren(_ node: inout Syntax) {
246+
for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) {
247+
var childNode = nodeFactory.create(parent: node, raw: child, absoluteInfo: info)
280248
visit(&childNode)
281-
if isKnownUniquelyReferenced(&childNode.info) {
282-
// The node didn't get stored by the subclass's visit method. We can re-use the memory of its `Syntax.Info`
283-
// for future syntax nodes.
284-
childNode.info.info = nil
285-
if let emptySlot = recyclableNodeInfosUsageBitmap.indexOfRightmostZero {
286-
// Use `swap` to store the recyclable syntax node without incurring ref-counting.
287-
swap(&recyclableNodeInfos[emptySlot], &childNode.info)
288-
assert(childNode.info == nil, "Slot should not have contained a value")
289-
recyclableNodeInfosUsageBitmap.setBitToOne(at: emptySlot)
290-
}
291-
}
249+
nodeFactory.dispose(&childNode)
292250
}
293251
}
294252
"""
295253
)
296254
}
297-
298-
DeclSyntax(
299-
"""
300-
fileprivate extension UInt64 {
301-
var indexOfRightmostZero: Int? {
302-
return (~self).indexOfRightmostOne
303-
}
304-
305-
var indexOfRightmostOne: Int? {
306-
let trailingZeroCount = self.trailingZeroBitCount
307-
if trailingZeroCount == Self.bitWidth {
308-
// All indicies are 0
309-
return nil
310-
}
311-
return trailingZeroCount
312-
}
313-
314-
mutating func setBitToZero(at index: Int) {
315-
self &= ~(1 << index)
316-
}
317-
318-
mutating func setBitToOne(at index: Int) {
319-
self |= 1 << index
320-
}
321-
}
322-
323-
"""
324-
)
325255
}

Sources/SwiftSyntax/generated/SyntaxVisitor.swift

Lines changed: 6 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,8 @@ public enum SyntaxVisitorContinueKind {
2424
open class SyntaxVisitor {
2525
public let viewMode: SyntaxTreeViewMode
2626

27-
/// `Syntax.Info` objects created in `visitChildren` but whose `Syntax` nodes were not retained by the `visit`
28-
/// functions implemented by a subclass of `SyntaxVisitor`.
29-
///
30-
/// Instead of deallocating them and allocating memory for new syntax nodes, store the allocated memory in an array.
31-
/// We can then re-use them to create new syntax nodes.
32-
///
33-
/// The array's size should be a typical nesting depth of a Swift file. That way we can store all allocated syntax
34-
/// nodes when unwinding the visitation stack.
35-
///
36-
/// The actual `info` stored in the `Syntax.Info` objects is garbage. It needs to be set when any of the `Syntax.Info`
37-
/// objects get re-used.
38-
private var recyclableNodeInfos: ContiguousArray<Syntax.Info?> = ContiguousArray(repeating: nil, count: 64)
39-
40-
/// A bit is set to 1 if the corresponding index in `recyclableNodeInfos` is occupied and ready to be reused.
41-
///
42-
/// The last bit in this UInt64 corresponds to index 0 in `recyclableNodeInfos`.
43-
private var recyclableNodeInfosUsageBitmap: UInt64 = 0
27+
/// 'Syntax' object factory recycling 'Syntax.Info' instances.
28+
private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory()
4429

4530
public init(viewMode: SyntaxTreeViewMode) {
4631
self.viewMode = viewMode
@@ -5267,60 +5252,11 @@ open class SyntaxVisitor {
52675252
#endif
52685253

52695254
/// - Note: `node` is `inout` to avoid reference counting. See comment in `visitImpl`.
5270-
private func visitChildren(_ syntaxNode: inout Syntax) {
5271-
for childRaw in NonNilRawSyntaxChildren(syntaxNode, viewMode: viewMode) {
5272-
// syntaxNode gets retained here. That seems unnecessary but I don't know how to remove it.
5273-
var childNode: Syntax
5274-
if let recycledInfoIndex = recyclableNodeInfosUsageBitmap.indexOfRightmostOne {
5275-
var recycledInfo: Syntax.Info? = nil
5276-
// Use `swap` to extract the recyclable syntax node without incurring ref-counting.
5277-
swap(&recycledInfo, &recyclableNodeInfos[recycledInfoIndex])
5278-
assert(recycledInfo != nil, "Slot indicated by the bitmap did not contain a value")
5279-
recyclableNodeInfosUsageBitmap.setBitToZero(at: recycledInfoIndex)
5280-
// syntaxNode.info gets retained here. This is necessary because we build up the parent tree.
5281-
recycledInfo!.info = .nonRoot(.init(parent: syntaxNode, absoluteInfo: childRaw.info))
5282-
childNode = Syntax(childRaw.raw, info: recycledInfo!)
5283-
} else {
5284-
childNode = Syntax(childRaw, parent: syntaxNode)
5285-
}
5255+
private func visitChildren(_ node: inout Syntax) {
5256+
for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) {
5257+
var childNode = nodeFactory.create(parent: node, raw: child, absoluteInfo: info)
52865258
visit(&childNode)
5287-
if isKnownUniquelyReferenced(&childNode.info) {
5288-
// The node didn't get stored by the subclass's visit method. We can re-use the memory of its `Syntax.Info`
5289-
// for future syntax nodes.
5290-
childNode.info.info = nil
5291-
if let emptySlot = recyclableNodeInfosUsageBitmap.indexOfRightmostZero {
5292-
// Use `swap` to store the recyclable syntax node without incurring ref-counting.
5293-
swap(&recyclableNodeInfos[emptySlot], &childNode.info)
5294-
assert(childNode.info == nil, "Slot should not have contained a value")
5295-
recyclableNodeInfosUsageBitmap.setBitToOne(at: emptySlot)
5296-
}
5297-
}
5298-
}
5299-
}
5300-
}
5301-
5302-
fileprivate extension UInt64 {
5303-
var indexOfRightmostZero: Int? {
5304-
return (~self).indexOfRightmostOne
5305-
}
5306-
5307-
5308-
var indexOfRightmostOne: Int? {
5309-
let trailingZeroCount = self.trailingZeroBitCount
5310-
if trailingZeroCount == Self.bitWidth {
5311-
// All indicies are 0
5312-
return nil
5259+
nodeFactory.dispose(&childNode)
53135260
}
5314-
return trailingZeroCount
5315-
}
5316-
5317-
5318-
mutating func setBitToZero(at index: Int) {
5319-
self &= ~(1 << index)
5320-
}
5321-
5322-
5323-
mutating func setBitToOne(at index: Int) {
5324-
self |= 1 << index
53255261
}
53265262
}

0 commit comments

Comments
 (0)