Skip to content

Commit 754410e

Browse files
committed
Initial working lazy val reimplementation with VarHandles
1 parent e914efe commit 754410e

File tree

4 files changed

+65
-32
lines changed

4 files changed

+65
-32
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,10 @@ class Definitions {
756756
def JavaEnumType = JavaEnumClass.typeRef
757757

758758
@tu lazy val MethodHandleClass: ClassSymbol = requiredClass("java.lang.invoke.MethodHandle")
759+
@tu lazy val MethodHandlesClass: TermSymbol = requiredModule("java.lang.invoke.MethodHandles")
760+
@tu lazy val MethodHandles_lookup: Symbol = MethodHandlesClass.requiredMethod("lookup")
759761
@tu lazy val MethodHandlesLookupClass: ClassSymbol = requiredClass("java.lang.invoke.MethodHandles.Lookup")
762+
@tu lazy val MethodHandlesLookup_FindVarHandle: Symbol = MethodHandlesLookupClass.requiredMethod("findVarHandle")
760763
@tu lazy val VarHandleClass: ClassSymbol = requiredClass("java.lang.invoke.VarHandle")
761764

762765
@tu lazy val StringBuilderClass: ClassSymbol = requiredClass("scala.collection.mutable.StringBuilder")

compiler/src/dotty/tools/dotc/transform/LazyVals.scala

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
2828
* The map contains the list of the offset trees.
2929
*/
3030
class OffsetInfo(var defs: List[Tree], var ord: Int = 0)
31+
class VarHandleInfo(var defs: List[Tree])
3132

3233
private val appendOffsetDefs = mutable.Map.empty[Symbol, OffsetInfo]
34+
private val appendVarHandleDefs = mutable.Map.empty[Symbol, VarHandleInfo]
3335

3436
override def phaseName: String = LazyVals.name
3537

@@ -109,12 +111,19 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
109111
*/
110112
override def transformTemplate(template: Template)(using Context): Tree = {
111113
val cls = ctx.owner.asClass
112-
appendOffsetDefs.get(cls) match {
113-
case None => template
114-
case Some(data) =>
115-
data.defs.foreach(defin => defin.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, defin.symbol.span)))
116-
cpy.Template(template)(body = addInFront(data.defs, template.body))
117-
}
114+
if ctx.settings.YlegacyLazyVals.value then
115+
appendOffsetDefs.get(cls) match {
116+
case None => template
117+
case Some(data) =>
118+
data.defs.foreach(defin => defin.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, defin.symbol.span)))
119+
cpy.Template(template)(body = addInFront(data.defs, template.body))
120+
}
121+
else
122+
appendVarHandleDefs.get(cls) match {
123+
case None => template
124+
case Some(data) =>
125+
cpy.Template(template)(body = addInFront(data.defs, template.body))
126+
}
118127
}
119128

