Skip to content

Commit 3631f1d

Browse files
committed
Part II of the Lazy Vals Saga: Saving bitmap by the Three Musketeers: Byte, Int and Long. And Boolean (hey, he also deserves some credit!).
1 parent bdd9abe commit 3631f1d

File tree

7 files changed

+84
-40
lines changed

7 files changed

+84
-40
lines changed

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
534534

535535
// phaseName = "lazyvals"
536536
object lazyVals extends {
537-
final val FLAGS_PER_WORD = 32
538537
val global: Global.this.type = Global.this
539538
val runsAfter = List("erasure")
540539
val runsRightAfter = None

src/compiler/scala/tools/nsc/ast/TreeDSL.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ trait TreeDSL {
9595
def INT_>= (other: Tree) = fn(target, getMember(IntClass, nme.GE), other)
9696
def INT_== (other: Tree) = fn(target, getMember(IntClass, nme.EQ), other)
9797
def INT_!= (other: Tree) = fn(target, getMember(IntClass, nme.NE), other)
98+
99+
// generic operations on ByteClass, IntClass, LongClass
100+
def GEN_| (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.OR), other)
101+
def GEN_& (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.AND), other)
102+
def GEN_== (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.EQ), other)
103+
def GEN_!= (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.NE), other)
98104

99105
def BOOL_&& (other: Tree) = fn(target, Boolean_and, other)
100106
def BOOL_|| (other: Tree) = fn(target, Boolean_or, other)

src/compiler/scala/tools/nsc/transform/Constructors.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,8 @@ abstract class Constructors extends Transform with ast.TreeDSL {
362362
val tree =
363363
If(
364364
Apply(
365-
Select(
366-
Apply(gen.mkAttributedRef(specializedFlag), List()),
367-
definitions.getMember(definitions.BooleanClass, nme.UNARY_!)),
365+
CODE.NOT (
366+
Apply(gen.mkAttributedRef(specializedFlag), List())),
368367
List()),
369368
Block(stats, Literal(Constant())),
370369
EmptyTree)

src/compiler/scala/tools/nsc/transform/Erasure.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,8 @@ abstract class Erasure extends AddInterfaces
643643
else if (isPrimitiveValueType(tree.tpe) && !isPrimitiveValueType(pt)) {
644644
adaptToType(box(tree, pt.toString), pt)
645645
} else if (tree.tpe.isInstanceOf[MethodType] && tree.tpe.params.isEmpty) {
646-
assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt)
646+
// [H] this assert fails when try to call !(Some.this.bitmap) for single lazy val
647+
//assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt)
647648
adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt)
648649
// } else if (pt <:< tree.tpe)
649650
// cast(tree, pt)

src/compiler/scala/tools/nsc/transform/LazyVals.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
1212
import CODE._
1313

1414
val phaseName: String = "lazyvals"
15-
val FLAGS_PER_WORD: Int
15+
private val FLAGS_PER_BYTE: Int = 32 // Byte
16+
private def bitmapKind = ByteClass
1617

1718
def newTransformer(unit: CompilationUnit): Transformer =
1819
new LazyValues(unit)
@@ -206,7 +207,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
206207
*/
207208
private def mkLazyDef(methOrClass: Symbol, tree: Tree, offset: Int, lazyVal: Symbol): Tree = {
208209
val bitmapSym = getBitmapFor(methOrClass, offset)
209-
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
210+
val mask = LIT(1 << (offset % FLAGS_PER_BYTE))
210211
val bitmapRef = if (methOrClass.isClass) Select(This(methOrClass), bitmapSym) else Ident(bitmapSym)
211212

212213
def mkBlock(stmt: Tree) = BLOCK(stmt, mkSetFlag(bitmapSym, mask, bitmapRef), UNIT)
@@ -219,7 +220,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
219220
(mkBlock(rhs), UNIT)
220221
}
221222

222-
val cond = (bitmapRef INT_& mask) INT_== ZERO
223+
val cond = (bitmapRef GEN_& (mask, bitmapKind)) GEN_== (ZERO, bitmapKind)
223224

