Skip to content

Commit ef9fabc

Browse files
committed
trace subphases
1 parent 2b7a09e commit ef9fabc

File tree

7 files changed

+126
-75
lines changed

7 files changed

+126
-75
lines changed

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,13 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
176176
if local != null then
177177
op(using ctx)(local)
178178

179-
def doBeginUnit(unit: CompilationUnit)(using Context): Unit =
179+
def doBeginUnit()(using Context): Unit =
180180
trackProgress: progress =>
181-
progress.informUnitStarting(unit)
181+
progress.informUnitStarting(ctx.compilationUnit)
182182

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

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

197197
private def doAdvancePhase(currentPhase: Phase, wasRan: Boolean)(using Context): Unit =
198198
trackProgress: progress =>
199-
progress.unitc = 0 // reset unit count in current phase
200-
progress.seen += 1 // trace that we've seen a phase
199+
progress.unitc = 0 // reset unit count in current (sub)phase
200+
progress.subtraversalc = 0 // reset subphase index to initial
201+
progress.seen += 1 // trace that we've seen a (sub)phase
201202
if wasRan then
202-
// add an extra traversal now that we completed a phase
203+
// add an extra traversal now that we completed a (sub)phase
203204
progress.traversalc += 1
204205
else
205-
// no phase was ran, remove a traversal from expected total
206-
progress.runnablePhases -= 1
206+
// no subphases were ran, remove traversals from expected total
207+
progress.totalTraversals -= currentPhase.traversals
208+
209+
private def doAdvanceSubPhase()(using Context): Unit =
210+
trackProgress: progress =>
211+
progress.unitc = 0 // reset unit count in current (sub)phase
212+
progress.seen += 1 // trace that we've seen a (sub)phase
213+
progress.traversalc += 1 // add an extra traversal now that we completed a (sub)phase
214+
progress.subtraversalc += 1 // record that we've seen a subphase
215+
progress.tickSubphase()
207216

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