120129
private def addInFront(prefix: List[Tree], stats: List[Tree]) = stats match {
@@ -328,20 +337,20 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
328337
* @param memberDef the transformed lazy field member definition
329338
* @param claz the class containing this lazy val field
330339
* @param target the target synthetic field
331-
* @param offset the offset of the field in the storage allocation of the class
340+
* @param varHandle the VarHandle of the field
332341
* @param thiz a reference to the transformed class
333342
*/
334343
def mkThreadSafeDef(memberDef: ValOrDefDef,
335344
claz: ClassSymbol,
336345
target: Symbol,
337-
offset: Tree,
346+
varHandle: Tree,
338347
thiz: Tree)(using Context): (DefDef, DefDef) = {
339348
val tp = memberDef.tpe.widenDealias.resultType.widenDealias
340349
val waiting = ref(defn.LazyValsWaitingState)
341350
val controlState = ref(defn.LazyValsControlState)
342351
val evaluating = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.evaluating)
343352
val nullValue = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.nullValue)
344-
val objCasFlag = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.objCas)
353+
val objCas2Flag = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.objCas2)
345354
val accessorMethodSymbol = memberDef.symbol.asTerm
346355
val lazyInitMethodName = LazyLocalInitName.fresh(memberDef.name.asTermName)
347356
val lazyInitMethodSymbol = newSymbol(claz, lazyInitMethodName, Synthetic | Method | Private, MethodType(Nil)(_ => Nil, _ => defn.ObjectType))
@@ -383,12 +392,12 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
383392
val lockRel = {
384393
val lockSymb = newSymbol(lazyInitMethodSymbol, lazyNme.lock, Synthetic, waiting.typeOpt)
385394
Block(ValDef(lockSymb, ref(target).cast(waiting.typeOpt))
386-
:: objCasFlag.appliedTo(thiz, offset, ref(lockSymb), ref(resSymb)) :: Nil,
395+
:: objCas2Flag.appliedTo(thiz, varHandle, ref(lockSymb), ref(resSymb)) :: Nil,
387396
ref(lockSymb).select(lazyNme.RLazyVals.waitingRelease).ensureApplied)
388397
}
389398
// finally block
390399
val fin = If(
391-
objCasFlag.appliedTo(thiz, offset, evaluating, ref(resSymb)).select(nme.UNARY_!).appliedToNone,
400+
objCas2Flag.appliedTo(thiz, varHandle, evaluating, ref(resSymb)).select(nme.UNARY_!).appliedToNone,
392401
lockRel,
393402
unitLiteral
394403
)
@@ -409,7 +418,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
409418
)
410419
// if CAS(_, null, Evaluating)
411420
If(
412-
objCasFlag.appliedTo(thiz, offset, nullLiteral, evaluating),
421+
objCas2Flag.appliedTo(thiz, varHandle, nullLiteral, evaluating),
413422
Block(ValDef(resSymb, nullLiteral) :: ValDef(resSymbNullable, nullLiteral) :: evaluate :: Nil, // var result: AnyRef = null
414423
Return(ref(resSymbNullable), lazyInitMethodSymbol)),
415424
unitLiteral
@@ -425,7 +434,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
425434
ref(current).select(defn.Object_eq).appliedTo(evaluating),
426435
// if is Evaluating then CAS(_, Evaluating, new Waiting)
427436
Block(
428-
objCasFlag.appliedTo(thiz, offset, ref(current), Select(New(waiting), StdNames.nme.CONSTRUCTOR).ensureApplied) :: Nil,
437+
objCas2Flag.appliedTo(thiz, varHandle, ref(current), Select(New(waiting), StdNames.nme.CONSTRUCTOR).ensureApplied) :: Nil,
429438
unitLiteral
430439
),
431440
// if not Evaluating
@@ -461,7 +470,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
461470
val claz = x.symbol.owner.asClass
462471
val thizClass = Literal(Constant(claz.info))
463472

464-
def offsetName(id: Int) = s"${StdNames.nme.LAZY_FIELD_OFFSET}${if (x.symbol.owner.is(Module)) "_m_" else ""}$id".toTermName
465473
val containerName = LazyLocalName.fresh(x.name.asTermName)
466474
val containerSymbol = newSymbol(claz, containerName, x.symbol.flags &~ containerFlagsMask | containerFlags | Private, defn.ObjectType, coord = x.symbol.coord).enteredAfter(this)
467475
containerSymbol.addAnnotation(Annotation(defn.VolatileAnnot, containerSymbol.span)) // private @volatile var _x: AnyRef
@@ -471,23 +479,22 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
471479
Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.getOffsetStatic)
472480
val containerTree = ValDef(containerSymbol, nullLiteral)
473481

474-
// create an offset for this lazy val
475-
val offsetSymbol: TermSymbol = appendOffsetDefs.get(claz) match
476-
case Some(info) =>
477-
newSymbol(claz, offsetName(info.defs.size), Synthetic, defn.LongType).enteredAfter(this)
478-
case None =>
479-
newSymbol(claz, offsetName(0), Synthetic, defn.LongType).enteredAfter(this)
480-
offsetSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, offsetSymbol.span))
481-
val fieldTree = thizClass.select(lazyNme.RLazyVals.getDeclaredField).appliedTo(Literal(Constant(containerName.mangledString)))
482-
val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(fieldTree))
483-
val offsetInfo = appendOffsetDefs.getOrElseUpdate(claz, new OffsetInfo(Nil))
484-
offsetInfo.defs = offsetTree :: offsetInfo.defs
485-
val offset = ref(offsetSymbol)
482+
// create a VarHandle for this lazy val
483+
val varHandleSymbol: TermSymbol = newSymbol(claz, s"$containerName${lazyNme.lzyHandleSuffix}".toTermName, Synthetic, defn.VarHandleClass.typeRef).enteredAfter(this)
484+
varHandleSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, varHandleSymbol.span))
485+
val getVarHandle = Apply(
486+
Select(Apply(Select(ref(defn.MethodHandlesClass), defn.MethodHandles_lookup.name), Nil), defn.MethodHandlesLookup_FindVarHandle.name),
487+
List(thizClass, Literal(Constant(containerName.toString)), Literal(Constant(defn.ObjectType)))
488+
)
489+
val varHandleTree = ValDef(varHandleSymbol, getVarHandle)
490+
val varHandle = ref(varHandleSymbol)
486491

492+
val varHandleInfo = appendVarHandleDefs.getOrElseUpdate(claz, new VarHandleInfo(Nil))
493+
varHandleInfo.defs = varHandleTree :: varHandleInfo.defs
487494
val swapOver =
488495
This(claz)
489496