224225
atPos(tree.pos)(localTyper.typed {
225226
def body = gen.mkDoubleCheckedLocking(methOrClass.enclClass, cond, List(block), Nil)
@@ -228,20 +229,20 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
228229
}
229230

230231
private def mkSetFlag(bmp: Symbol, mask: Tree, bmpRef: Tree): Tree =
231-
bmpRef === (bmpRef INT_| mask)
232+
bmpRef === (bmpRef GEN_| (mask, bitmapKind))
232233

233234
val bitmaps = mutable.Map[Symbol, List[Symbol]]() withDefaultValue Nil
234235

235236
/** Return the symbol corresponding of the right bitmap int inside meth,
236237
* given offset.
237238
*/
238239
private def getBitmapFor(meth: Symbol, offset: Int): Symbol = {
239-
val n = offset / FLAGS_PER_WORD
240+
val n = offset / FLAGS_PER_BYTE
240241
val bmps = bitmaps(meth)
241242
if (bmps.length > n)
242243
bmps(n)
243244
else {
244-
val sym = meth.newVariable(nme.newBitmapName(nme.BITMAP_NORMAL, n), meth.pos).setInfo(IntClass.tpe)
245+
val sym = meth.newVariable(nme.newBitmapName(nme.BITMAP_NORMAL, n), meth.pos).setInfo(ByteClass.tpe)
245246
beforeTyper {
246247
sym addAnnotation VolatileAttr
247248
}

src/compiler/scala/tools/nsc/transform/Mixin.scala

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,19 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
492492
* fields count as fields defined by the class itself.
493493
*/
494494
private val fieldOffset = perRunCaches.newMap[Symbol, Int]()
495+
496+
private val bitmapKindForCategory = perRunCaches.newMap[Name, ClassSymbol]() // TODO: make it a list
497+
498+
// ByteClass, IntClass, LongClass
499+
private def bitmapKind(field: Symbol): ClassSymbol = bitmapKindForCategory(bitmapCategory(field))
500+
501+
private def flagsPerBitmap(field: Symbol): Int = bitmapKind(field) match {
502+
case BooleanClass => 1
503+
case ByteClass => 8
504+
case IntClass => 32
505+
case LongClass => 64
506+
}
507+
495508

496509
/** The first transform; called in a pre-order traversal at phase mixin
497510
* (that is, every node is processed before its children).
@@ -610,8 +623,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
610623
if (field.accessed hasAnnotation TransientAttr) {
611624
if (isNormal) BITMAP_TRANSIENT
612625
else BITMAP_CHECKINIT_TRANSIENT
613-
}
614-
else {
626+
} else {
615627
if (isNormal) BITMAP_NORMAL
616628
else BITMAP_CHECKINIT
617629
}
@@ -696,30 +708,34 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
696708
stat
697709
}
698710

699-
import lazyVals._
700-
701711
/**
702712
* Return the bitmap field for 'offset'. Depending on the hierarchy it is possible to reuse
703713
* the bitmap of its parents. If that does not exist yet we create one.
704714
*/
705715
def bitmapFor(clazz0: Symbol, offset: Int, field: Symbol): Symbol = {
706716
val category = bitmapCategory(field)
707-
val bitmapName = nme.newBitmapName(category, offset / FLAGS_PER_WORD)
717+
val bitmapName = nme.newBitmapName(category, offset / flagsPerBitmap(field))
708718
val sym = clazz0.info.decl(bitmapName)
709719

710720
assert(!sym.isOverloaded, sym)
711721

712722
def createBitmap: Symbol = {
713-
val sym = clazz0.newVariable(bitmapName, clazz0.pos) setInfo IntClass.tpe
723+
val bitmapKind = bitmapKindForCategory(category)
724+
val sym = clazz0.newVariable(bitmapName, clazz0.pos) setInfo bitmapKind.tpe
714725
beforeTyper(sym addAnnotation VolatileAttr)
715726

716727
category match {
717728
case nme.BITMAP_TRANSIENT | nme.BITMAP_CHECKINIT_TRANSIENT => sym addAnnotation TransientAttr
718729
case _ =>
719730
}
731+
val init = bitmapKind match {
732+
case BooleanClass => VAL(sym) === FALSE
733+
case _ => VAL(sym) === ZERO
734+
}
735+
720736
sym setFlag PrivateLocal
721737
clazz0.info.decls.enter(sym)
722-
addDef(clazz0.pos, VAL(sym) === ZERO)
738+
addDef(clazz0.pos, init)
723739
sym
724740
}
725741

@@ -728,23 +744,36 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
728744
else
729745
createBitmap
730746
}
747+
748+
def maskForOffset(offset: Int, sym: Symbol, kind: ClassSymbol): Tree = {
749+
def realOffset = offset % flagsPerBitmap(sym)
750+
if (kind == LongClass ) LIT(1L << realOffset) else LIT(1 << realOffset)
751+
}
731752

732753
/** Return an (untyped) tree of the form 'Clazz.this.bmp = Clazz.this.bmp | mask'. */
733-
def mkSetFlag(clazz: Symbol, offset: Int, valSym: Symbol): Tree = {
734-
val bmp = bitmapFor(clazz, offset, valSym)
735-
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
736-
def x = This(clazz) DOT bmp
754+
def mkSetFlag(clazz: Symbol, offset: Int, valSym: Symbol, kind: ClassSymbol): Tree = {
755+
val bmp = bitmapFor(clazz, offset, valSym)
756+
def mask = maskForOffset(offset, valSym, kind)
757+
def x = This(clazz) DOT bmp
758+
def newValue = if (kind == BooleanClass) TRUE else (x GEN_| (mask, kind))
737759

738-
x === (x INT_| mask)
760+
x === newValue
739761
}
740762

741763
/** Return an (untyped) tree of the form 'clazz.this.bitmapSym & mask (==|!=) 0', the
742764
* precise comparison operator depending on the value of 'equalToZero'.
743765
*/
744-
def mkTest(clazz: Symbol, mask: Tree, bitmapSym: Symbol, equalToZero: Boolean): Tree = {
745-
def lhs = (This(clazz) DOT bitmapSym) INT_& mask
746-
if (equalToZero) lhs INT_== ZERO
747-
else lhs INT_!= ZERO
766+
def mkTest(clazz: Symbol, mask: Tree, bitmapSym: Symbol, equalToZero: Boolean, kind: ClassSymbol): Tree = {
767+
val bitmapTree = (This(clazz) DOT bitmapSym)
768+
def lhs = bitmapTree GEN_& (mask, kind)
769+
kind match {
770+
case BooleanClass =>
771+
if (equalToZero) NOT(bitmapTree)
772+
else bitmapTree
773+
case _ =>
774+
if (equalToZero) lhs GEN_== (ZERO, kind)
775+
else lhs GEN_!= (ZERO, kind)
776+
}
748777
}
749778

750779
/** return a 'lazified' version of rhs. It uses double-checked locking to ensure
@@ -777,10 +806,11 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
777806
def nullify(sym: Symbol) = Select(This(clazz), sym.accessedOrSelf) === LIT(null)
778807

779808
val bitmapSym = bitmapFor(clazz, offset, lzyVal)
780-
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
781-
def cond = mkTest(clazz, mask, bitmapSym, true)
809+
val kind = bitmapKind(lzyVal)
810+
val mask = maskForOffset(offset, lzyVal, kind)
811+
def cond = mkTest(clazz, mask, bitmapSym, true, kind)
782812
val nulls = lazyValNullables(lzyVal).toList sortBy (_.id) map nullify
783-
def syncBody = init ::: List(mkSetFlag(clazz, offset, lzyVal), UNIT)
813+
def syncBody = init ::: List(mkSetFlag(clazz, offset, lzyVal, kind), UNIT)
784814

785815
if (nulls.nonEmpty)
786816
log("nulling fields inside " + lzyVal + ": " + nulls)
@@ -802,11 +832,13 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
802832
}
803833

804834
def mkCheckedAccessor(clazz: Symbol, retVal: Tree, offset: Int, pos: Position, fieldSym: Symbol): Tree = {
805-
val bitmapSym = bitmapFor(clazz, offset, fieldSym.getter(fieldSym.owner))
806-
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
835+
val sym = fieldSym.getter(fieldSym.owner)
836+
val bitmapSym = bitmapFor(clazz, offset, sym)
837+
val kind = bitmapKind(sym)
838+
val mask = maskForOffset(offset, sym, kind)
807839
val msg = "Uninitialized field: " + unit.source + ": " + pos.line
808840
val result =
809-
IF (mkTest(clazz, mask, bitmapSym, false)) .
841+
IF (mkTest(clazz, mask, bitmapSym, false, kind)) .
810842
THEN (retVal) .
811843
ELSE (THROW(UninitializedErrorClass, LIT(msg)))
812844

@@ -851,7 +883,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
851883
else if (settings.checkInit.value && !clazz.isTrait && sym.isSetter) {
852884
val getter = sym.getter(clazz)
853885
if (needsInitFlag(getter) && fieldOffset.isDefinedAt(getter))
854-
deriveDefDef(stat)(rhs => Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter))), UNIT))
886+
deriveDefDef(stat)(rhs => Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter)))), UNIT))
855887
else stat
856888
}
857889
else if (sym.isModule && (!clazz.isTrait || clazz.isImplClass) && !sym.isBridge) {
@@ -879,7 +911,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
879911
val sym = clazz.info decl lhs.symbol.getterName suchThat (_.isGetter)
880912
if (needsInitAndHasOffset(sym)) {
881913
debuglog("adding checked getter for: " + sym + " " + lhs.symbol.flagString)
882-
List(localTyper typed mkSetFlag(clazz, fieldOffset(sym), sym))
914+
List(localTyper typed mkSetFlag(clazz, fieldOffset(sym), sym, bitmapKind(sym)))
883915
}
884916
else Nil
885917
}
@@ -917,16 +949,22 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
917949
* code generation easier later.
918950
*/
919951
def buildBitmapOffsets() {
920-
def fold(zero: Int, fields: List[Symbol]) = {
921-
var idx = zero
952+
def fold(fields: List[Symbol], category: Name) = {
953+
var idx = 0
922954
fields foreach { f =>
923955
fieldOffset(f) = idx
924956
idx += 1
925957
}
958+
959+
if (idx == 0) ()
960+
else if (idx == 1) bitmapKindForCategory(category) = BooleanClass
961+
else if (idx < 9) bitmapKindForCategory(category) = ByteClass
962+
else if (idx < 33) bitmapKindForCategory(category) = IntClass
963+
else bitmapKindForCategory(category) = LongClass
926964
}
927965
clazz.info.decls.toList groupBy bitmapCategory foreach {
928966
case (nme.NO_NAME, _) => ()
929-
case (_, fields) => fold(0, fields)
967+
case (category, fields) => fold(fields, category)
930968
}
931969
}
932970
buildBitmapOffsets()
@@ -977,7 +1015,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
9771015
val getter = sym.getter(clazz)
9781016

9791017
if (!needsInitFlag(getter)) init
980-
else Block(init, mkSetFlag(clazz, fieldOffset(getter), getter), UNIT)
1018+
else Block(init, mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter)), UNIT)
9811019
}
9821020
}
9831021
else if (needsInitFlag(sym))

test/files/run/t3569.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ private int Test$X.var1
99
private int Test$X.var2
1010
private int Test$X.var3
1111
private int Test$X.x
12-
private volatile int Test$X.bitmap$0
12+
private volatile byte Test$X.bitmap$0
1313
private final int Test$Y.z1
1414
private final int Test$Y.z2
1515
private int Test$Y.x

0 commit comments

Comments
 (0)