452461
object Run {
453462

454-
/**Computes the next MegaPhase for the given phase.*/
455-
def nextMegaPhase(phase: Phase)(using Context): Phase = phase.megaPhase.next.megaPhase
463+
class SubPhases(val phase: Phase):
464+
require(phase.exists)
465+
466+
val all = IArray.from(phase.subPhases.map(sub => s"${phase.phaseName} ($sub)"))
467+
468+
def next(using Context): Option[SubPhases] =
469+
val next0 = phase.megaPhase.next.megaPhase
470+
if next0.exists then Some(SubPhases(next0))
471+
else None
456472

457-
private class Progress(cb: ProgressCallback, private val run: Run, val initialPhases: Int):
458-
private[Run] var runnablePhases: Int = initialPhases // track how many phases we expect to run
459-
private[Run] var unitc: Int = 0 // current unit count in the current phase
473+
def subPhase(index: Int) =
474+
if index < all.size then all(index)
475+
else phase.phaseName
476+
477+
private class Progress(cb: ProgressCallback, private val run: Run, val initialTraversals: Int):
478+
private[Run] var totalTraversals: Int = initialTraversals // track how many phases we expect to run
479+
private[Run] var unitc: Int = 0 // current unit count in the current (sub)phase
460480
private[Run] var latec: Int = 0 // current late unit count
461481
private[Run] var traversalc: Int = 0 // completed traversals over all files
482+
private[Run] var subtraversalc: Int = 0 // completed subphases in the current phase
462483
private[Run] var seen: Int = 0 // how many phases we've seen so far
463484

464485
private var currPhase: Phase = uninitialized // initialized by enterPhase
486+
private var subPhases: SubPhases = uninitialized // initialized by enterPhase
465487
private var currPhaseName: String = uninitialized // initialized by enterPhase
466488
private var nextPhaseName: String = uninitialized // initialized by enterPhase
467489

468-
private def phaseNameFor(phase: Phase): String =
469-
if phase.exists then phase.phaseName
470-
else "<end>"
471-
490+
/** Enter into a new real phase, setting the current and next (sub)phases */
472491
private[Run] def enterPhase(newPhase: Phase)(using Context): Unit =
473492
if newPhase ne currPhase then
474493
currPhase = newPhase
475-
currPhaseName = phaseNameFor(newPhase)
476-
nextPhaseName = phaseNameFor(Run.nextMegaPhase(newPhase))
477-
if seen > 0 then
478-
refreshProgress()
494+
subPhases = SubPhases(newPhase)
495+
tickSubphase()
496+
497+
/** Compute the current (sub)phase name and next (sub)phase name */
498+
private[Run] def tickSubphase()(using Context): Unit =
499+
val index = subtraversalc
500+
val s = subPhases
501+
currPhaseName = s.subPhase(index)
502+
nextPhaseName =
503+
if index + 1 < s.all.size then s.subPhase(index + 1)
504+
else s.next match
505+
case None => "<end>"
506+
case Some(next0) => next0.subPhase(0)
507+
if seen > 0 then
508+
refreshProgress()
479509

480510

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

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

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

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

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

537+
/** record that the current phase has begun for the compilation unit of the current Context */
538+
def beginUnit()(using Context): Unit =
539+
if run != null then run.doBeginUnit()
540+
541+
/** advance the unit count and record progress in the current phase */
507542
def advanceUnit()(using Context): Unit =
508543
if run != null then run.doAdvanceUnit()
509544

545+
def advanceSubPhase()(using Context): Unit =
546+
if run != null then run.doAdvanceSubPhase()
547+
548+
/** advance the late count and record progress in the current phase */
510549
def advanceLate()(using Context): Unit =
511550
if run != null then run.doAdvanceLate()
512551

compiler/src/dotty/tools/dotc/core/Phases.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,14 +328,18 @@ object Phases {
328328
/** List of names of phases that should precede this phase */
329329
def runsAfter: Set[String] = Set.empty
330330

331+
/** for purposes of progress tracking, overridden in TyperPhase */
332+
def subPhases: List[String] = Nil
333+
final def traversals: Int = if subPhases.isEmpty then 1 else subPhases.length
334+
331335
/** @pre `isRunnable` returns true */
332336
def run(using Context): Unit
333337

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

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

460467
override def toString: String = phaseName
461468
}

compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class ReadTasty extends Phase {
2525
withMode(Mode.ReadPositions)(units.flatMap(applyPhase(_)))
2626

2727
private def applyPhase(unit: CompilationUnit)(using Context): Option[CompilationUnit] =
28-
ctx.run.beginUnit(unit)
28+
ctx.run.beginUnit()
2929
try readTASTY(unit)
3030
finally ctx.run.advanceUnit()
3131

compiler/src/dotty/tools/dotc/parsing/ParserPhase.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,12 @@ class Parser extends Phase {
4343
override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] = {
4444
val unitContexts =
4545
for unit <- units yield
46-
ctx.run.beginUnit(unit)
4746
report.inform(s"parsing ${unit.source}")
4847
ctx.fresh.setCompilationUnit(unit).withRootImports
4948

5049
for given Context <- unitContexts do
51-
try parse
52-
finally ctx.run.advanceUnit()
50+
parse
51+
5352
record("parsedTrees", ast.Trees.ntrees)
5453

5554
unitContexts.map(_.compilationUnit)

compiler/src/dotty/tools/dotc/transform/init/Checker.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Checker extends Phase:
3535
val checkCtx = ctx.fresh.setPhase(this.start)
3636
val traverser = new InitTreeTraverser()
3737
for unit <- units do
38-
checkCtx.run.beginUnit(unit)
38+
checkCtx.run.beginUnit()
3939
try traverser.traverse(unit.tpdTree)
4040
finally ctx.run.advanceUnit()
4141
val classes = traverser.getClasses()

compiler/src/dotty/tools/dotc/typer/TyperPhase.scala

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,25 @@ class TyperPhase(addRootImports: Boolean = true) extends Phase {
5858
protected def discardAfterTyper(unit: CompilationUnit)(using Context): Boolean =
5959
unit.isJava || unit.suspended
6060

61+
/** Keep synchronised with `monitor` subcalls */
62+
override def subPhases: List[String] = List("indexing", "typechecking", "checking java")
63+
6164
override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
6265
val unitContexts =
6366
for unit <- units yield
6467
val newCtx0 = ctx.fresh.setPhase(this.start).setCompilationUnit(unit)
6568
val newCtx = PrepareInlineable.initContext(newCtx0)
66-
newCtx.run.beginUnit(unit)
6769
report.inform(s"typing ${unit.source}")
6870
if (addRootImports)
6971
newCtx.withRootImports
7072
else
7173
newCtx
7274

73-
for given Context <- unitContexts do
74-
enterSyms
75+
try
76+
for given Context <- unitContexts do
77+
enterSyms
78+
finally
79+
ctx.run.advanceSubPhase() // tick from "typer (indexing)" to "typer (typechecking)"
7580

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

86-
for given Context <- unitContexts do
87-
typeCheck
91+
try
92+
for given Context <- unitContexts do
93+
typeCheck
94+
finally
95+
ctx.run.advanceSubPhase() // tick from "typer (typechecking)" to "typer (java checking)"
8896

8997
record("total trees after typer", ast.Trees.ntrees)
98+
9099
for given Context <- unitContexts do
91-
try javaCheck // after typechecking to avoid cycles
92-
finally ctx.run.advanceUnit()
100+
javaCheck // after typechecking to avoid cycles
93101

94102
val newUnits = unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper)
95103
ctx.run.nn.checkSuspendedUnits(newUnits)

0 commit comments

Comments
 (0)