Skip to content

Commit d6646de

Browse files
committed
Streamline treatment of withPhase and withSource
- Use a joint SimpleIdentityMap for both. - Set phases destructively when transforming Denotations.
1 parent d83d672 commit d6646de

File tree

2 files changed

+51
-52
lines changed

2 files changed

+51
-52
lines changed

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

+44-51
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ object Contexts {
6969

7070
/** Execute `op` at given phase */
7171
inline def atPhase[T](phase: Phase)(inline op: Context ?=> T)(using Context): T =
72-
atPhase(phase.id)(op)
72+
op(using ctx.withPhase(phase))
7373

7474
inline def atNextPhase[T](inline op: Context ?=> T)(using Context): T =
7575
atPhase(ctx.phase.next)(op)
@@ -262,7 +262,7 @@ object Contexts {
262262

263263
/** Sourcefile corresponding to given abstract file, memoized */
264264
def getSource(file: AbstractFile, codec: => Codec = Codec(settings.encoding.value)) = {
265-
util.Stats.record("getSource")
265+
util.Stats.record("Context.getSource")
266266
base.sources.getOrElseUpdate(file, new SourceFile(file, codec))
267267
}
268268

@@ -285,33 +285,49 @@ object Contexts {
285285
/** Sourcefile with given path, memoized */
286286
def getSource(path: String): SourceFile = getSource(path.toTermName)
287287

288-
/** Those fields are used to cache phases created in withPhase.
289-
* phasedCtx is first phase with altered phase ever requested.
290-
* phasedCtxs is array that uses phaseId's as indexes,
291-
* contexts are created only on request and cached in this array
292-
*/
293-
private var phasedCtx: Context = this
294-
private var phasedCtxs: Array[Context] = null
288+
private var related: SimpleIdentityMap[Phase | SourceFile, Context] = SimpleIdentityMap.Empty
295289

296-
/** This context at given phase.
297-
* This method will always return a phase period equal to phaseId, thus will never return a fused phase
298-
*/
299-
final def withPhase(phaseId: PhaseId): Context =
300-
if (this.period.phaseId == phaseId) this
301-
else if (phasedCtx.period.phaseId == phaseId) phasedCtx
302-
else if (phasedCtxs != null && phasedCtxs(phaseId) != null) phasedCtxs(phaseId)
303-
else {
304-
val ctx1 = fresh.setPhase(phaseId)
305-
if (phasedCtx eq this) phasedCtx = ctx1
306-
else {
307-
if (phasedCtxs == null) phasedCtxs = new Array[Context](base.phases.length)
308-
phasedCtxs(phaseId) = ctx1
309-
}
290+
private def lookup(key: Phase | SourceFile): Context =
291+
util.Stats.record("Context.related.lookup")
292+
if related == null then
293+
related = SimpleIdentityMap.Empty
294+
null
295+
else
296+
related(key)
297+
298+
private def withPhase(phase: Phase, pid: PhaseId): Context =
299+
util.Stats.record("Context.withPhase")
300+
val curId = phaseId
301+
if curId == pid then
302+
this
303+
else
304+
var ctx1 = lookup(phase)
305+
if ctx1 == null then
306+
util.Stats.record("Context.withPhase.new")
307+
ctx1 = fresh.setPhase(pid)
308+
related = related.updated(phase, ctx1)
309+
ctx1
310+
311+
final def withPhase(phase: Phase): Context = withPhase(phase, phase.id)
312+
final def withPhase(pid: PhaseId): Context = withPhase(base.phases(pid), pid)
313+
314+
final def withSource(source: SourceFile): Context =
315+
util.Stats.record("Context.withSource")
316+
if this.source eq source then
317+
this
318+
else
319+
var ctx1 = lookup(source)
320+
if ctx1 == null then
321+
util.Stats.record("Context.withSource.new")
322+
ctx1 = fresh.setSource(source)
323+
related = related.updated(source, ctx1)
310324
ctx1
311-
}
312325

313-
final def withPhase(phase: Phase): Context =
314-
withPhase(phase.id)
326+
inline def evalAt[T](phase: Phase)(inline op: Context ?=> T): T =
327+
val saved = period
328+
this.asInstanceOf[FreshContext].period = Period(runId, phase.id)
329+
try op(using this)
330+
finally period = saved
315331

316332
// `creationTrace`-related code. To enable, uncomment the code below and the
317333
// call to `setCreationTrace()` in this file.
@@ -476,8 +492,7 @@ object Contexts {
476492

477493
def reuseIn(outer: Context): this.type =
478494
implicitsCache = null
479-
phasedCtxs = null
480-
sourceCtx = null
495+
related = null
481496
init(outer, outer)
482497

483498
/** A fresh clone of this context embedded in this context. */
@@ -491,29 +506,6 @@ object Contexts {
491506
final def withOwner(owner: Symbol): Context =
492507
if (owner ne this.owner) fresh.setOwner(owner) else this
493508

494-
private var sourceCtx: SimpleIdentityMap[SourceFile, Context] = null
495-
496-
final def withSource(source: SourceFile): Context =
497-
if (source `eq` this.source) this
498-
else if ((source `eq` outer.source) &&
499-
outer.sourceCtx != null &&
500-
(outer.sourceCtx(this.source) `eq` this)) outer
501-
else {
502-
if (sourceCtx == null) sourceCtx = SimpleIdentityMap.Empty
503-
val prev = sourceCtx(source)
504-
if (prev != null) prev
505-
else {
506-
val newCtx = fresh.setSource(source)
507-
if (newCtx.compilationUnit == null)
508-
// `source` might correspond to a file not necessarily
509-
// in the current project (e.g. when inlining library code),
510-
// so set `mustExist` to false.
511-
newCtx.setCompilationUnit(CompilationUnit(source, mustExist = false))
512-
sourceCtx = sourceCtx.updated(source, newCtx)
513-
newCtx
514-
}
515-
}
516-
517509
final def withProperty[T](key: Key[T], value: Option[T]): Context =
518510
if (property(key) == value) this
519511
else value match {
@@ -732,6 +724,7 @@ object Contexts {
732724
result
733725

734726
inline def comparing[T](inline op: TypeComparer => T)(using Context): T =
727+
util.Stats.record("comparing")
735728
val saved = ctx.base.comparersInUse
736729
try op(comparer)
737730
finally ctx.base.comparersInUse = saved

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,13 @@ object Denotations {
797797
val transformer = ctx.base.denotTransformers(nextTransformerId)
798798
//println(s"transforming $this with $transformer")
799799
try
800-
next = atPhase(transformer)(transformer.transform(cur))
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.
801807
catch {
802808
case ex: CyclicReference =>
803809
println(s"error while transforming $this") // DEBUG

0 commit comments

Comments
 (0)