From 243ae043c882ca578e36febcc2e10f60933cc3a8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 Jul 2020 19:11:31 +0200 Subject: [PATCH 1/6] Make disambiguate an inline function Avoids creation of a closure in a hotspot (staticRef) --- compiler/src/dotty/tools/dotc/core/Denotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index c20be8ee252f..a9eb9f56caaf 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -277,7 +277,7 @@ object Denotations { * single-denotations that do not satisfy the predicate are left alone * (whereas suchThat would map them to NoDenotation). */ - def disambiguate(p: Symbol => Boolean)(using Context): SingleDenotation = this match { + inline def disambiguate(inline p: Symbol => Boolean)(using Context): SingleDenotation = this match { case sdenot: SingleDenotation => sdenot case mdenot => suchThat(p) orElse NoQualifyingRef(alternatives) } From 9149e6b58ed2c268a58e274ad83c75a91119d5e0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 Jul 2020 19:11:52 +0200 Subject: [PATCH 2/6] Avoid hot closure creation in sourceVersion --- compiler/src/dotty/tools/dotc/config/Feature.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index ed804ff4a7af..12665f88dc2c 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -68,7 +68,9 @@ object Feature: def sourceVersion(using Context): SourceVersion = if ctx.compilationUnit == null then sourceVersionSetting - else ctx.compilationUnit.sourceVersion.getOrElse(sourceVersionSetting) + else ctx.compilationUnit.sourceVersion match + case Some(v) => v + case none => sourceVersionSetting def migrateTo3(using Context): Boolean = sourceVersion == `3.0-migration` || enabledBySetting(nme.Scala2Compat) From 1b998450cad0219218d4536ad0a1612693996124 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 Jul 2020 19:12:13 +0200 Subject: [PATCH 3/6] Avoid hot closure creation in TreeAccumulator --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 922229d1c4b7..ce3ec4ca3700 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1375,7 +1375,12 @@ object Trees { // Ties the knot of the traversal: call `foldOver(x, tree))` to dive in the `tree` node. def apply(x: X, tree: Tree)(using Context): X - def apply(x: X, trees: Traversable[Tree])(using Context): X = trees.foldLeft(x)(apply) + def apply(x: X, trees: List[Tree])(using Context): X = trees match + case tree :: rest => + apply(apply(x, tree), rest) + case Nil => + x + def foldOver(x: X, tree: Tree)(using Context): X = if (tree.source != ctx.source && tree.source.exists) foldOver(x, tree)(using ctx.withSource(tree.source)) From 9f0dd66799523a3ffc6c1cfa1903a0b522a17f6e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 24 Jul 2020 10:02:49 +0200 Subject: [PATCH 4/6] Make explore an inline method This avoids both the closure creation, and the boxing if the result is primitive. --- .../src/dotty/tools/dotc/core/Contexts.scala | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index d75a60255003..5ef96561b8d9 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -680,11 +680,8 @@ object Contexts { final def retractMode(mode: Mode): c.type = c.setMode(c.mode &~ mode) } - /** Test `op` in a fresh context with a typerstate that is not committable. - * The passed context may not survive the operation. - */ - def explore[T](op: Context ?=> T)(using Context): T = - util.Stats.record("Context.test") + private def exploreCtx(using Context): Context = + util.Stats.record("explore") val base = ctx.base import base._ val nestedCtx = @@ -700,13 +697,15 @@ object Contexts { exploresInUse += 1 val nestedTS = nestedCtx.typerState nestedTS.init(ctx.typerState, ctx.typerState.constraint) - val result = - try op(using nestedCtx) - finally - nestedTS.reporter.asInstanceOf[ExploringReporter].reset() - exploresInUse -= 1 - result - end explore + nestedCtx + + private def wrapUpExplore(ectx: Context) = + ectx.reporter.asInstanceOf[ExploringReporter].reset() + ectx.base.exploresInUse -= 1 + + inline def explore[T](inline op: Context ?=> T)(using Context): T = + val ectx = exploreCtx + try op(using ectx) finally wrapUpExplore(ectx) /** The type comparer of the kind created by `maker` to be used. * This is the currently active type comparer CMP if @@ -716,6 +715,7 @@ object Contexts { * In other words: tracking or explaining is a sticky property in the same context. */ private def comparer(using Context): TypeComparer = + util.Stats.record("comparing") val base = ctx.base if base.comparersInUse > 0 && (base.comparers(base.comparersInUse - 1).comparerContext eq ctx) From c5ce6be55a1c5f67de585d8b925bc14da777d16f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 24 Jul 2020 10:03:20 +0200 Subject: [PATCH 5/6] Avoid closure creation in Thicket.span --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index ce3ec4ca3700..c422642879af 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -846,7 +846,11 @@ object Trees { override def isEmpty: Boolean = trees.isEmpty override def toList: List[Tree[T]] = flatten(trees) override def toString: String = if (isEmpty) "EmptyTree" else "Thicket(" + trees.mkString(", ") + ")" - override def span: Span = trees.foldLeft(NoSpan) ((span, t) => span union t.span) + override def span: Span = + def combine(s: Span, ts: List[Tree[T]]): Span = ts match + case t :: ts1 => combine(s.union(t.span), ts1) + case nil => s + combine(NoSpan, trees) override def withSpan(span: Span): this.type = mapElems(_.withSpan(span)).asInstanceOf[this.type] From 0fc0c0c89e4378ef5375ff73afbd25d24a928199 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 24 Jul 2020 10:03:44 +0200 Subject: [PATCH 6/6] Avoid withFilter in some warm spots --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 0aa3b65437fa..f11a27c43530 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -10,6 +10,7 @@ import reporting.trace import dotty.tools.dotc.transform.SymUtils._ import Decorators._ import Constants.Constant +import scala.collection.mutable import scala.annotation.tailrec @@ -698,7 +699,10 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => /** The symbols defined locally in a statement list */ def localSyms(stats: List[Tree])(using Context): List[Symbol] = - for (stat <- stats if stat.isDef && stat.symbol.exists) yield stat.symbol + val locals = new mutable.ListBuffer[Symbol] + for stat <- stats do + if stat.isDef && stat.symbol.exists then locals += stat.symbol + locals.toList /** If `tree` is a DefTree, the symbol defined by it, otherwise NoSymbol */ def definedSym(tree: Tree)(using Context): Symbol =