Skip to content

Commit a47bd70

Browse files
committed
[libSyntax] Don't create dedicated deferred nodes in SyntaxTreeCreator
We have finally reached our goal of optimising deferred node creation for SyntaxTreeCreator. Instead of creating dedicated deferred nodes and copying the data into a RawSyntax node when recording, we always create RawSyntax nodes. Recording a deferred node is thus a no-op, since we have already created a RawSyntax node. Should a deferred node not be recorded, it stays alive in the SyntaxArena without any reference to it. While this means, we are leaking some memory for such nodes, most nodes do get recorded, so the overhead should be fine compared to the performance benefit.
1 parent beadd4a commit a47bd70

File tree

7 files changed

+294
-209
lines changed

7 files changed

+294
-209
lines changed

include/swift/Parse/SyntaxParseActions.h

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@ struct DeferredNodeInfo {
8282
// MARK: - SyntaxParseActions
8383

8484
class SyntaxParseActions {
85-
llvm::BumpPtrAllocator DeferredNodeAllocator;
86-
8785
virtual void _anchor();
8886

8987
public:
@@ -111,24 +109,24 @@ class SyntaxParseActions {
111109
StringRef leadingTrivia,
112110
StringRef trailingTrivia,
113111
CharSourceRange range,
114-
bool isMissing);
112+
bool isMissing) = 0;
115113

116114
/// Create a deferred layout node that may or may not be recorded later using
117115
/// \c recordDeferredLayout. The \c SyntaxParseAction is responsible for
118116
/// keeping the deferred token alive until it is destructed.
119117
virtual OpaqueSyntaxNode
120118
makeDeferredLayout(syntax::SyntaxKind k, bool isMissing,
121-
const ArrayRef<RecordedOrDeferredNode> &children);
119+
const ArrayRef<RecordedOrDeferredNode> &children) = 0;
122120

123121
/// Record a deferred token node that was previously created using \c
124122
/// makeDeferredToken. The deferred data will never be used again, so it can
125123
/// be destroyed by this method. Note that not all deferred nodes will be
126124
/// recorded and that pending deferred nodes need to be freed when the \c
127125
/// SyntaxParseActions is destructed.
128-
virtual OpaqueSyntaxNode recordDeferredToken(OpaqueSyntaxNode deferred);
126+
virtual OpaqueSyntaxNode recordDeferredToken(OpaqueSyntaxNode deferred) = 0;
129127

130128
/// Record a deferred layout node. See recordDeferredToken.
131-
virtual OpaqueSyntaxNode recordDeferredLayout(OpaqueSyntaxNode deferred);
129+
virtual OpaqueSyntaxNode recordDeferredLayout(OpaqueSyntaxNode deferred) = 0;
132130

133131
/// Since most data of \c ParsedRawSyntax is described as opaque data, the
134132
/// \c ParsedRawSyntax node needs to reach out to the \c SyntaxParseAction,
@@ -140,11 +138,11 @@ class SyntaxParseActions {
140138
/// node starts at \p StartLoc.
141139
virtual DeferredNodeInfo getDeferredChild(OpaqueSyntaxNode node,
142140
size_t childIndex,
143-
SourceLoc startLoc);
141+
SourceLoc startLoc) = 0;
144142

145143
/// Return the number of children, \p node has. These can be retrieved using
146144
/// \c getDeferredChild.
147-
virtual size_t getDeferredNumChildren(OpaqueSyntaxNode node);
145+
virtual size_t getDeferredNumChildren(OpaqueSyntaxNode node) = 0;
148146

149147
/// Attempt to realize an opaque raw syntax node for a source file into a
150148
/// SourceFileSyntax node. This will return \c None if the parsing action

