Skip to content

Commit 5aa5c1f

Browse files
committed
Optimize Denotation#current
Split into smaller parts
1 parent 06756eb commit 5aa5c1f

File tree

2 files changed

+92
-91
lines changed

2 files changed

+92
-91
lines changed

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

-6
Original file line numberDiff line numberDiff line change
@@ -329,12 +329,6 @@ object Contexts {
329329
related = related.updated(source, ctx2)
330330
ctx1
331331

332-
inline def evalAt[T](phase: Phase)(inline op: Context ?=> T): T =
333-
val saved = period
334-
this.asInstanceOf[FreshContext].period = Period(runId, phase.id)
335-
try op(using this)
336-
finally period = saved
337-
338332
// `creationTrace`-related code. To enable, uncomment the code below and the
339333
// call to `setCreationTrace()` in this file.
340334
/*

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

+92-85
Original file line numberDiff line numberDiff line change
@@ -757,95 +757,102 @@ object Denotations {
757757
* is brought forward to be valid in the new runId. Otherwise
758758
* the symbol is stale, which constitutes an internal error.
759759
*/
760-
def current(using Context): SingleDenotation = {
760+
def current(using Context): SingleDenotation =
761+
util.Stats.record("current")
761762
val currentPeriod = ctx.period
762763
val valid = myValidFor
763-
if (valid.code <= 0) {
764-
// can happen if we sit on a stale denotation which has been replaced
765-
// wholesale by an installAfter; in this case, proceed to the next
766-
// denotation and try again.
767-
val nxt = nextDefined
768-
if (nxt.validFor != Nowhere) return nxt
769-
assert(false, this)
770-
}
771764

772-
if (valid.runId != currentPeriod.runId)
773-
if (exists) initial.bringForward().current
774-
else this
775-
else {
765+
def signalError() = println(s"error while transforming $this")
766+
767+
def assertNotPackage(d: SingleDenotation, transformer: DenotTransformer) = d match
768+
case d: ClassDenotation =>
769+
assert(!d.is(Package), s"illegal transformation of package denotation by transformer $transformer")
770+
case _ =>
771+
772+
def escapeToNext = nextDefined.ensuring(_.validFor != Nowhere)
773+
774+
def toNewRun =
775+
util.Stats.record("current.bringForward")
776+
if exists then initial.bringForward().current else this
777+
778+
def goForward =
776779
var cur = this
777-
if (currentPeriod.code > valid.code) {
778-
// search for containing period as long as nextInRun increases.
779-
var next = nextInRun
780-
while (next.validFor.code > valid.code && !(next.validFor contains currentPeriod)) {
781-
cur = next
782-
next = next.nextInRun
783-
}
784-
if (next.validFor.code > valid.code) {
785-
// in this case, next.validFor contains currentPeriod
786-
cur = next
787-
cur
788-
}
789-
else {
790-
//println(s"might need new denot for $cur, valid for ${cur.validFor} at $currentPeriod")
791-
// not found, cur points to highest existing variant
792-
val nextTransformerId = ctx.base.nextDenotTransformerId(cur.validFor.lastPhaseId)
793-
if (currentPeriod.lastPhaseId <= nextTransformerId)
794-
cur.validFor = Period(currentPeriod.runId, cur.validFor.firstPhaseId, nextTransformerId)
795-
else {
796-
var startPid = nextTransformerId + 1
797-
val transformer = ctx.base.denotTransformers(nextTransformerId)
798-
//println(s"transforming $this with $transformer")
799-
try
800-
util.Stats.record("denot transform")
801-
next = ctx.evalAt(transformer)(transformer.transform(cur))
802-
// We temporarily update the context with the new phase instead of creating a
803-
// new one. This is done for performance. We cut down on about 30% of context
804-
// creations that way, and also avoid phase caches in contexts to get large.
805-
// To work correctly, we need to demand that the context with the new phase
806-
// is not retained in the result.
807-
catch {
808-
case ex: CyclicReference =>
809-
println(s"error while transforming $this") // DEBUG
810-
throw ex
811-
}
812-
if (next eq cur)
813-
startPid = cur.validFor.firstPhaseId
814-
else {
815-
next match {
816-
case next: ClassDenotation =>
817-
assert(!next.is(Package), s"illegal transformation of package denotation by transformer $transformer")
818-
case _ =>
819-
}
820-
next.insertAfter(cur)
821-
cur = next
822-
}
823-
cur.validFor = Period(currentPeriod.runId, startPid, transformer.lastPhaseId)
824-
//printPeriods(cur)
825-
//println(s"new denot: $cur, valid for ${cur.validFor}")
826-
}
827-
cur.current // multiple transformations could be required
828-
}
829-
}
830-
else {
831-
// currentPeriod < end of valid; in this case a version must exist
832-
// but to be defensive we check for infinite loop anyway
833-
var cnt = 0
834-
while (!(cur.validFor contains currentPeriod)) {
835-
//println(s"searching: $cur at $currentPeriod, valid for ${cur.validFor}")
836-
cur = cur.nextInRun
837-
// Note: One might be tempted to add a `prev` field to get to the new denotation
838-
// more directly here. I tried that, but it degrades rather than improves
839-
// performance: Test setup: Compile everything in dotc and immediate subdirectories
840-
// 10 times. Best out of 10: 18154ms with `prev` field, 17777ms without.
841-
cnt += 1
842-
if (cnt > MaxPossiblePhaseId)
843-
return atPhase(coveredInterval.firstPhaseId)(current)
844-
}
780+
// search for containing period as long as nextInRun increases.
781+
var next = nextInRun
782+
while next.validFor.code > valid.code && !(next.validFor contains currentPeriod) do
783+
cur = next
784+
next = next.nextInRun
785+
if next.validFor.code > valid.code then
786+
// in this case, next.validFor contains currentPeriod
787+
cur = next
845788
cur
846-
}
847-
}
848-
}
789+
else
790+
//println(s"might need new denot for $cur, valid for ${cur.validFor} at $currentPeriod")
791+
// not found, cur points to highest existing variant
792+
val nextTransformerId = ctx.base.nextDenotTransformerId(cur.validFor.lastPhaseId)
793+
if (currentPeriod.lastPhaseId <= nextTransformerId)
794+
cur.validFor = Period(currentPeriod.runId, cur.validFor.firstPhaseId, nextTransformerId)
795+
else
796+
var startPid = nextTransformerId + 1
797+
val transformer = ctx.base.denotTransformers(nextTransformerId)
798+
//println(s"transforming $this with $transformer")
799+
val savedPeriod = ctx.period
800+
val mutCtx = ctx.asInstanceOf[FreshContext]
801+
try
802+
mutCtx.setPhase(transformer)
803+
next = transformer.transform(cur)
804+
// We temporarily update the context with the new phase instead of creating a
805+
// new one. This is done for performance. We cut down on about 30% of context
806+
// creations that way, and also avoid phase caches in contexts to get large.
807+
// To work correctly, we need to demand that the context with the new phase
808+
// is not retained in the result.
809+
catch case ex: CyclicReference =>
810+
signalError()
811+
throw ex
812+
finally
813+
mutCtx.setPeriod(savedPeriod)
814+
if next eq cur then
815+
startPid = cur.validFor.firstPhaseId
816+
else
817+
assertNotPackage(next, transformer)
818+
next.insertAfter(cur)
819+
cur = next
820+
cur.validFor = Period(currentPeriod.runId, startPid, transformer.lastPhaseId)
821+
//printPeriods(cur)
822+
//println(s"new denot: $cur, valid for ${cur.validFor}")
823+
cur.current // multiple transformations could be required
824+
end goForward
825+
826+
def goBack: SingleDenotation =
827+
// currentPeriod < end of valid; in this case a version must exist
828+
// but to be defensive we check for infinite loop anyway
829+
var cur = this
830+
var cnt = 0
831+
while !(cur.validFor contains currentPeriod) do
832+
//println(s"searching: $cur at $currentPeriod, valid for ${cur.validFor}")
833+
cur = cur.nextInRun
834+
// Note: One might be tempted to add a `prev` field to get to the new denotation
835+
// more directly here. I tried that, but it degrades rather than improves
836+
// performance: Test setup: Compile everything in dotc and immediate subdirectories
837+
// 10 times. Best out of 10: 18154ms with `prev` field, 17777ms without.
838+
cnt += 1
839+
if cnt > MaxPossiblePhaseId then
840+
return atPhase(coveredInterval.firstPhaseId)(current)
841+
cur
842+
end goBack
843+
844+
if valid.code <= 0 then
845+
// can happen if we sit on a stale denotation which has been replaced
846+
// wholesale by an installAfter; in this case, proceed to the next
847+
// denotation and try again.
848+
escapeToNext
849+
else if valid.runId != currentPeriod.runId then
850+
toNewRun
851+
else if currentPeriod.code > valid.code then
852+
goForward
853+
else
854+
goBack
855+
end current
849856

850857
private def demandOutsideDefinedMsg(using Context): String =
851858
s"demanding denotation of $this at phase ${ctx.phase}(${ctx.phaseId}) outside defined interval: defined periods are${definedPeriodsString}"
@@ -900,7 +907,7 @@ object Denotations {
900907

901908
/** Insert this denotation instead of `old`.
902909
* Also ensure that `old` refers with `nextInRun` to this denotation
903-
* and set its `validFor` field to `NoWhere`. This is necessary so that
910+
* and set its `validFor` field to `Nowhere`. This is necessary so that
904911
* references to the old denotation can be brought forward via `current`
905912
* to a valid denotation.
906913
*

0 commit comments

Comments
 (0)