@@ -10,7 +10,6 @@ import StdNames.*
10
10
import NameKinds .OuterSelectName
11
11
12
12
import ast .tpd .*
13
- import util .EqHashMap
14
13
import config .Printers .init as printer
15
14
import reporting .trace as log
16
15
@@ -198,17 +197,51 @@ object Semantic:
198
197
* Note: It's tempting to use location of trees as key. That should
199
198
* be avoided as a template may have the same location as its single
200
199
* statement body. Macros may also create incorrect locations.
201
- *
202
200
*/
203
201
204
202
object Cache :
205
- opaque type CacheStore = mutable.Map [Value , EqHashMap [Tree , Value ]]
203
+ /** Cache for expressions
204
+ *
205
+ * Ref -> Tree -> Value
206
+ *
207
+ * The first key is the value of `this` for the expression. We do not need
208
+ * environment, because the environment is always hot.
209
+ */
210
+ private type ExprValueCache = Map [Value , Map [TreeWrapper , Value ]]
211
+
212
+ /** The heap for abstract objects
213
+ *
214
+ * The heap objects are immutable.
215
+ */
206
216
private type Heap = Map [Ref , Objekt ]
207
217
218
+ /** A wrapper for trees for storage in maps based on referential equality of trees. */
219
+ private abstract class TreeWrapper :
220
+ def tree : Tree
221
+
222
+ override final def equals (other : Any ): Boolean =
223
+ other match
224
+ case that : TreeWrapper => this .tree eq that.tree
225
+ case _ => false
226
+
227
+ override final def hashCode = tree.hashCode
228
+
229
+ /** The immutable wrapper is intended to be stored as key in the heap. */
230
+ private class ImmutableTreeWrapper (val tree : Tree ) extends TreeWrapper
231
+
232
+ /** For queries on the heap, reuse the same wrapper to avoid unnecessary allocation. */
233
+ private class MutableTreeWrapper extends TreeWrapper :
234
+ var queryTree : Tree | Null = null
235
+ def tree : Tree = queryTree match
236
+ case tree : Tree => tree
237
+ case null => ???
238
+
239
+ private val queryTreeMapper : MutableTreeWrapper = new MutableTreeWrapper
240
+
208
241
class Cache :
209
- private var last : CacheStore = mutable. Map .empty
210
- private var current : CacheStore = mutable. Map .empty
211
- private val stable : CacheStore = mutable. Map .empty
242
+ private var last : ExprValueCache = Map .empty
243
+ private var current : ExprValueCache = Map .empty
244
+ private var stable : ExprValueCache = Map .empty
212
245
private var changed : Boolean = false
213
246
214
247
/** Abstract heap stores abstract objects
@@ -243,8 +276,8 @@ object Semantic:
243
276
current.contains(value, expr) || stable.contains(value, expr)
244
277
245
278
def apply (value : Value , expr : Tree ) =
246
- if current.contains(value, expr) then current(value)( expr)
247
- else stable(value)( expr)
279
+ if current.contains(value, expr) then current.get (value, expr)
280
+ else stable.get (value, expr)
248
281
249
282
/** Copy the value of `(value, expr)` from the last cache to the current cache
250
283
* (assuming it's `Hot` if it doesn't exist in the cache).
@@ -256,16 +289,16 @@ object Semantic:
256
289
if last.contains(value, expr) then
257
290
last.get(value, expr)
258
291
else
259
- last.put (value, expr, Hot )
292
+ this . last = last.updatedNested (value, expr, Hot )
260
293
Hot
261
294
end if
262
- current.put (value, expr, assumeValue)
295
+ this . current = current.updatedNested (value, expr, assumeValue)
263
296
264
297
val actual = fun
265
298
if actual != assumeValue then
266
299
this .changed = true
267
- last.put (value, expr, actual)
268
- current.put (value, expr, actual)
300
+ this . last = this .last.updatedNested (value, expr, actual)
301
+ this . current = this .current.updatedNested (value, expr, actual)
269
302
else
270
303
// It's tempting to cache the value in stable, but it's unsound.
271
304
// The reason is that the current value may depend on other values
@@ -280,12 +313,12 @@ object Semantic:
280
313
281
314
/** Commit current cache to stable cache. */
282
315
private def commitToStableCache () =
283
- current.foreach { (v, m) =>
284
- // It's useless to cache value for ThisRef.
285
- if v.isWarm then m.iterator.foreach { (e, res) =>
286
- stable.put(v, e, res)
287
- }
288
- }
316
+ for
317
+ (v, m) <- current
318
+ if v.isWarm // It's useless to cache value for ThisRef.
319
+ (wrapper, res) <- m
320
+ do
321
+ this .stable = stable.updatedNestedWrapper(v, wrapper. asInstanceOf [ ImmutableTreeWrapper ], res)
289
322
290
323
/** Prepare cache for the next iteration
291
324
*
@@ -298,7 +331,7 @@ object Semantic:
298
331
*/
299
332
def prepareForNextIteration ()(using Context ) =
300
333
this .changed = false
301
- this .current = mutable. Map .empty
334
+ this .current = Map .empty
302
335
this .heap = this .heapStable
303
336
304
337
/** Prepare for checking next class
@@ -319,8 +352,8 @@ object Semantic:
319
352
this .commitToStableCache()
320
353
this .heapStable = this .heap
321
354
322
- this .last = mutable. Map .empty
323
- this .current = mutable. Map .empty
355
+ this .last = Map .empty
356
+ this .current = Map .empty
324
357
325
358
def updateObject (ref : Ref , obj : Objekt ) =
326
359
assert(! this .heapStable.contains(ref))
@@ -331,14 +364,28 @@ object Semantic:
331
364
def getObject (ref : Ref ) = heap(ref)
332
365
end Cache
333
366
334
- extension (cache : CacheStore )
335
- def contains (value : Value , expr : Tree ) = cache.contains(value) && cache(value).contains(expr)
336
- def get (value : Value , expr : Tree ): Value = cache(value)(expr)
337
- def remove (value : Value , expr : Tree ) = cache(value).remove(expr)
338
- def put (value : Value , expr : Tree , result : Value ): Unit = {
339
- val innerMap = cache.getOrElseUpdate(value, new EqHashMap [Tree , Value ])
340
- innerMap(expr) = result
341
- }
367
+ extension (cache : ExprValueCache )
368
+ private def contains (value : Value , expr : Tree ) =
369
+ queryTreeMapper.queryTree = expr
370
+ cache.contains(value) && cache(value).contains(queryTreeMapper)
371
+
372
+ private def get (value : Value , expr : Tree ): Value =
373
+ queryTreeMapper.queryTree = expr
374
+ cache(value)(queryTreeMapper)
375
+
376
+ private def removed (value : Value , expr : Tree ) =
377
+ queryTreeMapper.queryTree = expr
378
+ val innerMap2 = cache(value).removed(queryTreeMapper)
379
+ cache.updated(value, innerMap2)
380
+
381
+ private def updatedNested (value : Value , expr : Tree , result : Value ): ExprValueCache =
382
+ val wrapper = new ImmutableTreeWrapper (expr)
383
+ updatedNestedWrapper(value, wrapper, result)
384
+
385
+ private def updatedNestedWrapper (value : Value , wrapper : ImmutableTreeWrapper , result : Value ): ExprValueCache =
386
+ val innerMap = cache.getOrElse(value, Map .empty[TreeWrapper , Value ])
387
+ val innerMap2 = innerMap.updated(wrapper, result)
388
+ cache.updated(value, innerMap2)
342
389
end extension
343
390
end Cache
344
391
0 commit comments