From 7b32a2c1d3e4c96548e661585c026b4802d00123 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 21 Feb 2019 11:48:04 +1000 Subject: [PATCH 1/8] Avoid phase.flatclasses check in hot code paths --- .../scala/reflect/internal/Scopes.scala | 25 ++++++++++++++++--- .../scala/reflect/internal/Symbols.scala | 4 +-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index a893db74f2c3..aed13752aeca 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -309,6 +309,19 @@ trait Scopes extends api.Scopes { self: SymbolTable => null } + private def hasName(sym: Symbol, name: Name, phaseFlatClasses: Boolean) = { + if (phaseFlatClasses) + sym.name eq name + else + sym.rawname eq name // opt: avoid calls to Symbol.needsFlatClasses inside the loops below + } + private def hasSameName(sym1: Symbol, sym2: Symbol, phaseFlatClasses: Boolean) = { + if (phaseFlatClasses) + sym1.name eq sym2.name + else + sym1.rawname eq sym2.rawname // opt: avoid calls to Symbol.needsFlatClasses inside the loops below + } + /** lookup a symbol entry matching given name. * @note from Martin: I believe this is a hotspot or will be one * in future versions of the type system. I have reverted the previous @@ -317,14 +330,16 @@ trait Scopes extends api.Scopes { self: SymbolTable => def lookupEntry(name: Name): ScopeEntry = { val startTime = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.scopeLookupTime) else null var e: ScopeEntry = null + val phaseFlatClasses = phase.flatClasses + if (hashtable ne null) { e = hashtable(name.start & HASHMASK) - while ((e ne null) && (e.sym.name ne name)) { + while ((e ne null) && !hasName(e.sym, name, phaseFlatClasses)) { e = e.tail } } else { e = elems - while ((e ne null) && (e.sym.name ne name)) { + while ((e ne null) && !hasName(e.sym, name, phaseFlatClasses)) { e = e.next } } @@ -339,10 +354,12 @@ trait Scopes extends api.Scopes { self: SymbolTable => */ def lookupNextEntry(entry: ScopeEntry): ScopeEntry = { var e = entry + val phaseFlatClasses = phase.flatClasses + if (hashtable ne null) - do { e = e.tail } while ((e ne null) && e.sym.name != entry.sym.name) + do { e = e.tail } while ((e ne null) && !hasSameName(e.sym, entry.sym, phaseFlatClasses)) else - do { e = e.next } while ((e ne null) && e.sym.name != entry.sym.name) + do { e = e.next } while ((e ne null) && !hasSameName(e.sym, entry.sym, phaseFlatClasses)) e } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 760199ec3560..216679bcc57d 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -2879,8 +2879,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def isLocalDummy = nme.isLocalDummyName(name) - override def isClassConstructor = name == nme.CONSTRUCTOR - override def isMixinConstructor = name == nme.MIXIN_CONSTRUCTOR + override def isClassConstructor = rawname == nme.CONSTRUCTOR + override def isMixinConstructor = rawname == nme.MIXIN_CONSTRUCTOR override def isConstructor = isClassConstructor || isMixinConstructor override def isPackageObject = isModule && (name == nme.PACKAGE) From 0459c685b827c473cc6b0ef640f88c5ef8c85475 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 21 Feb 2019 14:11:44 +1000 Subject: [PATCH 2/8] . --- src/reflect/scala/reflect/internal/Definitions.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 4bfed9156a0e..7cb0b3a0890a 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -661,6 +661,9 @@ trait Definitions extends api.StandardDefinitions { // add initialization from its generic class constructor val genericName = nme.unspecializedName(sym.name) val member = sym.owner.info.decl(genericName.toTypeName) + if (member.isOverloaded) + abort("Unexpected overloader: " + member.alternatives.map(_.debugLocationString)) + member } else sym From d0237bb539798a70be9f34d322b3523e3304ee99 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 21 Feb 2019 14:45:15 +1000 Subject: [PATCH 3/8] Optimize formalTypes --- .../scala/tools/nsc/typechecker/Infer.scala | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 0d84ecebb76a..6d6c0898c2c8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -50,11 +50,23 @@ trait Infer extends Checkable { && isVarArgTypes(formals1) ) def lastType = formals1.last.dealiasWiden.typeArgs.head - def expanded(n: Int) = (1 to n).toList map (_ => lastType) - if (expandLast) - formals1.init ::: expanded(numArgs - numFormals + 1) - else + if (expandLast) { + // Optimized version of: formals1.init ::: expanded(numArgs - numFormals + 1) + val result = mutable.ListBuffer[Type]() + var fs = formals + while ((fs ne Nil) && (fs.tail ne Nil)) { + result.addOne(fs.head) + fs = fs.tail + } + var i = 0 + val n = numArgs - numFormals + 1 + while (i < n) { + result += lastType + i += 1 + } + result.toList + } else formals1 } From 3794d59cf07ec3d5ef47c8ade6c5623318d0922c Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 21 Feb 2019 15:00:56 +1000 Subject: [PATCH 4/8] Avoid boxing in some settings queries --- src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala | 2 +- src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index c9f340745d19..2dffdeb41bd1 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -645,7 +645,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic { // without it. This is particularly bad because the availability of // generic information could disappear as a consequence of a seemingly // unrelated change. - settings.Ynogenericsig + settings.Ynogenericsig.value || sym.isArtifact || sym.isLiftedMethod || sym.isBridge diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 80de9c229c53..87190461197f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -480,7 +480,7 @@ trait TypeDiagnostics { } def apply(context: Context, tree: Tree): Tree = { - if (settings.warnDeadCode && context.unit.exists && treeOK(tree) && !context.contextMode.inAny(ContextMode.SuppressDeadArgWarning)) + if (settings.warnDeadCode.value && context.unit.exists && treeOK(tree) && !context.contextMode.inAny(ContextMode.SuppressDeadArgWarning)) context.warning(tree.pos, "dead code following this construct") tree } From c4fc2e7e9fbae46fe6a8eb7384c6de15946b58cb Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 21 Feb 2019 15:02:08 +1000 Subject: [PATCH 5/8] Remove nodeByCount statistic --- src/compiler/scala/tools/nsc/Global.scala | 2 +- src/reflect/scala/reflect/internal/Trees.scala | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 45d2b752c2ca..da77f2bda8bc 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1655,7 +1655,7 @@ class Global(var currentSettings: Settings, reporter0: LegacyReporter) } private val hotCounters = - List(statistics.retainedCount, statistics.retainedByType, statistics.nodeByType) + List(statistics.retainedCount, statistics.retainedByType) private val parserStats = { import statistics.treeNodeCount if (settings.YhotStatisticsEnabled) treeNodeCount :: hotCounters diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 3b8628488003..a1694c05ef3b 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -47,9 +47,6 @@ trait Trees extends api.Trees { val id = nodeCount // TODO: add to attachment? nodeCount += 1 - if (StatisticsStatics.areSomeHotStatsEnabled()) - statistics.incCounter(statistics.nodeByType, getClass) - final override def pos: Position = rawatt.pos private[this] var rawtpe: Type = _ @@ -2053,7 +2050,6 @@ trait TreesStats { self: Statistics => val symbolTable: SymbolTable val treeNodeCount = newView("#created tree nodes")(symbolTable.nodeCount) - val nodeByType = newByClass("#created tree nodes by type")(newCounter("")) val retainedCount = newCounter("#retained tree nodes") val retainedByType = newByClass("#retained tree nodes by type")(newCounter("")) } From 6457fa0e530d5742514698f499c8564bf7b44c1a Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 21 Feb 2019 15:03:45 +1000 Subject: [PATCH 6/8] Remove scope statistic --- src/reflect/scala/reflect/internal/Scopes.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index aed13752aeca..b24702da475b 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -328,7 +328,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => * change to use iterators as too costly. */ def lookupEntry(name: Name): ScopeEntry = { - val startTime = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.scopeLookupTime) else null var e: ScopeEntry = null val phaseFlatClasses = phase.flatClasses @@ -343,7 +342,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => e = e.next } } - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.scopeLookupTime, startTime) e } @@ -540,5 +538,4 @@ trait ScopeStats { self: Statistics => val scopeCountView = newView("#created scopes")(symbolTable.scopeCount) val scopePopulationTime = newTimer("time spent in scope population") - val scopeLookupTime = newTimer("time spent in scope lookup") } From 2821c263dc690d2694828b851aa7fd34c50b9942 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 21 Feb 2019 15:08:26 +1000 Subject: [PATCH 7/8] Avoid AnyRef.== in LazyTreeCopier --- .../scala/reflect/internal/Trees.scala | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index a1694c05ef3b..cb2ddf005980 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1105,14 +1105,15 @@ trait Trees extends api.Trees { class LazyTreeCopier extends InternalTreeCopierOps { val treeCopy: TreeCopier = newStrictTreeCopier + private def same[T <: AnyRef](as: List[T], bs: List[T]) = sameElementsEquals(as, bs) def ClassDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], impl: Template) = tree match { case t @ ClassDef(mods0, name0, tparams0, impl0) - if (mods0 == mods) && (name0 == name) && (tparams0 == tparams) && (impl0 == impl) => t + if (mods0 == mods) && (name0 == name) && (same(tparams0, tparams)) && (impl0 == impl) => t case _ => treeCopy.ClassDef(tree, mods, name, tparams, impl) } def PackageDef(tree: Tree, pid: RefTree, stats: List[Tree]) = tree match { case t @ PackageDef(pid0, stats0) - if (pid0 == pid) && (stats0 == stats) => t + if (pid0 == pid) && same(stats0, stats) => t case _ => treeCopy.PackageDef(tree, pid, stats) } def ModuleDef(tree: Tree, mods: Modifiers, name: Name, impl: Template) = tree match { @@ -1127,33 +1128,33 @@ trait Trees extends api.Trees { } def DefDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) = tree match { case t @ DefDef(mods0, name0, tparams0, vparamss0, tpt0, rhs0) - if (mods0 == mods) && (name0 == name) && (tparams0 == tparams) && - (vparamss0 == vparamss) && (tpt0 == tpt) && (rhs == rhs0) => t + if (mods0 == mods) && (name0 == name) && same(tparams0, tparams) && + same(vparamss0, vparamss) && (tpt0 == tpt) && (rhs == rhs0) => t case _ => treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs) } def TypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], rhs: Tree) = tree match { case t @ TypeDef(mods0, name0, tparams0, rhs0) - if (mods0 == mods) && (name0 == name) && (tparams0 == tparams) && (rhs0 == rhs) => t + if (mods0 == mods) && (name0 == name) && same(tparams0, tparams) && (rhs0 == rhs) => t case _ => treeCopy.TypeDef(tree, mods, name, tparams, rhs) } def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree) = tree match { case t @ LabelDef(name0, params0, rhs0) - if (name0 == name) && (params0 == params) && (rhs0 == rhs) => t + if (name0 == name) && same(params0, params) && (rhs0 == rhs) => t case _ => treeCopy.LabelDef(tree, name, params, rhs) } def Import(tree: Tree, expr: Tree, selectors: List[ImportSelector]) = tree match { case t @ Import(expr0, selectors0) - if (expr0 == expr) && (selectors0 == selectors) => t + if (expr0 == expr) && same(selectors0, selectors) => t case _ => treeCopy.Import(tree, expr, selectors) } def Template(tree: Tree, parents: List[Tree], self: ValDef, body: List[Tree]) = tree match { case t @ Template(parents0, self0, body0) - if (parents0 == parents) && (self0 == self) && (body0 == body) => t + if (parents0 == parents) && (self0 == self) && same(body0, body) => t case _ => treeCopy.Template(tree, parents, self, body) } def Block(tree: Tree, stats: List[Tree], expr: Tree) = tree match { case t @ Block(stats0, expr0) - if ((stats0 == stats) && (expr0 == expr)) => t + if (same(stats0, stats) && (expr0 == expr)) => t case _ => treeCopy.Block(tree, stats, expr) } def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree) = tree match { @@ -1163,7 +1164,7 @@ trait Trees extends api.Trees { } def Alternative(tree: Tree, trees: List[Tree]) = tree match { case t @ Alternative(trees0) - if trees0 == trees => t + if same(trees0, trees) => t case _ => treeCopy.Alternative(tree, trees) } def Star(tree: Tree, elem: Tree) = tree match { @@ -1178,17 +1179,17 @@ trait Trees extends api.Trees { } def UnApply(tree: Tree, fun: Tree, args: List[Tree]) = tree match { case t @ UnApply(fun0, args0) - if (fun0 == fun) && (args0 == args) => t + if (fun0 == fun) && same(args0, args) => t case _ => treeCopy.UnApply(tree, fun, args) } def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]) = tree match { case t @ ArrayValue(elemtpt0, trees0) - if (elemtpt0 == elemtpt) && (trees0 == trees) => t + if (elemtpt0 == elemtpt) && same(trees0, trees) => t case _ => treeCopy.ArrayValue(tree, elemtpt, trees) } def Function(tree: Tree, vparams: List[ValDef], body: Tree) = tree match { case t @ Function(vparams0, body0) - if (vparams0 == vparams) && (body0 == body) => t + if same(vparams0, vparams) && (body0 == body) => t case _ => treeCopy.Function(tree, vparams, body) } def Assign(tree: Tree, lhs: Tree, rhs: Tree) = tree match { @@ -1208,7 +1209,7 @@ trait Trees extends api.Trees { } def Match(tree: Tree, selector: Tree, cases: List[CaseDef]) = tree match { case t @ Match(selector0, cases0) - if (selector0 == selector) && (cases0 == cases) => t + if (selector0 == selector) && same(cases0, cases) => t case _ => treeCopy.Match(tree, selector, cases) } def Return(tree: Tree, expr: Tree) = tree match { @@ -1218,7 +1219,7 @@ trait Trees extends api.Trees { } def Try(tree: Tree, block: Tree, catches: List[CaseDef], finalizer: Tree) = tree match { case t @ Try(block0, catches0, finalizer0) - if (block0 == block) && (catches0 == catches) && (finalizer0 == finalizer) => t + if (block0 == block) && same(catches0, catches) && (finalizer0 == finalizer) => t case _ => treeCopy.Try(tree, block, catches, finalizer) } def Throw(tree: Tree, expr: Tree) = tree match { @@ -1238,17 +1239,17 @@ trait Trees extends api.Trees { } def TypeApply(tree: Tree, fun: Tree, args: List[Tree]) = tree match { case t @ TypeApply(fun0, args0) - if (fun0 == fun) && (args0 == args) => t + if (fun0 == fun) && same(args0, args) => t case _ => treeCopy.TypeApply(tree, fun, args) } def Apply(tree: Tree, fun: Tree, args: List[Tree]) = tree match { case t @ Apply(fun0, args0) - if (fun0 == fun) && (args0 == args) => t + if (fun0 == fun) && same(args0, args) => t case _ => treeCopy.Apply(tree, fun, args) } def ApplyDynamic(tree: Tree, qual: Tree, args: List[Tree]) = tree match { case t @ ApplyDynamic(qual0, args0) - if (qual0 == qual) && (args0 == args) => t + if (qual0 == qual) && same(args0, args) => t case _ => treeCopy.ApplyDynamic(tree, qual, args) } def Super(tree: Tree, qual: Tree, mix: TypeName) = tree match { @@ -1312,7 +1313,7 @@ trait Trees extends api.Trees { } def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]) = tree match { case t @ AppliedTypeTree(tpt0, args0) - if (tpt0 == tpt) && (args0 == args) => t + if (tpt0 == tpt) && same(args0, args) => t case _ => treeCopy.AppliedTypeTree(tree, tpt, args) } def TypeBoundsTree(tree: Tree, lo: Tree, hi: Tree) = tree match { @@ -1322,7 +1323,7 @@ trait Trees extends api.Trees { } def ExistentialTypeTree(tree: Tree, tpt: Tree, whereClauses: List[MemberDef]) = tree match { case t @ ExistentialTypeTree(tpt0, whereClauses0) - if (tpt0 == tpt) && (whereClauses0 == whereClauses) => t + if (tpt0 == tpt) && same(whereClauses0, whereClauses) => t case _ => treeCopy.ExistentialTypeTree(tree, tpt, whereClauses) } } From 59955a7082bf4fb1e84649df757063e5511eb99d Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 21 Feb 2019 16:44:24 +1000 Subject: [PATCH 8/8] Microopts --- src/reflect/scala/reflect/internal/Symbols.scala | 3 ++- src/reflect/scala/reflect/internal/Types.scala | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 216679bcc57d..81d6c598c1bf 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -689,6 +689,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isRootSymbol = false // RootPackage and RootClass. TODO: also NoSymbol. def isEmptyPackage = false def isEmptyPackageClass = false + final def isNoSymbol: Boolean = this.isInstanceOf[NoSymbol] /** Is this symbol an effective root for fullname string? */ @@ -841,7 +842,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isDelambdafyFunction = isSynthetic && (name containsName tpnme.DELAMBDAFY_LAMBDA_CLASS_NAME) final def isDelambdafyTarget = isArtifact && isMethod && hasAttachment[DelambdafyTarget.type] final def isDefinedInPackage = effectiveOwner.isPackageClass - final def needsFlatClasses = phase.flatClasses && (rawowner ne NoSymbol) && !rawowner.isPackageClass && !isMethod + final def needsFlatClasses = phase.flatClasses && !rawowner.isNoSymbol && !rawowner.isPackageClass && !isMethod // TODO introduce a flag for these? final def isPatternTypeVariable: Boolean = diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index f5cd258e51e8..57a1bee7efa4 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -4024,7 +4024,7 @@ trait Types // Optimization to avoid creating unnecessary new typerefs. def copyTypeRef(tp: Type, pre: Type, sym: Symbol, args: List[Type]): Type = tp match { - case TypeRef(pre0, sym0, _) if pre == pre0 && sym0.name == sym.name => + case TypeRef(pre0, sym0, _) if pre == pre0 && sym0.rawname == sym.rawname => if (sym.isAliasType && sameLength(sym.info.typeParams, args) && !sym.lockOK) throw new RecoverableCyclicReference(sym)