Skip to content

Commit cd9009f

Browse files
committed
[Syntax] Use BumpPtrAllocator for Syntax node internals
Rework 'Syntax' internals. "Red" tree are now bump-pointer allocated and the visited chilren are cached. * 'Syntax' is a pair of the allocator (retaing ref to 'SyntaxDataArena' class) and a pointer to the allocated data ('SyntaxData' struct). * 'child(at:)' is now O(n) because the red-tree children are cached. * Simplifed 'SyntaxChildren'. 'SyntaxChildrenIndex' is now just a 'Int' Remove some hacks as they aren't needed anymore: * 'UnownedSyntax' because 'SyntaxDataReference' replaces it. * 'SyntaxNodeFactory' because 'Syntax.Info' is gone. Additionally: * Flatten 'AbsoluteSyntaxInfo' to simplify the data and clarify what 'advancedBySibling(_:)' and 'advancedToFirstChild()' do. * Remove 'RawSyntaxChildren' and 'RawNonNilSyntaxChildren' as they were implementation detail of 'SyntaxChildren'. * Remove 'AbsoluteRawSyntax' and 'AbsoluteSyntaxPosition' as nobody was really using it. * Rename 'Syntax.indexInParent' to 'layoutIndexInParent' because it was confusing with 'SyntaxProtocol.indexInParent: SyntaxChildrenIndex'
1 parent eae92fe commit cd9009f

17 files changed

