Skip to content

Commit

Permalink
trace subphases
Browse files Browse the repository at this point in the history
  • Loading branch information
bishabosha committed Oct 24, 2023
1 parent 2b7a09e commit ef9fabc
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 75 deletions.
89 changes: 64 additions & 25 deletions compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,13 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
if local != null then
op(using ctx)(local)

def doBeginUnit(unit: CompilationUnit)(using Context): Unit =
def doBeginUnit()(using Context): Unit =
trackProgress: progress =>
progress.informUnitStarting(unit)
progress.informUnitStarting(ctx.compilationUnit)

def doAdvanceUnit()(using Context): Unit =
trackProgress: progress =>
progress.unitc += 1 // trace that we completed a unit in the current phase
progress.unitc += 1 // trace that we completed a unit in the current (sub)phase
progress.refreshProgress()

def doAdvanceLate()(using Context): Unit =
Expand All @@ -196,14 +196,23 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint

private def doAdvancePhase(currentPhase: Phase, wasRan: Boolean)(using Context): Unit =
trackProgress: progress =>
progress.unitc = 0 // reset unit count in current phase
progress.seen += 1 // trace that we've seen a phase
progress.unitc = 0 // reset unit count in current (sub)phase
progress.subtraversalc = 0 // reset subphase index to initial
progress.seen += 1 // trace that we've seen a (sub)phase
if wasRan then
// add an extra traversal now that we completed a phase
// add an extra traversal now that we completed a (sub)phase
progress.traversalc += 1
else
// no phase was ran, remove a traversal from expected total
progress.runnablePhases -= 1
// no subphases were ran, remove traversals from expected total
progress.totalTraversals -= currentPhase.traversals

private def doAdvanceSubPhase()(using Context): Unit =
trackProgress: progress =>
progress.unitc = 0 // reset unit count in current (sub)phase
progress.seen += 1 // trace that we've seen a (sub)phase
progress.traversalc += 1 // add an extra traversal now that we completed a (sub)phase
progress.subtraversalc += 1 // record that we've seen a subphase
progress.tickSubphase()

/** Will be set to true if any of the compiled compilation units contains
* a pureFunctions language import.
Expand Down Expand Up @@ -318,7 +327,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
unfusedPhases.foreach(_.initContext(runCtx))
val fusedPhases = runCtx.base.allPhases
runCtx.withProgressCallback: cb =>
_progress = Progress(cb, this, fusedPhases.length)
_progress = Progress(cb, this, fusedPhases.map(_.traversals).sum)
runPhases(allPhases = fusedPhases)(using runCtx)
if (!ctx.reporter.hasErrors)
Rewrites.writeBack()
Expand Down Expand Up @@ -451,31 +460,52 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint

object Run {

/**Computes the next MegaPhase for the given phase.*/
def nextMegaPhase(phase: Phase)(using Context): Phase = phase.megaPhase.next.megaPhase
class SubPhases(val phase: Phase):
require(phase.exists)

val all = IArray.from(phase.subPhases.map(sub => s"${phase.phaseName} ($sub)"))

def next(using Context): Option[SubPhases] =
val next0 = phase.megaPhase.next.megaPhase
if next0.exists then Some(SubPhases(next0))
else None

private class Progress(cb: ProgressCallback, private val run: Run, val initialPhases: Int):
private[Run] var runnablePhases: Int = initialPhases // track how many phases we expect to run
private[Run] var unitc: Int = 0 // current unit count in the current phase
def subPhase(index: Int) =
if index < all.size then all(index)
else phase.phaseName

private class Progress(cb: ProgressCallback, private val run: Run, val initialTraversals: Int):
private[Run] var totalTraversals: Int = initialTraversals // track how many phases we expect to run
private[Run] var unitc: Int = 0 // current unit count in the current (sub)phase
private[Run] var latec: Int = 0 // current late unit count
private[Run] var traversalc: Int = 0 // completed traversals over all files
private[Run] var subtraversalc: Int = 0 // completed subphases in the current phase
private[Run] var seen: Int = 0 // how many phases we've seen so far

