Skip to content

Commit

Permalink
Fix #505: Remove unnecessary boxing of synchronized block
Browse files Browse the repository at this point in the history
`synchronized` is now treated as a magic method by the compiler and is no
longer erased (similarly to `asInstanceOf` and `isInstanceOf`). Backend
is updated to handle this new magic polymorphic method.
  • Loading branch information
allanrenucci committed Nov 8, 2018
1 parent f47c720 commit 3d33bae
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
val Throwable_Type: Type = defn.ThrowableType
val Object_isInstanceOf: Symbol = defn.Any_isInstanceOf
val Object_asInstanceOf: Symbol = defn.Any_asInstanceOf
val Object_synchronized: Symbol = defn.Object_synchronized
val Object_equals: Symbol = defn.Any_equals
val ArrayClass: Symbol = defn.ArrayClass
val UnitClass: Symbol = defn.UnitClass
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,7 @@ class Definitions {
lazy val NoInitClasses: Set[Symbol] = NotRuntimeClasses + FunctionXXLClass

def isPolymorphicAfterErasure(sym: Symbol): Boolean =
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf) || (sym eq Object_synchronized)

def isTupleType(tp: Type)(implicit ctx: Context): Boolean = {
val arity = tp.dealias.argInfos.length
Expand Down
9 changes: 4 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/LazyVals.scala
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,10 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
val initSymbol = ctx.newSymbol(x.symbol.owner, initName, initFlags, MethodType(Nil, tpe), coord = x.pos)
val rhs = x.rhs.changeOwnerAfter(x.symbol, initSymbol, this)
val initialize = holderRef.select(lazyNme.initialize).appliedTo(rhs)
val initBody =
adaptToType(
holderRef.select(defn.Object_synchronized).appliedTo(
adaptToType(If(initialized, getValue, initialize), defn.ObjectType)),
tpe)
val initBody = holderRef
.select(defn.Object_synchronized)
.appliedToType(tpe)
.appliedTo(If(initialized, getValue, initialize).ensureConforms(tpe))
val initTree = DefDef(initSymbol, initBody)

// def x(): Int = if (x$lzy.initialized()) x$lzy.value() else x$lzycompute()
Expand Down
26 changes: 25 additions & 1 deletion compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ class TestBCode extends DottyBytecodeTest {
/** Test that the size of the lazy val initialiazer is under a certain threshold
*
* - Fix to #5340 reduced the size from 39 instructions to 34
* - Fix to #505 reduced the size from 34 instructions to 32
*/
@Test def i5340 = {
val source =
Expand All @@ -519,7 +520,30 @@ class TestBCode extends DottyBytecodeTest {
val clsIn = dir.lookupName("Test.class", directory = false).input
val clsNode = loadClassNode(clsIn)
val method = getMethod(clsNode, "x$lzyINIT1$1")
assertEquals(34, instructionsFromMethod(method).size)
assertEquals(32, instructionsFromMethod(method).size)
}
}

/** Test that synchronize blocks don't box */
@Test def i505 = {
val source =
"""class Test {
| def test: Int = synchronized(1)
|}
""".stripMargin

checkBCode(source) { dir =>
val clsIn = dir.lookupName("Test.class", directory = false).input
val clsNode = loadClassNode(clsIn)
val method = getMethod(clsNode, "test")

val doBox = instructionsFromMethod(method).exists {
case Invoke(_, _, name, _, _) =>
name == "boxToInteger" || name == "unboxToInt"
case _ =>
false
}
assertFalse(doBox)
}
}
}
15 changes: 15 additions & 0 deletions tests/run/i505.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
object Test {
def main(args: Array[String]): Unit = {
val a: Int = synchronized(1)
val b: Long = synchronized(1L)
val c: Boolean = synchronized(true)
val d: Float = synchronized(1f)
val e: Double = synchronized(1.0)
val f: Byte = synchronized(1.toByte)
val g: Char = synchronized('1')
val h: Short = synchronized(1.toShort)
val i: String = synchronized("Hello")
val j: List[Int] = synchronized(List(1))
synchronized(())
}
}

0 comments on commit 3d33bae

Please sign in to comment.