From 4753414407b49336df368400293e27de30b129e5 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 3 Oct 2019 10:40:48 -0700 Subject: [PATCH] Move implementation of each case in SyntaxRewriter.doVisit to separate function In debug builds the compiler lays the stack space for each case statement out next to each other. This causes a call to doVisit(_:_:) to use ~50KB of stack space, quickly resulting in a stack overflow. Moving each implementation into its own function causes the each case to not use any stack space, circumventing the issue. Fixes SR-11170 --- Sources/SwiftSyntax/SyntaxRewriter.swift.gyb | 50 +++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/Sources/SwiftSyntax/SyntaxRewriter.swift.gyb b/Sources/SwiftSyntax/SyntaxRewriter.swift.gyb index b7776d80680..b76032b0511 100644 --- a/Sources/SwiftSyntax/SyntaxRewriter.swift.gyb +++ b/Sources/SwiftSyntax/SyntaxRewriter.swift.gyb @@ -263,6 +263,32 @@ extension Syntax { } } +% for node in SYNTAX_NODES: +/// Implementation detail of doVisit(_:_:). Do not call directly. +private func _doVisitImpl${node.name}( + _ data: SyntaxData, _ visitor: inout Visitor +) where Visitor : SyntaxVisitor { +% if node.is_base(): + let node = Unknown${node.name}(data) + let needsChildren = (visitor.visit(node) == .visitChildren) + // Avoid casting to `_SyntaxBase` if we don't need to visit children. + if needsChildren && data.raw.numberOfChildren > 0 { + visitChildren(data, parent: node, &visitor) + } + visitor.visitPost(node) +% else: + let node = ${node.name}(data) + let needsChildren = (visitor.visit(node) == .visitChildren) + // Avoid casting to `_SyntaxBase` if we don't need to visit children. + if needsChildren && data.raw.numberOfChildren > 0 { + visitChildren(data, parent: node, &visitor) + } + visitor.visitPost(node) +% end +} + +% end + fileprivate func doVisit( _ data: SyntaxData, _ visitor: inout Visitor ) where Visitor : SyntaxVisitor { @@ -276,31 +302,19 @@ fileprivate func doVisit( visitor.visitPost(node) case .unknown: let node = UnknownSyntax(data) - let needsChildren = visitor.visit(node) == .visitChildren + let needsChildren = (visitor.visit(node) == .visitChildren) // Avoid casting to `_SyntaxBase` if we don't need to visit children. if needsChildren && data.raw.numberOfChildren > 0 { visitChildren(data, parent: node, &visitor) } visitor.visitPost(node) + // The implementation of every generated case goes into its own function. This + // circumvents an issue where the compiler allocates stack space for every + // case statement next to each other in debug builds, causing it to allocate + // ~50KB per call to this function. rdar://55929175 % for node in SYNTAX_NODES: case .${node.swift_syntax_kind}: -% if node.is_base(): - let node = Unknown${node.name}(data) - let needsChildren = visitor.visit(node) == .visitChildren - // Avoid casting to `_SyntaxBase` if we don't need to visit children. - if needsChildren && data.raw.numberOfChildren > 0 { - visitChildren(data, parent: node, &visitor) - } - visitor.visitPost(node) -% else: - let node = ${node.name}(data) - let needsChildren = visitor.visit(node) == .visitChildren - // Avoid casting to `_SyntaxBase` if we don't need to visit children. - if needsChildren && data.raw.numberOfChildren > 0 { - visitChildren(data, parent: node, &visitor) - } - visitor.visitPost(node) -% end + _doVisitImpl${node.name}(data, &visitor) % end } }