private var currPhase: Phase = uninitialized // initialized by enterPhase
private var subPhases: SubPhases = uninitialized // initialized by enterPhase
private var currPhaseName: String = uninitialized // initialized by enterPhase
private var nextPhaseName: String = uninitialized // initialized by enterPhase

private def phaseNameFor(phase: Phase): String =
if phase.exists then phase.phaseName
else "<end>"

/** Enter into a new real phase, setting the current and next (sub)phases */
private[Run] def enterPhase(newPhase: Phase)(using Context): Unit =
if newPhase ne currPhase then
currPhase = newPhase
currPhaseName = phaseNameFor(newPhase)
nextPhaseName = phaseNameFor(Run.nextMegaPhase(newPhase))
if seen > 0 then
refreshProgress()
subPhases = SubPhases(newPhase)
tickSubphase()

/** Compute the current (sub)phase name and next (sub)phase name */
private[Run] def tickSubphase()(using Context): Unit =
val index = subtraversalc
val s = subPhases
currPhaseName = s.subPhase(index)
nextPhaseName =
if index + 1 < s.all.size then s.subPhase(index + 1)
else s.next match
case None => "<end>"
case Some(next0) => next0.subPhase(0)
if seen > 0 then
refreshProgress()


/** Counts the number of completed full traversals over files, plus the number of units in the current phase */
Expand All @@ -487,26 +517,35 @@ object Run {
* - the number of late compilations
*/
private def totalProgress()(using Context): Int =
runnablePhases * run.files.size + run.lateFiles.size
totalTraversals * run.files.size + run.lateFiles.size

private def requireInitialized(): Unit =
require((currPhase: Phase | Null) != null, "enterPhase was not called")

/** trace that we are beginning a unit in the current (sub)phase */
private[Run] def informUnitStarting(unit: CompilationUnit)(using Context): Unit =
requireInitialized()
cb.informUnitStarting(currPhaseName, unit)

/** trace the current progress out of the total, in the current (sub)phase, reporting the next (sub)phase */
private[Run] def refreshProgress()(using Context): Unit =
requireInitialized()
cb.progress(currentProgress(), totalProgress(), currPhaseName, nextPhaseName)

extension (run: Run | Null)
def beginUnit(unit: CompilationUnit)(using Context): Unit =
if run != null then run.doBeginUnit(unit)

/** record that the current phase has begun for the compilation unit of the current Context */
def beginUnit()(using Context): Unit =
if run != null then run.doBeginUnit()

/** advance the unit count and record progress in the current phase */
def advanceUnit()(using Context): Unit =
if run != null then run.doAdvanceUnit()

def advanceSubPhase()(using Context): Unit =
if run != null then run.doAdvanceSubPhase()

/** advance the late count and record progress in the current phase */
def advanceLate()(using Context): Unit =
if run != null then run.doAdvanceLate()

Expand Down
11 changes: 9 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -328,14 +328,18 @@ object Phases {
/** List of names of phases that should precede this phase */
def runsAfter: Set[String] = Set.empty

/** for purposes of progress tracking, overridden in TyperPhase */
def subPhases: List[String] = Nil
final def traversals: Int = if subPhases.isEmpty then 1 else subPhases.length

/** @pre `isRunnable` returns true */
def run(using Context): Unit

/** @pre `isRunnable` returns true */
def runOn(units: List[CompilationUnit])(using runCtx: Context): List[CompilationUnit] =
units.map { unit =>
given unitCtx: Context = runCtx.fresh.setPhase(this.start).setCompilationUnit(unit).withRootImports
ctx.run.beginUnit(unit)
ctx.run.beginUnit()
try run
catch case ex: Throwable if !ctx.run.enrichedErrorMessage =>
println(ctx.run.enrichErrorMessage(s"unhandled exception while running $phaseName on $unit"))
Expand Down Expand Up @@ -450,12 +454,15 @@ object Phases {
final def iterator: Iterator[Phase] =
Iterator.iterate(this)(_.next) takeWhile (_.hasNext)

final def monitor(doing: String)(body: => Unit)(using Context): Unit =
/** run the body as one iteration of a (sub)phase (see Run.Progress), Enrich crash messages */
final def monitor(doing: String)(body: Context ?=> Unit)(using Context): Unit =
ctx.run.beginUnit()
try body
catch
case NonFatal(ex) if !ctx.run.enrichedErrorMessage =>
report.echo(ctx.run.enrichErrorMessage(s"exception occurred while $doing ${ctx.compilationUnit}"))
throw ex
finally ctx.run.advanceUnit()

override def toString: String = phaseName
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class ReadTasty extends Phase {
withMode(Mode.ReadPositions)(units.flatMap(applyPhase(_)))

private def applyPhase(unit: CompilationUnit)(using Context): Option[CompilationUnit] =
ctx.run.beginUnit(unit)
ctx.run.beginUnit()
try readTASTY(unit)
finally ctx.run.advanceUnit()

Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/parsing/ParserPhase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,12 @@ class Parser extends Phase {
override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] = {
val unitContexts =
for unit <- units yield
ctx.run.beginUnit(unit)
report.inform(s"parsing ${unit.source}")
ctx.fresh.setCompilationUnit(unit).withRootImports

for given Context <- unitContexts do
try parse
finally ctx.run.advanceUnit()
parse

record("parsedTrees", ast.Trees.ntrees)

unitContexts.map(_.compilationUnit)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/init/Checker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Checker extends Phase:
val checkCtx = ctx.fresh.setPhase(this.start)
val traverser = new InitTreeTraverser()
for unit <- units do
checkCtx.run.beginUnit(unit)
checkCtx.run.beginUnit()
try traverser.traverse(unit.tpdTree)
finally ctx.run.advanceUnit()
val classes = traverser.getClasses()
Expand Down
22 changes: 15 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/TyperPhase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,25 @@ class TyperPhase(addRootImports: Boolean = true) extends Phase {
protected def discardAfterTyper(unit: CompilationUnit)(using Context): Boolean =
unit.isJava || unit.suspended

/** Keep synchronised with `monitor` subcalls */
override def subPhases: List[String] = List("indexing", "typechecking", "checking java")

override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
val unitContexts =
for unit <- units yield
val newCtx0 = ctx.fresh.setPhase(this.start).setCompilationUnit(unit)
val newCtx = PrepareInlineable.initContext(newCtx0)
newCtx.run.beginUnit(unit)
report.inform(s"typing ${unit.source}")
if (addRootImports)
newCtx.withRootImports
else
newCtx

for given Context <- unitContexts do
enterSyms
try
for given Context <- unitContexts do
enterSyms
finally
ctx.run.advanceSubPhase() // tick from "typer (indexing)" to "typer (typechecking)"

ctx.base.parserPhase match {
case p: ParserPhase =>
Expand All @@ -83,13 +88,16 @@ class TyperPhase(addRootImports: Boolean = true) extends Phase {
case _ =>
}

for given Context <- unitContexts do
typeCheck
try
for given Context <- unitContexts do
typeCheck
finally
ctx.run.advanceSubPhase() // tick from "typer (typechecking)" to "typer (java checking)"

record("total trees after typer", ast.Trees.ntrees)

for given Context <- unitContexts do
try javaCheck // after typechecking to avoid cycles
finally ctx.run.advanceUnit()
javaCheck // after typechecking to avoid cycles

val newUnits = unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper)
ctx.run.nn.checkSuspendedUnits(newUnits)
Expand Down
Loading

0 comments on commit ef9fabc

Please sign in to comment.