From 20f7e525cb3d44f38c3f6b341a0aaa75c39cdb98 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 16 Aug 2019 11:52:20 +1000 Subject: [PATCH 1/6] Remove isDeveloper guarded assertion call from SubstMap.init --- src/reflect/scala/reflect/internal/tpe/TypeMaps.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index 40b65e68244..0110543a78f 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -729,9 +729,6 @@ private[internal] trait TypeMaps { /** A base class to compute all substitutions */ abstract class SubstMap[T](from: List[Symbol], to: List[T]) extends TypeMap { - // OPT this check was 2-3% of some profiles, demoted to -Xdev - if (isDeveloper) assert(sameLength(from, to), "Unsound substitution from "+ from +" to "+ to) - private[this] var fromHasTermSymbol = false private[this] var fromMin = Int.MaxValue private[this] var fromMax = Int.MinValue From f86eafec9f4c63b8fcdaea39b1a2300ece9236fe Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 16 Aug 2019 11:54:08 +1000 Subject: [PATCH 2/6] Remove nodeByType 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 29a70e9848b..7dc6e0eb545 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1645,7 +1645,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) } 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 d6dd771922e..a7cb9db9ef2 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -46,9 +46,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 = _ @@ -1955,7 +1952,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 6429d14b30a5f1cd600e311724a985926139ab9a Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 16 Aug 2019 11:59:04 +1000 Subject: [PATCH 3/6] Disable statistics statically --- .../internal/util/StatisticsStatics.java | 44 +------------------ 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/src/reflect/scala/reflect/internal/util/StatisticsStatics.java b/src/reflect/scala/reflect/internal/util/StatisticsStatics.java index dc9021471d8..cbd46ea3659 100644 --- a/src/reflect/scala/reflect/internal/util/StatisticsStatics.java +++ b/src/reflect/scala/reflect/internal/util/StatisticsStatics.java @@ -15,53 +15,13 @@ import scala.reflect.internal.util.AlmostFinalValue; import java.lang.invoke.MethodHandle; -/** - * Represents all the simulated statics for Statistics. - * - * Its implementation delegates to {@link scala.reflect.internal.util.AlmostFinalValue}, - * which helps performance (see docs to find out why). - */ public final class StatisticsStatics { - private static final AlmostFinalValue COLD_STATS = new AlmostFinalValue() { - @Override - protected boolean initialValue() { - return false; - } - }; - - private static final AlmostFinalValue HOT_STATS = new AlmostFinalValue() { - @Override - protected boolean initialValue() { - return false; - } - }; - - private static final MethodHandle COLD_STATS_GETTER = COLD_STATS.createGetter(); - private static final MethodHandle HOT_STATS_GETTER = HOT_STATS.createGetter(); - - public static boolean areSomeColdStatsEnabled() throws Throwable { - return (boolean) COLD_STATS_GETTER.invokeExact(); - } - - public static boolean areSomeHotStatsEnabled() throws Throwable { - return (boolean) HOT_STATS_GETTER.invokeExact(); - } + public static final boolean areSomeColdStatsEnabled = false; + public static final boolean areSomeHotStatsEnabled = false; public static void enableColdStats() throws Throwable { - if (!areSomeColdStatsEnabled()) - COLD_STATS.setValue(true); - } - - public static void disableColdStats() { - COLD_STATS.setValue(false); } public static void enableHotStats() throws Throwable { - if (!areSomeHotStatsEnabled()) - HOT_STATS.setValue(true); - } - - public static void disableHotStats() { - HOT_STATS.setValue(false); } } From 22a1366e101a78bc9721f86ae7fab57e29df3d15 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 20 Feb 2019 17:15:36 +1000 Subject: [PATCH 4/6] Break up Symbol.info for JIT friendliness Also remove commented out debugging code and simplify adaptInfos. (cherry picked from commit 3bcabcbf7874d5d54efac06484c62c93742dd664) --- .../scala/reflect/internal/Symbols.scala | 173 +++++++++--------- 1 file changed, 85 insertions(+), 88 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index a5e2488740f..1a82a2b77de 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -36,12 +36,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => protected def nextId() = { ids += 1; ids } - /** Used for deciding in the IDE whether we can interrupt the compiler */ - //protected var activeLocks = 0 - - /** Used for debugging only */ - //protected var lockedSyms = scala.collection.immutable.Set[Symbol]() - /** Used to keep track of the recursion depth on locked symbols */ private var _recursionTable = immutable.Map.empty[Symbol, Int] def recursionTable = _recursionTable @@ -587,16 +581,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => } else { _rawflags |= LOCKED true -// activeLocks += 1 -// lockedSyms += this } } // Unlock a symbol private[scala] def unlock() = { if ((_rawflags & LOCKED) != 0L) { -// activeLocks -= 1 -// lockedSyms -= this _rawflags &= ~LOCKED if (settings.Yrecursion.value != 0) recursionTable -= this @@ -1517,41 +1507,42 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Get type info associated with symbol at current phase, after * ensuring that symbol is initialized (i.e. type is completed). */ - def info: Type = try { + def info: Type = { var cnt = 0 - while (validTo == NoPeriod) { - assert(infos ne null, this.name) - assert(infos.prev eq null, this.name) - val tp = infos.info - - if ((_rawflags & LOCKED) != 0L) { // rolled out once for performance - lock { - setInfo(ErrorType) - throw CyclicReference(this, tp) - } - } else { - _rawflags |= LOCKED - // TODO another commented out lines - this should be solved in one way or another -// activeLocks += 1 - // lockedSyms += this - } - val current = phase - try { - assertCorrectThread() - phase = phaseOf(infos.validFrom) - tp.complete(this) - } finally { - unlock() - phase = current - } + while (_validTo == NoPeriod) { + completeInfo() cnt += 1 // allow for two completions: // one: sourceCompleter to LazyType, two: LazyType to completed type - if (cnt == 3) abort(s"no progress in completing $this: $tp") + def abortNoProgress() = abort(s"no progress in completing $this: ${infos.info}") + if (cnt == 3) abortNoProgress() } rawInfo } - catch { + + private def completeInfo(): Unit = try { + assert(infos ne null, this.name) + assert(infos.prev eq null, this.name) + val tp = infos.info + + if ((_rawflags & LOCKED) != 0L) { // rolled out once for performance + lock { + setInfo(ErrorType) + throw CyclicReference(this, tp) + } + } else { + _rawflags |= LOCKED + } + val current = phase + try { + assertCorrectThread() + phase = phaseOf(infos.validFrom) + tp.complete(this) + } finally { + unlock() + phase = current + } + } catch { case ex: CyclicReference => devWarning("... hit cycle trying to complete " + this.fullLocationString) throw ex @@ -1607,81 +1598,87 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Return info without checking for initialization or completing */ def rawInfo: Type = { + // OPT: hoisting the outer reference reduces the bytecode size of this method a little which makes it more + // likely to inline into hot callers of .info + val outer = Symbols.this + var infos = this.infos - assert(infos != null) - val curPeriod = currentPeriod - val curPid = phaseId(curPeriod) + outer.assert(infos != null) + + if (_validTo != NoPeriod) { + val curPeriod = outer.currentPeriod + val curPid = outer.phaseId(curPeriod) - if (validTo != NoPeriod) { // skip any infos that concern later phases - while (curPid < phaseId(infos.validFrom) && infos.prev != null) + while (curPid < outer.phaseId(infos.validFrom) && infos.prev != null) infos = infos.prev - if (validTo < curPeriod) { - assertCorrectThread() + if (_validTo < curPeriod) { // adapt any infos that come from previous runs - val current = phase + val curPhase = outer.phase try { - infos = adaptInfos(infos) + if (infos != null && outer.runId(infos.validFrom) != outer.currentRunId) { + // scala/bug#8871 Discard all but the first element of type history. Specialization only works in the resident + // compiler / REPL if re-run its info transformer in this run to correctly populate its + // per-run caches, e.g. typeEnv + infos = adaptInfo(infos.oldest) + } //assert(runId(validTo) == currentRunId, name) //assert(runId(infos.validFrom) == currentRunId, name) - if (validTo < curPeriod) { - var itr = infoTransformers.nextFrom(phaseId(validTo)) - infoTransformers = itr; // caching optimization - while (itr.pid != NoPhase.id && itr.pid < current.id) { - phase = phaseWithId(itr.pid) - val info1 = itr.transform(this, infos.info) - if (info1 ne infos.info) { - infos = TypeHistory(currentPeriod + 1, info1, infos) - this.infos = infos - } - _validTo = currentPeriod + 1 // to enable reads from same symbol during info-transform - itr = itr.next - } - _validTo = if (itr.pid == NoPhase.id) curPeriod - else period(currentRunId, itr.pid) + if (_validTo < curPeriod) { + infos = transformInfos(infos, curPhase, curPeriod) } } finally { - phase = current + outer.phase = curPhase } } } infos.info } + private def transformInfos(infos0: TypeHistory, curPhase: Phase, curPeriod: Period): TypeHistory = { + assertCorrectThread() + var infos = infos0 + var itr = infoTransformers.nextFrom(phaseId(_validTo)) + infoTransformers = itr; // caching optimization + while (itr.pid != NoPhase.id && itr.pid < curPhase.id) { + phase = phaseWithId(itr.pid) + val info1 = itr.transform(this, infos.info) + if (info1 ne infos.info) { + infos = TypeHistory(currentPeriod + 1, info1, infos) + this.infos = infos + } + _validTo = currentPeriod + 1 // to enable reads from same symbol during info-transform + itr = itr.next + } + _validTo = if (itr.pid == NoPhase.id) curPeriod + else period(currentRunId, itr.pid) + infos + } + // adapt to new run in fsc. - private def adaptInfos(infos: TypeHistory): TypeHistory = { + private def adaptInfo(oldest: TypeHistory): TypeHistory = { assert(isCompilerUniverse) - if (infos == null || runId(infos.validFrom) == currentRunId) { - infos - } else if (infos ne infos.oldest) { - // scala/bug#8871 Discard all but the first element of type history. Specialization only works in the resident - // compiler / REPL if re-run its info transformer in this run to correctly populate its - // per-run caches, e.g. typeEnv - adaptInfos(infos.oldest) + assert(oldest.prev == null) + val pid = phaseId(oldest.validFrom) + + _validTo = period(currentRunId, pid) + phase = phaseWithId(pid) + + val info1 = adaptToNewRunMap(oldest.info) + if (info1 eq oldest.info) { + oldest.validFrom = validTo + this.infos = oldest + oldest } else { - val prev1 = adaptInfos(infos.prev) - if (prev1 ne infos.prev) prev1 - else { - val pid = phaseId(infos.validFrom) - - _validTo = period(currentRunId, pid) - phase = phaseWithId(pid) - - val info1 = adaptToNewRunMap(infos.info) - if (info1 eq infos.info) { - infos.validFrom = validTo - infos - } else { - this.infos = TypeHistory(validTo, info1, prev1) - this.infos - } - } + this.infos = TypeHistory(validTo, info1, null) + this.infos } } + /** Raises a `MissingRequirementError` if this symbol is a `StubSymbol` */ def failIfStub() {} From c793bde7c4068d20deebc62822c1589a92a07794 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 16 Aug 2019 12:05:07 +1000 Subject: [PATCH 5/6] fixup statistics --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index fbb08d1b121..659b6099925 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -5690,7 +5690,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def typed(tree: Tree, mode: Mode, pt: Type): Tree = { lastTreeToTyper = tree - val statsEnabled = StatisticsStatics.areSomeHotStatsEnabled() && statistics.areHotStatsLocallyEnabled + val statsEnabled = StatisticsStatics.areSomeHotStatsEnabled && statistics.areHotStatsLocallyEnabled val startByType = if (statsEnabled) statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) else null if (statsEnabled) statistics.incCounter(visitsByType, tree.getClass) val shouldPrintTyping = printTypings && !phase.erasedTypes && !noPrintTyping(tree) From a14feaf0327dd2cb9238f27b8982751d45ac523e Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 16 Aug 2019 12:05:25 +1000 Subject: [PATCH 6/6] Optimise NoSymbol check --- src/reflect/scala/reflect/internal/Symbols.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 1a82a2b77de..93fea43d155 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -830,7 +830,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 = @@ -1086,6 +1086,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => supersym == NoSymbol || supersym.isIncompleteIn(base) } + final def isNoSymbol: Boolean = isInstanceOf[NoSymbol] def exists: Boolean = !isTopLevel || { val isSourceLoader = rawInfo match { case sl: SymLoader => sl.fromSource