Skip to content

Commit 57f3efb

Browse files
authored
Merge pull request #5446 from dotty-staging/lazy-vals
Reduce size of generated code for lazy fields
2 parents 2698733 + 236815b commit 57f3efb

File tree

2 files changed

+46
-23
lines changed

2 files changed

+46
-23
lines changed

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

+27-23
Original file line numberDiff line numberDiff line change
@@ -206,22 +206,25 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
206206
/** Create non-threadsafe lazy accessor equivalent to such code
207207
* ```
208208
* def methodSymbol() = {
209-
* if (flag) target
210-
* else {
209+
* if (!flag) {
211210
* target = rhs
212211
* flag = true
213212
* nullable = null
214-
* target
215-
* }
216213
* }
214+
* target
217215
* }
218216
* ```
219217
*/
220-
def mkNonThreadSafeDef(target: Tree, flag: Tree, rhs: Tree, nullables: List[Symbol])(implicit ctx: Context): If = {
221-
val stats = new mutable.ListBuffer[Tree]
222-
if (!isWildcardArg(rhs)) stats += target.becomes(rhs)
223-
stats += flag.becomes(Literal(Constant(true))) ++= nullOut(nullables)
224-
If(flag.ensureApplied, target.ensureApplied, Block(stats.toList, target.ensureApplied))
218+
def mkNonThreadSafeDef(sym: Symbol, flag: Symbol, target: Symbol, rhs: Tree)(implicit ctx: Context): DefDef = {
219+
val targetRef = ref(target)
220+
val flagRef = ref(flag)
221+
val stats = targetRef.becomes(rhs) :: flagRef.becomes(Literal(Constant(true))) :: nullOut(nullableFor(sym))
222+
val init = If(
223+
flagRef.ensureApplied.select(nme.UNARY_!).ensureApplied,
224+
Block(stats.init, stats.last),
225+
unitLiteral
226+
)
227+
DefDef(sym.asTerm, Block(List(init), targetRef.ensureApplied))
225228
}
226229

227230
/** Create non-threadsafe lazy accessor for not-nullable types equivalent to such code
@@ -230,18 +233,20 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
230233
* if (target eq null) {
231234
* target = rhs
232235
* nullable = null
233-
* target
234-
* } else target
236+
* }
237+
* target
235238
* }
236239
* ```
237240
*/
238-
def mkDefNonThreadSafeNonNullable(target: Symbol, rhs: Tree, nullables: List[Symbol])(implicit ctx: Context): If = {
239-
val cond = ref(target).select(nme.eq).appliedTo(Literal(Constant(null)))
240-
val exp = ref(target)
241-
val setTarget = exp.becomes(rhs)
242-
val setNullables = nullOut(nullables)
243-
val init = Block(setTarget :: setNullables, exp)
244-
If(cond, init, exp)
241+
def mkDefNonThreadSafeNonNullable(sym: Symbol, target: Symbol, rhs: Tree)(implicit ctx: Context): DefDef = {
242+
val targetRef = ref(target)
243+
val stats = targetRef.becomes(rhs) :: nullOut(nullableFor(sym))
244+
val init = If(
245+
targetRef.select(nme.eq).appliedTo(Literal(Constant(null))),
246+
Block(stats.init, stats.last),
247+
unitLiteral
248+
)
249+
DefDef(sym.asTerm, Block(List(init), targetRef.ensureApplied))
245250
}
246251

247252
def transformMemberDefNonVolatile(x: ValOrDefDef)(implicit ctx: Context): Thicket = {
@@ -255,16 +260,15 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
255260
).enteredAfter(this)
256261

257262
val containerTree = ValDef(containerSymbol, defaultValue(tpe))
258-
if (x.tpe.isNotNull && tpe <:< defn.ObjectType) { // can use 'null' value instead of flag
259-
val slowPath = DefDef(x.symbol.asTerm, mkDefNonThreadSafeNonNullable(containerSymbol, x.rhs, nullableFor(x.symbol)))
260-
Thicket(containerTree, slowPath)
263+
if (x.tpe.isNotNull && tpe <:< defn.ObjectType) {
264+
// can use 'null' value instead of flag
265+
Thicket(containerTree, mkDefNonThreadSafeNonNullable(x.symbol, containerSymbol, x.rhs))
261266
}
262267
else {
263268
val flagName = LazyBitMapName.fresh(x.name.asTermName)
264269
val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags | Flags.Private, defn.BooleanType).enteredAfter(this)
265270
val flag = ValDef(flagSymbol, Literal(Constant(false)))
266-
val slowPath = DefDef(x.symbol.asTerm, mkNonThreadSafeDef(ref(containerSymbol), ref(flagSymbol), x.rhs, nullableFor(x.symbol)))
267-
Thicket(containerTree, flag, slowPath)
271+
Thicket(containerTree, flag, mkNonThreadSafeDef(x.symbol, flagSymbol, containerSymbol, x.rhs))
268272
}
269273
}
270274

compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala

+19
Original file line numberDiff line numberDiff line change
@@ -546,4 +546,23 @@ class TestBCode extends DottyBytecodeTest {
546546
assertFalse(doBox)
547547
}
548548
}
549+
550+
/** Test that the size of lazy field accesors is under a certain threshold
551+
*
552+
* - Changed from 19 to 14
553+
*/
554+
@Test def lazyFields = {
555+
val source =
556+
"""class Test {
557+
| lazy val test = 1
558+
|}
559+
""".stripMargin
560+
561+
checkBCode(source) { dir =>
562+
val clsIn = dir.lookupName("Test.class", directory = false).input
563+
val clsNode = loadClassNode(clsIn)
564+
val method = getMethod(clsNode, "test")
565+
assertEquals(14, instructionsFromMethod(method).size)
566+
}
567+
}
549568
}

0 commit comments

Comments
 (0)