Skip to content

Reduce allocations in TypingTransformer based phases #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: 2.13.x
Choose a base branch
from
4 changes: 2 additions & 2 deletions src/compiler/scala/tools/nsc/transform/Constructors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
private val guardedCtorStats: mutable.Map[Symbol, List[Tree]] = perRunCaches.newMap[Symbol, List[Tree]]()
private val ctorParams: mutable.Map[Symbol, List[Symbol]] = perRunCaches.newMap[Symbol, List[Symbol]]()

class ConstructorTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
class ConstructorTransformer(unit: CompilationUnit) extends LightTypingTransformer(unit) {
/*
* Inspect for obvious out-of-order initialization; concrete, eager vals or vars, declared in this class,
* for which a reference to the member precedes its definition.
Expand Down Expand Up @@ -452,7 +452,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
} // GuardianOfCtorStmts

private class TemplateTransformer(val unit: CompilationUnit, val impl: Template)
extends TypingTransformer(unit)
extends LightTypingTransformer(unit)
with StaticsTransformer
with DelayedInitHelper
with OmittablesHelper
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/transform/Delambdafy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
protected def newTransformer(unit: CompilationUnit): Transformer =
new DelambdafyTransformer(unit)

class DelambdafyTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
class DelambdafyTransformer(unit: CompilationUnit) extends LightTypingTransformer(unit) {
// we need to know which methods refer to the 'this' reference so that we can determine which lambdas need access to it
// TODO: this looks expensive, so I made it a lazy val. Can we make it more pay-as-you-go / optimize for common shapes?
private[this] lazy val methodReferencesThis: Set[Symbol] =
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ abstract class ExplicitOuter extends InfoTransform
* values for outer parameters of constructors.
* The class provides methods for referencing via outer.
*/
abstract class OuterPathTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
abstract class OuterPathTransformer(unit: CompilationUnit) extends LightTypingTransformer(unit) {
/** The directly enclosing outer parameter, if we are in a constructor */
protected var outerParam: Symbol = NoSymbol

Expand Down
4 changes: 2 additions & 2 deletions src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
stpe
}

class Extender(unit: CompilationUnit) extends TypingTransformer(unit) {
class Extender(unit: CompilationUnit) extends LightTypingTransformer(unit) {
private val extensionDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]()

@tailrec
Expand Down Expand Up @@ -260,7 +260,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
}

final class SubstututeRecursion(origMeth: Symbol, extensionMeth: Symbol,
unit: CompilationUnit) extends TypingTransformer(unit) {
unit: CompilationUnit) extends LightTypingTransformer(unit) {
override def transform(tree: Tree): Tree = tree match {
// scala/bug#6574 Rewrite recursive calls against the extension method so they can
// be tail call optimized later. The tailcalls phases comes before
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/transform/PostErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ trait PostErasure extends InfoTransform with TypingTransformers with scala.refle
def newTransformer(unit: CompilationUnit): Transformer = new PostErasureTransformer(unit)
override def changesBaseClasses = false

class PostErasureTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
class PostErasureTransformer(unit: CompilationUnit) extends LightTypingTransformer(unit) {
override def transform(tree: Tree) = {
def finish(res: Tree) = logResult(s"Posterasure reduction\n Old: $tree\n New")(res)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1472,7 +1472,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
&& originalClass(clazz).parentSymbolsIterator.exists(p => hasSpecializedParams(p) && !p.isTrait)
)

class SpecializationTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
class SpecializationTransformer(unit: CompilationUnit) extends LightTypingTransformer(unit) {

override def transformUnit(unit: CompilationUnit): Unit = if (!settings.nospecialization) {
informProgress("specializing " + unit)
Expand Down
37 changes: 37 additions & 0 deletions src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,42 @@ trait TypingTransformers {
}
}
}

// Like TypingTransfomer, but mutates `Context` rather than creating new when recursing into each new owner.
abstract class LightTypingTransformer(unit: CompilationUnit) extends Transformer {
var localTyper: analyzer.Typer =
if (phase.erasedTypes)
erasure.newTyper(erasure.rootContextPostTyper(unit, EmptyTree)).asInstanceOf[analyzer.Typer]
else // TODO: AM: should some phases use a regular rootContext instead of a post-typer one??
analyzer.newTyper(analyzer.rootContextPostTyper(unit, EmptyTree))
protected var curTree: Tree = _

override final def atOwner[A](owner: Symbol)(trans: => A): A = atOwner(curTree, owner)(trans)

def atOwner[A](tree: Tree, owner: Symbol)(trans: => A): A = {
val context = localTyper.context
val savedOwner = context.owner
val savedTree = context.tree
context.tree = tree
context.owner = if (owner.isModuleNotMethod) owner.moduleClass else owner
val result = super.atOwner(owner)(trans)
context.tree = savedTree
context.owner = savedOwner
result
}

override def transform(tree: Tree): Tree = {
curTree = tree
tree match {
case Template(_, _, _) =>
// enter template into context chain
atOwner(currentOwner) { tree.transform(this) }
case PackageDef(_, _) =>
atOwner(tree.symbol) { tree.transform(this) }
case _ =>
tree.transform(this)
}
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ trait PatternMatching extends Transform

def newTransformer(unit: CompilationUnit): Transformer = new MatchTransformer(unit)

class MatchTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
class MatchTransformer(unit: CompilationUnit) extends LightTypingTransformer(unit) {
override def transform(tree: Tree): Tree = tree match {
case Match(sel, cases) =>
val origTp = tree.tpe
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/typechecker/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ trait Contexts { self: Analyzer =>
* @param scope The current scope
* @param _outer The next outer context.
*/
class Context private[typechecker](val tree: Tree, val owner: Symbol, val scope: Scope,
class Context private[typechecker](var tree: Tree, var owner: Symbol, val scope: Scope,
val unit: CompilationUnit, _outer: Context,
private[this] var _reporter: ContextReporter = new ThrowingReporter) {
private def outerIsNoContext = _outer eq null
Expand Down