+496
-904
lines changed

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

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,6 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
4242
"""
4343
)
4444

45-
DeclSyntax(
46-
"""
47-
/// 'Syntax' object factory recycling 'Syntax.Info' instances.
48-
private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory()
49-
"""
50-
)
51-
5245
DeclSyntax(
5346
"""
5447
public init(viewMode: SyntaxTreeViewMode = .sourceAccurate) {
@@ -322,13 +315,13 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
322315
// with 'Syntax'
323316
var rewrittens: ContiguousArray<RetainedSyntaxArena> = []
324317
325-
for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) {
318+
for case let childDataRef? in node.layoutBuffer where viewMode.shouldTraverse(node: childDataRef.pointee.raw) {
326319
327320
// Build the Syntax node to rewrite
328-
var childNode = nodeFactory.create(parent: node, raw: child, absoluteInfo: info)
321+
var childNode = Syntax(arena: node.arena, dataRef: childDataRef)
329322
330323
dispatchVisit(&childNode)
331-
if childNode.raw.id != child.id {
324+
if childNode.raw.id != childDataRef.pointee.raw.id {
332325
// The node was rewritten, let's handle it
333326
334327
if newLayout.baseAddress == nil {
@@ -339,13 +332,10 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
339332
}
340333
341334
// Update the rewritten child.
342-
newLayout[Int(info.indexInParent)] = childNode.raw
335+
newLayout[Int(childDataRef.pointee.absoluteInfo.layoutIndexInParent)] = childNode.raw
343336
// Retain the syntax arena of the new node until it's wrapped with Syntax node.
344337
rewrittens.append(childNode.raw.arenaReference.retained)
345338
}
346-
347-
// Recycle 'childNode.info'
348-
nodeFactory.dispose(&childNode)
349339
}
350340
351341
if newLayout.baseAddress != nil {

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

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,6 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
3232
try! ClassDeclSyntax("open class SyntaxVisitor") {
3333
DeclSyntax("public let viewMode: SyntaxTreeViewMode")
3434

35-
DeclSyntax(
36-
"""
37-
/// 'Syntax' object factory recycling 'Syntax.Info' instances.
38-
private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory()
39-
"""
40-
)
41-
4235
DeclSyntax(
4336
"""
4437
public init(viewMode: SyntaxTreeViewMode) {
@@ -243,10 +236,9 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
243236
"""
244237
/// - Note: `node` is `inout` to avoid reference counting. See comment in `visitImpl`.
245238
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)
248-
visit(&childNode)
249-
nodeFactory.dispose(&childNode)
239+
for case let childDataRef? in node.layoutBuffer where viewMode.shouldTraverse(node: childDataRef.pointee.raw) {
240+
var child = Syntax(arena: node.arena, dataRef: childDataRef)
241+
visit(&child)
250242
}
251243
}
252244
"""

Sources/SwiftSyntax/AbsoluteRawSyntax.swift

Lines changed: 0 additions & 47 deletions
This file was deleted.

Sources/SwiftSyntax/AbsoluteSyntaxInfo.swift

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,59 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
struct AbsoluteSyntaxPosition: Sendable {
14-
/// The UTF-8 offset of the syntax node in the source file
15-
let offset: UInt32
16-
let indexInParent: UInt32
17-
18-
func advancedBySibling(_ raw: RawSyntax?) -> AbsoluteSyntaxPosition {
19-
let newOffset = self.offset + UInt32(truncatingIfNeeded: raw?.totalLength.utf8Length ?? 0)
20-
let newIndexInParent = self.indexInParent + 1
21-
return .init(offset: newOffset, indexInParent: newIndexInParent)
22-
}
23-
24-
func advancedToFirstChild() -> AbsoluteSyntaxPosition {
25-
return .init(offset: self.offset, indexInParent: 0)
26-
}
27-
28-
static var forRoot: AbsoluteSyntaxPosition {
29-
return .init(offset: 0, indexInParent: 0)
30-
}
31-
}
32-
3313
/// `AbsoluteSyntaxInfo` represents the information that relates a `RawSyntax`
3414
/// to a source file tree, like its absolute source offset.
3515
struct AbsoluteSyntaxInfo: Sendable {
36-
let position: AbsoluteSyntaxPosition
37-
let nodeId: SyntaxIdentifier
16+
/// Byte offset in the tree.
17+
let offset: UInt32
18+
19+
/// Index in parent's layout. Note that this counts `nil` children.
20+
let layoutIndexInParent: UInt32
3821

39-
/// The UTF-8 offset of the syntax node in the source file
40-
var offset: UInt32 { return position.offset }
41-
var indexInParent: UInt32 { return position.indexInParent }
22+
/// index of the node when traversing the syntax tree using a depth-first traversal.
23+
/// This skips `nil` children in the parent's layout.
24+
let indexInTree: UInt32
4225

4326
func advancedBySibling(_ raw: RawSyntax?) -> AbsoluteSyntaxInfo {
44-
let newPosition = position.advancedBySibling(raw)
45-
let newNodeId = nodeId.advancedBySibling(raw)
46-
return .init(position: newPosition, nodeId: newNodeId)
27+
if let raw {
28+
return AbsoluteSyntaxInfo(
29+
offset: offset &+ UInt32(truncatingIfNeeded: raw.totalLength.utf8Length),
30+
layoutIndexInParent: layoutIndexInParent &+ 1,
31+
indexInTree: indexInTree &+ UInt32(truncatingIfNeeded: raw.totalNodes)
32+
)
33+
} else {
34+
return AbsoluteSyntaxInfo(
35+
offset: offset,
36+
layoutIndexInParent: layoutIndexInParent &+ 1,
37+
indexInTree: indexInTree
38+
)
39+
}
4740
}
4841

4942
func advancedToFirstChild() -> AbsoluteSyntaxInfo {
50-
let newPosition = position.advancedToFirstChild()
51-
let newNodeId = nodeId.advancedToFirstChild()
52-
return .init(position: newPosition, nodeId: newNodeId)
43+
return AbsoluteSyntaxInfo(
44+
offset: offset,
45+
layoutIndexInParent: 0,
46+
indexInTree: indexInTree &+ 1
47+
)
5348
}
5449

5550
static func forRoot(_ raw: RawSyntax) -> AbsoluteSyntaxInfo {
56-
return .init(position: .forRoot, nodeId: .forRoot(raw))
51+
// These checks ensure the safety of the unchecked arithmetic operations in 'advancedBySibling(_:)'.
52+
precondition(raw.totalLength.utf8Length <= UInt32.max, "too long")
53+
precondition(raw.totalNodes <= UInt32.max, "too many nodes")
54+
return AbsoluteSyntaxInfo(
55+
offset: 0,
56+
layoutIndexInParent: 0,
57+
indexInTree: 0
58+
)
5759
}
5860
}

Sources/SwiftSyntax/CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
add_swift_syntax_library(SwiftSyntax
1010
AbsolutePosition.swift
11-
AbsoluteRawSyntax.swift
1211
AbsoluteSyntaxInfo.swift
1312
Assert.swift
1413
BumpPtrAllocator.swift
@@ -19,6 +18,7 @@ add_swift_syntax_library(SwiftSyntax
1918
Identifier.swift
2019
MemoryLayout.swift
2120
MissingNodeInitializers.swift
21+
PlatformMutex.swift
2222
SourceEdit.swift
2323
SourceLength.swift
2424
SourceLocation.swift
@@ -31,7 +31,6 @@ add_swift_syntax_library(SwiftSyntax
3131
SyntaxCollection.swift
3232
SyntaxHashable.swift
3333
SyntaxIdentifier.swift
34-
SyntaxNodeFactory.swift
3534
SyntaxNodeStructure.swift
3635
SyntaxProtocol.swift
3736
SyntaxText.swift
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022-2024 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+
#if compiler(>=6.0)
14+
#if canImport(Darwin)
15+
private import Darwin
16+
#elseif canImport(Glibc)
17+
private import Glibc
18+
#elseif canImport(Bionic)
19+
private import Bionic
20+
#elseif canImport(Musl)
21+
private import Musl
22+
#elseif canImport(WinSDK)
23+
private import WinSDK
24+
#endif
25+
#else
26+
#if canImport(Darwin)
27+
import Darwin
28+
#elseif canImport(Glibc)
29+
import Glibc
30+
#elseif canImport(Bionic)
31+
import Bionic
32+
#elseif canImport(Musl)
33+
import Musl
34+
#elseif canImport(WinSDK)
35+
import WinSDK
36+
#endif
37+
#endif
38+
39+
/// A protocol that platform-specific mutual exclusion primitives should conform to.
40+
final class PlatformMutex {
41+
private typealias PlatformLock = UnsafeMutablePointer<Primitive>
42+
private let lock: PlatformLock
43+
44+
/// Allocate memory for, and initialize, the mutex in a platform-specific fashion.
45+
init() {
46+
let storage = PlatformLock.allocate(capacity: 1)
47+
storage.initialize(to: Primitive())
48+
self.lock = storage
49+
Self.initialize(self.lock)
50+
}
51+
52+
/// Deinitialize and deallocate the memory associated with the mutex.
53+
deinit {
54+
Self.deinitialize(self.lock)
55+
self.lock.deinitialize(count: 1)
56+
self.lock.deallocate()
57+
}
58+
59+
/// Invoke the provided block under the protection of the mutex.
60+
func withGuard<T>(body: () throws -> T) rethrows -> T {
61+
Self.lock(self.lock)
62+
defer { Self.unlock(self.lock) }
63+
return try body()
64+
}
65+
}
66+
67+
#if canImport(Darwin)
68+
extension PlatformMutex {
69+
private typealias Primitive = os_unfair_lock
70+
71+
private static func initialize(_ platformLock: PlatformLock) {
72+
// Platform specific initialization is not needed.
73+
}
74+
private static func deinitialize(_ platformLock: PlatformLock) {
75+
// Platform specific deinitialization is not needed.
76+
}
77+
private static func lock(_ platformLock: PlatformLock) {
78+
os_unfair_lock_lock(platformLock)
79+
}
80+
private static func unlock(_ platformLock: PlatformLock) {
81+
os_unfair_lock_unlock(platformLock)
82+
}
83+
}
84+
85+
#elseif canImport(Glibc) || canImport(Bionic) || canImport(Musl)
86+
extension PlatformMutex {
87+
private typealias Primitive = pthread_mutex_t
88+
89+
private static func initialize(_ platformLock: PlatformLock) {
90+
let result = pthread_mutex_init(platformLock, nil)
91+
precondition(result == 0)
92+
}
93+
private static func deinitialize(_ platformLock: PlatformLock) {
94+
let result = pthread_mutex_destroy(self.lock)
95+
precondition(result == 0)
96+
}
97+
private static func lock(_ platformLock: PlatformLock) {
98+
let result = pthread_mutex_lock(platformLock)
99+
assert(result == 0)
100+
}
101+
private static func unlock(_ platformLock: PlatformLock) {
102+
let result = pthread_mutex_unlock(platformLock)
103+
assert(result == 0)
104+
}
105+
}
106+
107+
#elseif canImport(WinSDK)
108+
extension PlatformMutex {
109+
private typealias Primitive = SRWLOCK
110+
111+
private static func initialize(_ platformLock: PlatformLock) {
112+
InitializeSRWLock(platformLock)
113+
}
114+
private static func deinitialize(_ platformLock: PlatformLock) {
115+
// Platform specific deinitialization is not needed.
116+
}
117+
private static func lock(_ platformLock: PlatformLock) {
118+
AcquireSRWLockExclusive(platformLock)
119+
}
120+
private static func unlock(_ platformLock: PlatformLock) {
121+
ReleaseSRWLockExclusive(platformLock)
122+
}
123+
}
124+
125+
#endif

0 commit comments

Comments
 (0)