Skip to content

Fix static lazy field holder for GraalVM #16800

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 5 commits into from
Feb 6, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package dotty.tools.benchmarks.lazyvals

import org.openjdk.jmh.annotations.*
import org.openjdk.jmh.infra.Blackhole
import LazyVals.LazyIntHolder
import java.util.concurrent.TimeUnit

@BenchmarkMode(Array(Mode.AverageTime))
@Fork(2)
@Threads(1)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
class InitializedAccessInt {

var holder: LazyIntHolder = _

@Setup
def prepare: Unit = {
holder = new LazyIntHolder
holder.value
}

@Benchmark
def measureInitialized(bh: Blackhole) = {
bh.consume(holder)
bh.consume(holder.value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dotty.tools.benchmarks.lazyvals

import org.openjdk.jmh.annotations.*
import org.openjdk.jmh.infra.Blackhole
import LazyVals.ObjectHolder
import java.util.concurrent.TimeUnit

@BenchmarkMode(Array(Mode.AverageTime))
@Fork(2)
@Threads(1)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
class InitializedObject {

@Benchmark
def measureInitialized(bh: Blackhole) = {
bh.consume(ObjectHolder)
bh.consume(ObjectHolder.value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,22 @@ object LazyVals {
}
}
}

class LazyIntHolder {
lazy val value: Int = {
(System.nanoTime() % 1000).toInt
}
}

object ObjectHolder {
lazy val value: String = {
System.nanoTime() % 5 match {
case 0 => "abc"
case 1 => "def"
case 2 => "ghi"
case 3 => "jkl"
case 4 => "mno"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {

// !!! Part of this logic is duplicated in JSCodeGen.genCompilationUnit
claszSymbol.info.decls.foreach { f =>
if f.isField && !f.name.is(LazyBitMapName) then
if f.isField && !f.name.is(LazyBitMapName) && !f.name.is(LazyLocalName) then
f.setFlag(JavaStatic)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import dotty.tools.dotc.report
import tpd._

import StdNames.nme
import NameKinds.LazyBitMapName
import NameKinds.{LazyBitMapName, LazyLocalName}
import Names.Name

class DottyBackendInterface(val outputDirectory: AbstractFile, val superCallsMap: ReadOnlyMap[Symbol, Set[ClassSymbol]])(using val ctx: Context) {
Expand Down Expand Up @@ -129,10 +129,11 @@ object DottyBackendInterface {
* the new lazy val encoding: https://github.com/lampepfl/dotty/issues/7140
*/
def isStaticModuleField(using Context): Boolean =
sym.owner.isStaticModuleClass && sym.isField && !sym.name.is(LazyBitMapName)
sym.owner.isStaticModuleClass && sym.isField && !sym.name.is(LazyBitMapName) && !sym.name.is(LazyLocalName)

def isStaticMember(using Context): Boolean = (sym ne NoSymbol) &&
(sym.is(JavaStatic) || sym.isScalaStatic || sym.isStaticModuleField)
(sym.is(JavaStatic) || sym.isScalaStatic || sym.isStaticModuleField)

// guard against no sumbol cause this code is executed to select which call type(static\dynamic) to use to call array.clone

/**
Expand Down
11 changes: 1 addition & 10 deletions compiler/src/dotty/tools/dotc/transform/LazyVals.scala
Original file line number Diff line number Diff line change
Expand Up @@ -466,13 +466,8 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
val containerSymbol = newSymbol(claz, containerName, x.symbol.flags &~ containerFlagsMask | containerFlags | Private, defn.ObjectType, coord = x.symbol.coord).enteredAfter(this)
containerSymbol.addAnnotation(Annotation(defn.VolatileAnnot, containerSymbol.span)) // private @volatile var _x: AnyRef
containerSymbol.addAnnotations(x.symbol.annotations) // pass annotations from original definition
val stat = x.symbol.isStatic
if stat then
containerSymbol.setFlag(JavaStatic)
containerSymbol.removeAnnotation(defn.ScalaStaticAnnot)
val getOffset =
if stat then
Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.getStaticFieldOffset)
else
Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.getOffsetStatic)
val containerTree = ValDef(containerSymbol, nullLiteral)

Expand All @@ -490,9 +485,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
val offset = ref(offsetSymbol.nn)

val swapOver =
if stat then
tpd.clsOf(x.symbol.owner.typeRef)
else
This(claz)

val (accessorDef, initMethodDef) = mkThreadSafeDef(x, claz, containerSymbol, offset, swapOver)
Expand Down Expand Up @@ -682,7 +674,6 @@ object LazyVals {
val cas: TermName = N.cas.toTermName
val getOffset: TermName = N.getOffset.toTermName
val getOffsetStatic: TermName = "getOffsetStatic".toTermName
val getStaticFieldOffset: TermName = "getStaticFieldOffset".toTermName
val getDeclaredField: TermName = "getDeclaredField".toTermName
}
val flag: TermName = "flag".toTermName
Expand Down
24 changes: 12 additions & 12 deletions tests/printing/transformed/lazy-vals-new.check
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,30 @@ package <empty> {
@static private def <clinit>(): Unit =
{
A.OFFSET$_m_0 =
scala.runtime.LazyVals.getStaticFieldOffset(
scala.runtime.LazyVals.getOffsetStatic(
classOf[Object {...}].getDeclaredField("x$lzy1"))
()
}
@static @static val OFFSET$_m_0: Long =
scala.runtime.LazyVals.getStaticFieldOffset(
scala.runtime.LazyVals.getOffsetStatic(
classOf[Object {...}].getDeclaredField("x$lzy1"))
private def writeReplace(): Object =
new scala.runtime.ModuleSerializationProxy(classOf[A])
@volatile private lazy <static> var x$lzy1: Object = null
@volatile private lazy var x$lzy1: Object = null
lazy def x(): Int =
{
val result: Object = A#x$lzy1
val result: Object = A.x$lzy1
if result.isInstanceOf[Int] then scala.Int.unbox(result) else
if result.eq(scala.runtime.LazyVals.NullValue) then
scala.Int.unbox(null) else scala.Int.unbox(A.x$lzyINIT1())
}
private def x$lzyINIT1(): Object =
while <empty> do
{
val current: Object = A#x$lzy1
val current: Object = A.x$lzy1
if current.eq(null) then
if
scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0, null,
scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, null,
scala.runtime.LazyVals.Evaluating)
then
{
Expand All @@ -49,15 +49,15 @@ package <empty> {
}
finally
if
scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0,
scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0,
scala.runtime.LazyVals.Evaluating, result).unary_!()
then
{
val lock: scala.runtime.LazyVals.LazyVals$Waiting =
A#x$lzy1.asInstanceOf[
A.x$lzy1.asInstanceOf[
scala.runtime.LazyVals.LazyVals$Waiting]
scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0,
lock, result)
scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, lock,
result)
lock.countDown()
}
else ()
Expand All @@ -71,8 +71,8 @@ package <empty> {
then
if current.eq(scala.runtime.LazyVals.Evaluating) then
{
scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0,
current, new scala.runtime.LazyVals.LazyVals$Waiting())
scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, current,
new scala.runtime.LazyVals.LazyVals$Waiting())
()
}
else
Expand Down