Skip to content

Use WeakHashSet instead of HashSet for hash-consing types #12935

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import dotty.tools.dotc.core.Phases._
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.transform.SymUtils._
import dotty.tools.dotc.util.WeakHashSet

/**
* This class mainly contains the method classBTypeFromSymbol, which extracts the necessary
Expand Down Expand Up @@ -49,7 +48,6 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes {
def newAnyRefMap[K <: AnyRef, V](): mutable.AnyRefMap[K, V] = new mutable.AnyRefMap[K, V]()
def newWeakMap[K, V](): mutable.WeakHashMap[K, V] = new mutable.WeakHashMap[K, V]()
def recordCache[T <: Clearable](cache: T): T = cache
def newWeakSet[K >: Null <: AnyRef](): WeakHashSet[K] = new WeakHashSet[K]()
def newMap[K, V](): mutable.HashMap[K, V] = new mutable.HashMap[K, V]()
def newSet[K](): mutable.Set[K] = new mutable.HashSet[K]
}
Expand All @@ -60,7 +58,6 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes {
def newWeakMap[K, V](): collection.mutable.WeakHashMap[K, V]
def newMap[K, V](): collection.mutable.HashMap[K, V]
def newSet[K](): collection.mutable.Set[K]
def newWeakSet[K >: Null <: AnyRef](): dotty.tools.dotc.util.WeakHashSet[K]
def newAnyRefMap[K <: AnyRef, V](): collection.mutable.AnyRefMap[K, V]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import scala.annotation.threadUnsafe
import scala.collection.generic.Clearable
import scala.collection.mutable
import scala.reflect.ClassTag
import dotty.tools.dotc.util.WeakHashSet
import dotty.tools.io.AbstractFile
import scala.tools.asm.AnnotationVisitor
import dotty.tools.dotc.core._
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ object Contexts {
def platform: Platform = base.platform
def pendingUnderlying: util.HashSet[Type] = base.pendingUnderlying
def uniqueNamedTypes: Uniques.NamedTypeUniques = base.uniqueNamedTypes
def uniques: util.HashSet[Type] = base.uniques
def uniques: util.WeakHashSet[Type] = base.uniques

def initialize()(using Context): Unit = base.initialize()
}
Expand Down
54 changes: 36 additions & 18 deletions compiler/src/dotty/tools/dotc/core/Uniques.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ package core
import Types._, Contexts._, util.Stats._, Hashable._, Names._
import config.Config
import Decorators._
import util.{HashSet, Stats}
import util.{WeakHashSet, Stats}
import WeakHashSet.Entry
import scala.annotation.tailrec

class Uniques extends HashSet[Type](Config.initialUniquesCapacity):
class Uniques extends WeakHashSet[Type](Config.initialUniquesCapacity):
override def hash(x: Type): Int = x.hash
override def isEqual(x: Type, y: Type) = x.eql(y)

Expand All @@ -32,7 +34,7 @@ object Uniques:
if tp.hash == NotCached then tp
else ctx.uniques.put(tp).asInstanceOf[T]

final class NamedTypeUniques extends HashSet[NamedType](Config.initialUniquesCapacity * 4) with Hashable:
final class NamedTypeUniques extends WeakHashSet[NamedType](Config.initialUniquesCapacity * 4) with Hashable:
override def hash(x: NamedType): Int = x.hash

def enterIfNew(prefix: Type, designator: Designator, isTerm: Boolean)(using Context): NamedType =
Expand All @@ -43,17 +45,25 @@ object Uniques:
else new CachedTypeRef(prefix, designator, h)
if h == NotCached then newType
else
// Inlined from WeakHashSet#put
Stats.record(statsItem("put"))
var idx = index(h)
var e = entryAt(idx)
while e != null do
if (e.prefix eq prefix) && (e.designator eq designator) && (e.isTerm == isTerm) then return e
idx = nextIndex(idx)
e = entryAt(idx)
addEntryAt(idx, newType)
removeStaleEntries()
val bucket = index(h)
val oldHead = table(bucket)

@tailrec
def linkedListLoop(entry: Entry[NamedType]): NamedType = entry match
case null => addEntryAt(bucket, newType, h, oldHead)
case _ =>
val e = entry.get
if e != null && (e.prefix eq prefix) && (e.designator eq designator) && (e.isTerm == isTerm) then e
else linkedListLoop(entry.tail)

linkedListLoop(oldHead)
end if
end NamedTypeUniques

final class AppliedUniques extends HashSet[AppliedType](Config.initialUniquesCapacity * 2) with Hashable:
final class AppliedUniques extends WeakHashSet[AppliedType](Config.initialUniquesCapacity * 2) with Hashable:
override def hash(x: AppliedType): Int = x.hash

def enterIfNew(tycon: Type, args: List[Type]): AppliedType =
Expand All @@ -62,13 +72,21 @@ object Uniques:
if monitored then recordCaching(h, classOf[CachedAppliedType])
if h == NotCached then newType
else
// Inlined from WeakHashSet#put
Stats.record(statsItem("put"))
var idx = index(h)
var e = entryAt(idx)
while e != null do
if (e.tycon eq tycon) && e.args.eqElements(args) then return e
idx = nextIndex(idx)
e = entryAt(idx)
addEntryAt(idx, newType)
removeStaleEntries()
val bucket = index(h)
val oldHead = table(bucket)

@tailrec
def linkedListLoop(entry: Entry[AppliedType]): AppliedType = entry match
case null => addEntryAt(bucket, newType, h, oldHead)
case _ =>
val e = entry.get
if e != null && (e.tycon eq tycon) && e.args.eqElements(args) then e
else linkedListLoop(entry.tail)

linkedListLoop(oldHead)
end if
end AppliedUniques
end Uniques
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/util/MutableSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ abstract class MutableSet[T] extends ReadOnlySet[T]:
def +=(x: T): Unit

/** Like `+=` but return existing element equal to `x` of it exists,
* `x` itself otherwose.
* `x` itself otherwise.
*/
def put(x: T): T

Expand Down
Loading