490-
val (accessorDef, initMethodDef) = mkThreadSafeDef(x, claz, containerSymbol, offset, swapOver)
497+
val (accessorDef, initMethodDef) = mkThreadSafeDef(x, claz, containerSymbol, varHandle, swapOver)
491498
Thicket(containerTree, accessorDef, initMethodDef)
492499
}
493500

@@ -667,6 +674,7 @@ object LazyVals {
667674
val evaluating: TermName = "Evaluating".toTermName
668675
val nullValue: TermName = "NullValue".toTermName
669676
val objCas: TermName = "objCAS".toTermName
677+
val objCas2: TermName = "objCAS2".toTermName
670678
val get: TermName = N.get.toTermName
671679
val setFlag: TermName = N.setFlag.toTermName
672680
val wait4Notification: TermName = N.wait4Notification.toTermName
@@ -687,5 +695,9 @@ object LazyVals {
687695
val current: TermName = "current".toTermName
688696
val lock: TermName = "lock".toTermName
689697
val discard: TermName = "discard".toTermName
698+
val lzyHandleSuffix: String = "$$lzyHandle"
690699
}
700+
701+
extension (sym: Symbol) def isVarHandleForLazy(using Context) =
702+
sym.name.endsWith(lazyNme.lzyHandleSuffix)
691703
}

compiler/src/dotty/tools/dotc/transform/MoveStatics.scala

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import SymDenotations.SymDenotation
1212
import Names.Name
1313
import StdNames.nme
1414
import NameOps.*
15+
import LazyVals.isVarHandleForLazy
1516

1617
import ast.*
1718

19+
import scala.collection.mutable
1820

1921
import MegaPhase.*
2022

@@ -28,7 +30,7 @@ class MoveStatics extends MiniPhase with SymTransformer {
2830

2931
def transformSym(sym: SymDenotation)(using Context): SymDenotation =
3032
if (sym.hasAnnotation(defn.ScalaStaticAnnot) && sym.owner.is(Flags.Module) && sym.owner.companionClass.exists &&
31-
(sym.is(Flags.Method) || !(sym.isMutableVarOrAccessor && sym.owner.companionClass.is(Flags.Trait)))) {
33+
(sym.is(Flags.Method) || !(sym.isMutableVarOrAccessor && sym.owner.companionClass.is(Flags.Trait)) && !sym.symbol.isVarHandleForLazy)) {
3234
sym.owner.asClass.delete(sym.symbol)
3335
sym.owner.companionClass.asClass.enter(sym.symbol)
3436
sym.copySymDenotation(owner = sym.owner.companionClass)
@@ -64,12 +66,21 @@ class MoveStatics extends MiniPhase with SymTransformer {
6466
else {
6567
val moduleTmpl = module.rhs.asInstanceOf[Template]
6668
val companionTmpl = companion.rhs.asInstanceOf[Template]
67-
val (staticDefs, remainingDefs) = moduleTmpl.body.partition {
68-
case memberDef: MemberDef => memberDef.symbol.isScalaStatic
69-
case _ => false
69+
70+
val staticDefs = mutable.ListBuffer[Tree]()
71+
val staticTiedDefs = mutable.ListBuffer[Tree]()
72+
val remainingDefs = mutable.ListBuffer[Tree]()
73+
74+
moduleTmpl.body.foreach {
75+
case memberDef: MemberDef if memberDef.symbol.isScalaStatic =>
76+
if memberDef.symbol.isVarHandleForLazy then
77+
staticTiedDefs.addOne(memberDef)
78+
else
79+
staticDefs.addOne(memberDef)
80+
case other => remainingDefs.addOne(other)
7081
}
7182

72-
rebuild(companion, companionTmpl.body ++ staticDefs) :: rebuild(module, remainingDefs) :: Nil
83+
rebuild(companion, companionTmpl.body ++ staticDefs) :: rebuild(module, staticTiedDefs.toList ++ remainingDefs.toList) :: Nil
7384
}
7485
}
7586
val newPairs =

library/src/scala/runtime/LazyVals.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import language.experimental.captureChecking
44
import java.util.concurrent.CountDownLatch
55

66
import scala.annotation.*
7+
import java.lang.invoke.VarHandle
78

89
/**
910
* Helper methods used in thread-safe lazy vals.
@@ -106,6 +107,12 @@ object LazyVals {
106107
unsafe.compareAndSwapObject(t, offset, exp, n): @nowarn("cat=deprecation")
107108
}
108109

110+
def objCAS2(t: Object, handle: VarHandle, exp: Object, n: Object): Boolean = {
111+
if (debug)
112+
println(s"objCAS2($t, $exp, $n)")
113+
handle.compareAndSet(t, exp, n)
114+
}
115+
109116
def setFlag(t: Object, offset: Long, v: Int, ord: Int): Unit = {
110117
if (debug)
111118
println(s"setFlag($t, $offset, $v, $ord)")

0 commit comments

Comments
 (0)