include/swift/SyntaxParse/SyntaxTreeCreator.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class SourceFileSyntax;
3333
///
3434
/// It also handles caching re-usable RawSyntax objects and skipping parsed
3535
/// nodes via consulting a \c SyntaxParsingCache.
36-
class SyntaxTreeCreator: public SyntaxParseActions {
36+
class SyntaxTreeCreator final : public SyntaxParseActions {
3737
SourceManager &SM;
3838
unsigned BufferID;
3939
RC<syntax::SyntaxArena> Arena;
@@ -48,6 +48,8 @@ class SyntaxTreeCreator: public SyntaxParseActions {
4848
/// tree.
4949
SyntaxParsingCache *SyntaxCache;
5050

51+
llvm::BumpPtrAllocator ScratchAlloc;
52+
5153
public:
5254
SyntaxTreeCreator(SourceManager &SM, unsigned bufferID,
5355
SyntaxParsingCache *syntaxCache,
@@ -70,6 +72,23 @@ class SyntaxTreeCreator: public SyntaxParseActions {
7072

7173
std::pair<size_t, OpaqueSyntaxNode>
7274
lookupNode(size_t lexerOffset, syntax::SyntaxKind kind) override;
75+
76+
OpaqueSyntaxNode makeDeferredToken(tok tokenKind, StringRef leadingTrivia,
77+
StringRef trailingTrivia,
78+
CharSourceRange range,
79+
bool isMissing) override;
80+
81+
OpaqueSyntaxNode
82+
makeDeferredLayout(syntax::SyntaxKind k, bool IsMissing,
83+
const ArrayRef<RecordedOrDeferredNode> &children) override;
84+
85+
OpaqueSyntaxNode recordDeferredToken(OpaqueSyntaxNode deferred) override;
86+
OpaqueSyntaxNode recordDeferredLayout(OpaqueSyntaxNode deferred) override;
87+
88+
DeferredNodeInfo getDeferredChild(OpaqueSyntaxNode node, size_t ChildIndex,
89+
SourceLoc StartLoc) override;
90+
91+
size_t getDeferredNumChildren(OpaqueSyntaxNode node) override;
7392
};
7493

7594
} // end namespace swift

lib/Parse/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ add_swift_host_library(swiftParse STATIC
2222
ParseStmt.cpp
2323
ParseType.cpp
2424
PersistentParserState.cpp
25-
SyntaxParseActions.cpp
2625
SyntaxParsingCache.cpp
2726
SyntaxParsingContext.cpp)
2827
_swift_gyb_target_sources(swiftParse PRIVATE

lib/Parse/SyntaxParseActions.cpp

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

lib/SyntaxParse/SyntaxTreeCreator.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,92 @@ SyntaxTreeCreator::lookupNode(size_t lexerOffset, syntax::SyntaxKind kind) {
164164
size_t length = raw->getTextLength();
165165
return {length, static_cast<OpaqueSyntaxNode>(raw)};
166166
}
167+
168+
OpaqueSyntaxNode SyntaxTreeCreator::makeDeferredToken(tok tokenKind,
169+
StringRef leadingTrivia,
170+
StringRef trailingTrivia,
171+
CharSourceRange range,
172+
bool isMissing) {
173+
// Instead of creating dedicated deferred nodes that will be recorded only if
174+
// needed, the SyntaxTreeCreator always records all nodes and forms RawSyntax
175+
// nodes for them. This eliminates a bunch of copies that would otherwise
176+
// be required to record the deferred nodes.
177+
// Should a deferred node not be recorded, its data stays alive in the
178+
// SyntaxArena. This causes a small memory leak but since most nodes are
179+
// being recorded, it is acceptable.
180+
if (isMissing) {
181+
auto Node = recordMissingToken(tokenKind, range.getStart());
182+
return Node;
183+
} else {
184+
auto Node = recordToken(tokenKind, leadingTrivia, trailingTrivia, range);
185+
return Node;
186+
}
187+
}
188+
189+
OpaqueSyntaxNode SyntaxTreeCreator::makeDeferredLayout(
190+
syntax::SyntaxKind k, bool IsMissing,
191+
const ArrayRef<RecordedOrDeferredNode> &children) {
192+
SmallVector<OpaqueSyntaxNode, 16> opaqueChildren;
193+
opaqueChildren.reserve(children.size());
194+
195+
for (size_t i = 0; i < children.size(); ++i) {
196+
opaqueChildren.push_back(children[i].getOpaque());
197+
}
198+
199+
// Also see comment in makeDeferredToken
200+
return recordRawSyntax(k, opaqueChildren);
201+
}
202+
203+
OpaqueSyntaxNode
204+
SyntaxTreeCreator::recordDeferredToken(OpaqueSyntaxNode deferred) {
205+
// We don't diffirentiate between deferred and recorded nodes. See comment in
206+
// makeDeferredToken.
207+
return deferred;
208+
}
209+
210+
OpaqueSyntaxNode
211+
SyntaxTreeCreator::recordDeferredLayout(OpaqueSyntaxNode deferred) {
212+
// We don't diffirentiate between deferred and recorded nodes. See comment in
213+
// makeDeferredToken.
214+
return deferred;
215+
}
216+
217+
DeferredNodeInfo SyntaxTreeCreator::getDeferredChild(OpaqueSyntaxNode node,
218+
size_t ChildIndex,
219+
SourceLoc StartLoc) {
220+
const RawSyntax *raw = static_cast<const RawSyntax *>(node);
221+
222+
// Compute the start offset of the child node by advancing StartLoc by the
223+
// length of all previous child nodes.
224+
for (unsigned i = 0; i < ChildIndex; ++i) {
225+
const RawSyntax *child = raw->getChild(i);
226+
if (child) {
227+
StartLoc = StartLoc.getAdvancedLoc(child->getTextLength());
228+
}
229+
}
230+
231+
const RawSyntax *Child = raw->getChild(ChildIndex);
232+
if (Child == nullptr) {
233+
return DeferredNodeInfo(
234+
RecordedOrDeferredNode(nullptr, RecordedOrDeferredNode::Kind::Null),
235+
syntax::SyntaxKind::Unknown, tok::NUM_TOKENS, /*IsMissing=*/false,
236+
CharSourceRange(StartLoc, /*Length=*/0));
237+
} else if (Child->isToken()) {
238+
return DeferredNodeInfo(
239+
RecordedOrDeferredNode(Child,
240+
RecordedOrDeferredNode::Kind::DeferredToken),
241+
syntax::SyntaxKind::Token, Child->getTokenKind(), Child->isMissing(),
242+
CharSourceRange(StartLoc, Child->getTextLength()));
243+
} else {
244+
return DeferredNodeInfo(
245+
RecordedOrDeferredNode(Child,
246+
RecordedOrDeferredNode::Kind::DeferredLayout),
247+
Child->getKind(), tok::NUM_TOKENS,
248+
/*IsMissing=*/false, CharSourceRange(StartLoc, Child->getTextLength()));
249+
}
250+
}
251+
252+
size_t SyntaxTreeCreator::getDeferredNumChildren(OpaqueSyntaxNode node) {
253+
const syntax::RawSyntax *raw = static_cast<const syntax::RawSyntax *>(node);
254+
return raw->getNumChildren();
255+
}

0 commit comments

Comments
 (0)