@@ -10,7 +10,6 @@ import StdNames.*
1010import NameKinds .OuterSelectName
1111
1212import ast .tpd .*
13- import util .EqHashMap
1413import config .Printers .init as printer
1514import reporting .trace as log
1615
@@ -198,17 +197,51 @@ object Semantic:
198197 * Note: It's tempting to use location of trees as key. That should
199198 * be avoided as a template may have the same location as its single
200199 * statement body. Macros may also create incorrect locations.
201- *
202200 */
203201
204202 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+ */
206216 private type Heap = Map [Ref , Objekt ]
207217
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+
208241 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
212245 private var changed : Boolean = false
213246
214247 /** Abstract heap stores abstract objects
@@ -243,8 +276,8 @@ object Semantic:
243276 current.contains(value, expr) || stable.contains(value, expr)
244277
245278 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)
248281
249282 /** Copy the value of `(value, expr)` from the last cache to the current cache
250283 * (assuming it's `Hot` if it doesn't exist in the cache).
@@ -256,16 +289,16 @@ object Semantic:
256289 if last.contains(value, expr) then
257290 last.get(value, expr)
258291 else
259- last.put (value, expr, Hot )
292+ this . last = last.updatedNested (value, expr, Hot )
260293 Hot
261294 end if
262- current.put (value, expr, assumeValue)
295+ this . current = current.updatedNested (value, expr, assumeValue)
263296
264297 val actual = fun
265298 if actual != assumeValue then
266299 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)
269302 else
270303 // It's tempting to cache the value in stable, but it's unsound.
271304 // The reason is that the current value may depend on other values
@@ -280,12 +313,12 @@ object Semantic:
280313
281314 /** Commit current cache to stable cache. */
282315 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)
289322
290323 /** Prepare cache for the next iteration
291324 *
@@ -298,7 +331,7 @@ object Semantic:
298331 */
299332 def prepareForNextIteration ()(using Context ) =
300333 this .changed = false
301- this .current = mutable. Map .empty
334+ this .current = Map .empty
302335 this .heap = this .heapStable
303336
304337 /** Prepare for checking next class
@@ -319,8 +352,8 @@ object Semantic:
319352 this .commitToStableCache()
320353 this .heapStable = this .heap
321354
322- this .last = mutable. Map .empty
323- this .current = mutable. Map .empty
355+ this .last = Map .empty
356+ this .current = Map .empty
324357
325358 def updateObject (ref : Ref , obj : Objekt ) =
326359 assert(! this .heapStable.contains(ref))
@@ -331,14 +364,28 @@ object Semantic:
331364 def getObject (ref : Ref ) = heap(ref)
332365 end Cache
333366
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)
342389 end extension
343390 end Cache
344391
0 commit comments