Skip to content

Typer microopts #56

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 7 commits into
base: 2.12.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 14 additions & 2 deletions src/reflect/scala/reflect/internal/BaseTypeSeqs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,18 @@ trait BaseTypeSeqs {
self =>
if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(baseTypeSeqCount)
if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(baseTypeSeqLenTotal, elems.length)
private[this] var _hasAbstractTypeSymbol = false
private[this] val typeSymbols = {
val tmp = new Array[Int](elems.length)
var i = 0
while (i < elems.length) {
_hasAbstractTypeSymbol ||= elems(i).typeSymbolDirect.isAbstractType
tmp(i) = elems(i).typeSymbol.id
i += 1
}
tmp
}
def hasAbstractTypeSymbol: Boolean = _hasAbstractTypeSymbol

/** The number of types in the sequence */
def length: Int = elems.length
Expand Down Expand Up @@ -162,7 +165,16 @@ trait BaseTypeSeqs {

def lateMap(f: Type => Type): BaseTypeSeq = newMappedBaseTypeSeq(this, f)

def exists(p: Type => Boolean): Boolean = elems exists p
@inline
final def exists(p: Type => Boolean): Boolean = {
var i = 0
val es = elems
while (i < es.length) {
if (p(es(i))) return true
i += 1
}
false
}

lazy val maxDepth = maxDepthOfElems

Expand Down Expand Up @@ -269,8 +281,8 @@ trait BaseTypeSeqs {
override def copy(head: Type, offset: Int) = (orig map f).copy(head, offset)
override def map(g: Type => Type) = lateMap(g)
override def lateMap(g: Type => Type) = orig.lateMap(x => g(f(x)))
override def exists(p: Type => Boolean) = elems exists (x => p(f(x)))
override protected def maxDepthOfElems: Depth = elems.map(x => typeDepth(f(x))).max
override def hasAbstractTypeSymbol: Boolean = elems exists (x => (f(x)).typeSymbolDirect.isAbstractType)
override def toString = elems.mkString("MBTS(", ",", ")")
}

Expand Down
8 changes: 0 additions & 8 deletions src/reflect/scala/reflect/internal/Scopes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,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 flat = phase.flatClasses
if (hashtable ne null) {
Expand All @@ -330,7 +329,6 @@ trait Scopes extends api.Scopes { self: SymbolTable =>
e = e.next
}
}
if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.scopeLookupTime, startTime)
e
}

Expand Down Expand Up @@ -481,22 +479,18 @@ trait Scopes extends api.Scopes { self: SymbolTable =>

/** Create a new scope nested in another one with which it shares its elements */
final def newNestedScope(outer: Scope): Scope = {
val startTime = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.scopePopulationTime) else null
val nested = newScope // not `new Scope`, we must allow the runtime reflection universe to mixin SynchronizedScopes!
nested.elems = outer.elems
nested.nestinglevel = outer.nestinglevel + 1
if (outer.hashtable ne null)
nested.hashtable = java.util.Arrays.copyOf(outer.hashtable, outer.hashtable.length)
if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.scopePopulationTime, startTime)
nested
}

/** Create a new scope with given initial elements */
def newScopeWith(elems: Symbol*): Scope = {
val startTime = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.scopePopulationTime) else null
val scope = newScope
elems foreach scope.enter
if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.scopePopulationTime, startTime)
scope
}

Expand Down Expand Up @@ -527,6 +521,4 @@ trait Scopes extends api.Scopes { self: SymbolTable =>
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")
}
173 changes: 85 additions & 88 deletions src/reflect/scala/reflect/internal/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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() {}

Expand Down
4 changes: 0 additions & 4 deletions src/reflect/scala/reflect/internal/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = _
Expand Down Expand Up @@ -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(""))
}
Loading