Skip to content

Commit f97dc02

Browse files
committed
Lazily populate warm objects
1 parent 34ab48e commit f97dc02

File tree

1 file changed

+30
-41
lines changed

1 file changed

+30
-41
lines changed

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

Lines changed: 30 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ object Semantic {
9090
*
9191
* Fields in class body are not initialized.
9292
*/
93-
def populateParams(): Contextual[this.type] = log("populating parameters", printer, (_: Warm).objekt.toString) {
93+
private def populateParams(): Contextual[this.type] = log("populating parameters", printer, (_: Warm).objekt.toString) {
9494
assert(!populatingParams, "the object is already populating parameters")
9595
populatingParams = true
9696
val tpl = klass.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template]
@@ -244,7 +244,7 @@ object Semantic {
244244
private type Heap = Map[Ref, Objekt]
245245

246246
class Cache {
247-
private val last: CacheStore = mutable.Map.empty
247+
private var last: CacheStore = mutable.Map.empty
248248
private var current: CacheStore = mutable.Map.empty
249249
private val stable: CacheStore = mutable.Map.empty
250250
private var changed: Boolean = false
@@ -258,8 +258,8 @@ object Semantic {
258258
*/
259259
private var heap: Heap = Map.empty
260260

261-
/** Used to easily revert heap changes. */
262-
private var heapBefore: Heap = Map.empty
261+
/** Used to revert heap to last stable heap. */
262+
private var heapStable: Heap = Map.empty
263263

264264
def hasChanged = changed
265265

@@ -305,7 +305,6 @@ object Semantic {
305305
stable.put(v, e, res)
306306
}
307307
}
308-
current = mutable.Map.empty
309308

310309
/** Prepare cache for the next iteration
311310
*
@@ -323,34 +322,16 @@ object Semantic {
323322
*
324323
*/
325324
def prepareForNextIteration(isStable: Boolean)(using State, Context) =
326-
if isStable then this.commitToStableCache()
325+
if isStable then
326+
this.commitToStableCache()
327+
this.heapStable = this.heap
328+
// If the current iteration is not stable, we need to use `last` for the next iteration,
329+
// which already contains the updated value from the current iteration.
330+
this.last = mutable.Map.empty
327331

328-
changed = false
329-
current = mutable.Map.empty
330-
331-
if !isStable then revertHeapChanges()
332-
heapBefore = this.heap
333-
334-
def revertHeapChanges()(using State, Context) =
335-
printer.println("reverting heap changes")
336-
this.heap.keys.foreach {
337-
case warm: Warm =>
338-
if heapBefore.contains(warm) then
339-
this.heap = heap.updated(warm, heapBefore(warm))
340-
else
341-
// We cannot simply remove the object, as the values in the
342-
// updated cache may refer to the warm object.
343-
given Env = Env.empty
344-
given Trace = Trace.empty
345-
given Promoted = Promoted.empty
346-
printer.println("resetting " + warm)
347-
warm.ensureObjectFreshAndPopulated()
348-
case _ =>
349-
}
350-
351-
// ThisRef objects are not reachable, thus it's fine to leave them in
352-
// the heap
353-
end revertHeapChanges
332+
this.changed = false
333+
this.current = mutable.Map.empty
334+
this.heap = this.heapStable
354335

355336
def updateObject(ref: Ref, obj: Objekt) =
356337
this.heap = this.heap.updated(ref, obj)
@@ -463,11 +444,19 @@ object Semantic {
463444

464445

465446
extension (ref: Ref)
466-
def objekt(using Cache): Objekt = cache.getObject(ref)
447+
def objekt: Contextual[Objekt] =
448+
// TODO: improve performance
449+
ref match
450+
case warm: Warm => warm.ensureObjectExistsAndPopulated()
451+
case _ =>
452+
cache.getObject(ref)
467453

468454
def ensureObjectExists()(using Cache): ref.type =
469-
if cache.containsObject(ref) then ref
470-
else ensureFresh()
455+
if cache.containsObject(ref) then
456+
printer.println("object " + ref + " already exists")
457+
ref
458+
else
459+
ensureFresh()
471460

472461
def ensureFresh()(using Cache): ref.type =
473462
val obj = Objekt(ref.klass, fields = Map.empty, outers = Map(ref.klass -> ref.outer))
@@ -479,9 +468,9 @@ object Semantic {
479468
*
480469
* Invariant: fields are immutable and only set once
481470
*/
482-
def updateField(field: Symbol, value: Value)(using Cache, Context): Unit = log("set field " + field + " of " + ref + " to " + value) {
471+
def updateField(field: Symbol, value: Value): Contextual[Unit] = log("set field " + field + " of " + ref + " to " + value) {
483472
val obj = objekt
484-
assert(!obj.hasField(field), field.show + " already init, new = " + value + ", old = " + obj.field(field) + ", ref = " + ref)
473+
assert(!obj.hasField(field) || field.is(Flags.ParamAccessor) && obj.field(field) == value, field.show + " already init, new = " + value + ", old = " + obj.field(field) + ", ref = " + ref)
485474
val obj2 = obj.copy(fields = obj.fields.updated(field, value))
486475
cache.updateObject(ref, obj2)
487476
}
@@ -490,9 +479,9 @@ object Semantic {
490479
*
491480
* Invariant: outers are immutable and only set once
492481
*/
493-
def updateOuter(klass: ClassSymbol, value: Value)(using Cache, Context): Unit = log("set outer " + klass + " of " + ref + " to " + value) {
482+
def updateOuter(klass: ClassSymbol, value: Value): Contextual[Unit] = log("set outer " + klass + " of " + ref + " to " + value) {
494483
val obj = objekt
495-
assert(!obj.hasOuter(klass), klass.show + " already has outer, new = " + value + ", old = " + obj.outer(klass) + ", ref = " + ref)
484+
assert(!obj.hasOuter(klass) || obj.outer(klass) == value, klass.show + " already has outer, new = " + value + ", old = " + obj.outer(klass) + ", ref = " + ref)
496485
val obj2 = obj.copy(outers = obj.outers.updated(klass, value))
497486
cache.updateObject(ref, obj2)
498487
}
@@ -679,7 +668,7 @@ object Semantic {
679668
Result(Hot, Errors.empty)
680669
else
681670
val outer = Hot
682-
val warm = Warm(klass, outer, ctor, args2).ensureFresh()
671+
val warm = Warm(klass, outer, ctor, args2).ensureObjectExists()
683672
val argInfos2 = args.zip(args2).map { (argInfo, v) => argInfo.copy(value = v) }
684673
val res = warm.callConstructor(ctor, argInfos2, source)
685674
Result(warm, res.errors)
@@ -699,7 +688,7 @@ object Semantic {
699688

700689
val argsWidened = args.map(_.value).widenArgs
701690
val argInfos2 = args.zip(argsWidened).map { (argInfo, v) => argInfo.copy(value = v) }
702-
val warm = Warm(klass, outer, ctor, argsWidened).ensureFresh()
691+
val warm = Warm(klass, outer, ctor, argsWidened).ensureObjectExists()
703692
val res = warm.callConstructor(ctor, argInfos2, source)
704693
Result(warm, res.errors)
705694

0 commit comments

Comments
 (0)