Permalink
Browse files

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!).
  • Loading branch information...
hubertp committed May 3, 2012
1 parent bdd9abe commit 3631f1dadc8ce2beac86aa618a62120b88bda50c
@@ -534,7 +534,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
// phaseName = "lazyvals"
object lazyVals extends {
final val FLAGS_PER_WORD = 32
val global: Global.this.type = Global.this
val runsAfter = List("erasure")
val runsRightAfter = None
@@ -95,6 +95,12 @@ trait TreeDSL {
def INT_>= (other: Tree) = fn(target, getMember(IntClass, nme.GE), other)
def INT_== (other: Tree) = fn(target, getMember(IntClass, nme.EQ), other)
def INT_!= (other: Tree) = fn(target, getMember(IntClass, nme.NE), other)
// generic operations on ByteClass, IntClass, LongClass
def GEN_| (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.OR), other)
def GEN_& (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.AND), other)
def GEN_== (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.EQ), other)
def GEN_!= (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.NE), other)
def BOOL_&& (other: Tree) = fn(target, Boolean_and, other)
def BOOL_|| (other: Tree) = fn(target, Boolean_or, other)
@@ -362,9 +362,8 @@ abstract class Constructors extends Transform with ast.TreeDSL {
val tree =
If(
Apply(
Select(
Apply(gen.mkAttributedRef(specializedFlag), List()),
definitions.getMember(definitions.BooleanClass, nme.UNARY_!)),
CODE.NOT (
Apply(gen.mkAttributedRef(specializedFlag), List())),
List()),
Block(stats, Literal(Constant())),
EmptyTree)
@@ -643,7 +643,8 @@ abstract class Erasure extends AddInterfaces
else if (isPrimitiveValueType(tree.tpe) && !isPrimitiveValueType(pt)) {
adaptToType(box(tree, pt.toString), pt)
} else if (tree.tpe.isInstanceOf[MethodType] && tree.tpe.params.isEmpty) {
assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt)
// [H] this assert fails when try to call !(Some.this.bitmap) for single lazy val
//assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt)
adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt)
// } else if (pt <:< tree.tpe)
// cast(tree, pt)
@@ -12,7 +12,8 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
import CODE._
val phaseName: String = "lazyvals"
val FLAGS_PER_WORD: Int
private val FLAGS_PER_BYTE: Int = 32 // Byte
private def bitmapKind = ByteClass
def newTransformer(unit: CompilationUnit): Transformer =
new LazyValues(unit)
@@ -206,7 +207,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
*/
private def mkLazyDef(methOrClass: Symbol, tree: Tree, offset: Int, lazyVal: Symbol): Tree = {
val bitmapSym = getBitmapFor(methOrClass, offset)
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
val mask = LIT(1 << (offset % FLAGS_PER_BYTE))
val bitmapRef = if (methOrClass.isClass) Select(This(methOrClass), bitmapSym) else Ident(bitmapSym)
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
(mkBlock(rhs), UNIT)
}
val cond = (bitmapRef INT_& mask) INT_== ZERO
val cond = (bitmapRef GEN_& (mask, bitmapKind)) GEN_== (ZERO, bitmapKind)
atPos(tree.pos)(localTyper.typed {
def body = gen.mkDoubleCheckedLocking(methOrClass.enclClass, cond, List(block), Nil)
@@ -228,20 +229,20 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
}
private def mkSetFlag(bmp: Symbol, mask: Tree, bmpRef: Tree): Tree =
bmpRef === (bmpRef INT_| mask)
bmpRef === (bmpRef GEN_| (mask, bitmapKind))
val bitmaps = mutable.Map[Symbol, List[Symbol]]() withDefaultValue Nil
/** Return the symbol corresponding of the right bitmap int inside meth,
* given offset.
*/
private def getBitmapFor(meth: Symbol, offset: Int): Symbol = {
val n = offset / FLAGS_PER_WORD
val n = offset / FLAGS_PER_BYTE
val bmps = bitmaps(meth)
if (bmps.length > n)
bmps(n)
else {
val sym = meth.newVariable(nme.newBitmapName(nme.BITMAP_NORMAL, n), meth.pos).setInfo(IntClass.tpe)
val sym = meth.newVariable(nme.newBitmapName(nme.BITMAP_NORMAL, n), meth.pos).setInfo(ByteClass.tpe)
beforeTyper {
sym addAnnotation VolatileAttr
}
@@ -492,6 +492,19 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
* fields count as fields defined by the class itself.
*/
private val fieldOffset = perRunCaches.newMap[Symbol, Int]()
private val bitmapKindForCategory = perRunCaches.newMap[Name, ClassSymbol]() // TODO: make it a list
// ByteClass, IntClass, LongClass
private def bitmapKind(field: Symbol): ClassSymbol = bitmapKindForCategory(bitmapCategory(field))
private def flagsPerBitmap(field: Symbol): Int = bitmapKind(field) match {
case BooleanClass => 1
case ByteClass => 8
case IntClass => 32
case LongClass => 64
}
/** The first transform; called in a pre-order traversal at phase mixin
* (that is, every node is processed before its children).
@@ -610,8 +623,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
if (field.accessed hasAnnotation TransientAttr) {
if (isNormal) BITMAP_TRANSIENT
else BITMAP_CHECKINIT_TRANSIENT
}
else {
} else {
if (isNormal) BITMAP_NORMAL
else BITMAP_CHECKINIT
}
@@ -696,30 +708,34 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
stat
}
import lazyVals._
/**
* Return the bitmap field for 'offset'. Depending on the hierarchy it is possible to reuse
* the bitmap of its parents. If that does not exist yet we create one.
*/
def bitmapFor(clazz0: Symbol, offset: Int, field: Symbol): Symbol = {
val category = bitmapCategory(field)
val bitmapName = nme.newBitmapName(category, offset / FLAGS_PER_WORD)
val bitmapName = nme.newBitmapName(category, offset / flagsPerBitmap(field))
val sym = clazz0.info.decl(bitmapName)
assert(!sym.isOverloaded, sym)
def createBitmap: Symbol = {
val sym = clazz0.newVariable(bitmapName, clazz0.pos) setInfo IntClass.tpe
val bitmapKind = bitmapKindForCategory(category)
val sym = clazz0.newVariable(bitmapName, clazz0.pos) setInfo bitmapKind.tpe
beforeTyper(sym addAnnotation VolatileAttr)
category match {
case nme.BITMAP_TRANSIENT | nme.BITMAP_CHECKINIT_TRANSIENT => sym addAnnotation TransientAttr
case _ =>
}
val init = bitmapKind match {
case BooleanClass => VAL(sym) === FALSE
case _ => VAL(sym) === ZERO
}
sym setFlag PrivateLocal
clazz0.info.decls.enter(sym)
addDef(clazz0.pos, VAL(sym) === ZERO)
addDef(clazz0.pos, init)
sym
}
@@ -728,23 +744,36 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
else
createBitmap
}
def maskForOffset(offset: Int, sym: Symbol, kind: ClassSymbol): Tree = {
def realOffset = offset % flagsPerBitmap(sym)
if (kind == LongClass ) LIT(1L << realOffset) else LIT(1 << realOffset)
}
/** Return an (untyped) tree of the form 'Clazz.this.bmp = Clazz.this.bmp | mask'. */
def mkSetFlag(clazz: Symbol, offset: Int, valSym: Symbol): Tree = {
val bmp = bitmapFor(clazz, offset, valSym)
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
def x = This(clazz) DOT bmp
def mkSetFlag(clazz: Symbol, offset: Int, valSym: Symbol, kind: ClassSymbol): Tree = {
val bmp = bitmapFor(clazz, offset, valSym)
def mask = maskForOffset(offset, valSym, kind)
def x = This(clazz) DOT bmp
def newValue = if (kind == BooleanClass) TRUE else (x GEN_| (mask, kind))
x === (x INT_| mask)
x === newValue
}
/** Return an (untyped) tree of the form 'clazz.this.bitmapSym & mask (==|!=) 0', the
* precise comparison operator depending on the value of 'equalToZero'.
*/
def mkTest(clazz: Symbol, mask: Tree, bitmapSym: Symbol, equalToZero: Boolean): Tree = {
def lhs = (This(clazz) DOT bitmapSym) INT_& mask
if (equalToZero) lhs INT_== ZERO
else lhs INT_!= ZERO
def mkTest(clazz: Symbol, mask: Tree, bitmapSym: Symbol, equalToZero: Boolean, kind: ClassSymbol): Tree = {
val bitmapTree = (This(clazz) DOT bitmapSym)
def lhs = bitmapTree GEN_& (mask, kind)
kind match {
case BooleanClass =>
if (equalToZero) NOT(bitmapTree)
else bitmapTree
case _ =>
if (equalToZero) lhs GEN_== (ZERO, kind)
else lhs GEN_!= (ZERO, kind)
}
}
/** 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 {
def nullify(sym: Symbol) = Select(This(clazz), sym.accessedOrSelf) === LIT(null)
val bitmapSym = bitmapFor(clazz, offset, lzyVal)
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
def cond = mkTest(clazz, mask, bitmapSym, true)
val kind = bitmapKind(lzyVal)
val mask = maskForOffset(offset, lzyVal, kind)
def cond = mkTest(clazz, mask, bitmapSym, true, kind)
val nulls = lazyValNullables(lzyVal).toList sortBy (_.id) map nullify
def syncBody = init ::: List(mkSetFlag(clazz, offset, lzyVal), UNIT)
def syncBody = init ::: List(mkSetFlag(clazz, offset, lzyVal, kind), UNIT)
if (nulls.nonEmpty)
log("nulling fields inside " + lzyVal + ": " + nulls)
@@ -802,11 +832,13 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
}
def mkCheckedAccessor(clazz: Symbol, retVal: Tree, offset: Int, pos: Position, fieldSym: Symbol): Tree = {
val bitmapSym = bitmapFor(clazz, offset, fieldSym.getter(fieldSym.owner))
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
val sym = fieldSym.getter(fieldSym.owner)
val bitmapSym = bitmapFor(clazz, offset, sym)
val kind = bitmapKind(sym)
val mask = maskForOffset(offset, sym, kind)
val msg = "Uninitialized field: " + unit.source + ": " + pos.line
val result =
IF (mkTest(clazz, mask, bitmapSym, false)) .
IF (mkTest(clazz, mask, bitmapSym, false, kind)) .
THEN (retVal) .
ELSE (THROW(UninitializedErrorClass, LIT(msg)))
@@ -851,7 +883,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
else if (settings.checkInit.value && !clazz.isTrait && sym.isSetter) {
val getter = sym.getter(clazz)
if (needsInitFlag(getter) && fieldOffset.isDefinedAt(getter))
deriveDefDef(stat)(rhs => Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter))), UNIT))
deriveDefDef(stat)(rhs => Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter)))), UNIT))
else stat
}
else if (sym.isModule && (!clazz.isTrait || clazz.isImplClass) && !sym.isBridge) {
@@ -879,7 +911,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
val sym = clazz.info decl lhs.symbol.getterName suchThat (_.isGetter)
if (needsInitAndHasOffset(sym)) {
debuglog("adding checked getter for: " + sym + " " + lhs.symbol.flagString)
List(localTyper typed mkSetFlag(clazz, fieldOffset(sym), sym))
List(localTyper typed mkSetFlag(clazz, fieldOffset(sym), sym, bitmapKind(sym)))
}
else Nil
}
@@ -917,16 +949,22 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
* code generation easier later.
*/
def buildBitmapOffsets() {
def fold(zero: Int, fields: List[Symbol]) = {
var idx = zero
def fold(fields: List[Symbol], category: Name) = {
var idx = 0
fields foreach { f =>
fieldOffset(f) = idx
idx += 1
}
if (idx == 0) ()
else if (idx == 1) bitmapKindForCategory(category) = BooleanClass
else if (idx < 9) bitmapKindForCategory(category) = ByteClass
else if (idx < 33) bitmapKindForCategory(category) = IntClass
else bitmapKindForCategory(category) = LongClass
}
clazz.info.decls.toList groupBy bitmapCategory foreach {
case (nme.NO_NAME, _) => ()
case (_, fields) => fold(0, fields)
case (category, fields) => fold(fields, category)
}
}
buildBitmapOffsets()
@@ -977,7 +1015,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
val getter = sym.getter(clazz)
if (!needsInitFlag(getter)) init
else Block(init, mkSetFlag(clazz, fieldOffset(getter), getter), UNIT)
else Block(init, mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter)), UNIT)
}
}
else if (needsInitFlag(sym))
@@ -9,7 +9,7 @@ private int Test$X.var1
private int Test$X.var2
private int Test$X.var3
private int Test$X.x
private volatile int Test$X.bitmap$0
private volatile byte Test$X.bitmap$0
private final int Test$Y.z1
private final int Test$Y.z2
private int Test$Y.x

0 comments on commit 3631f1d

Please sign in to comment.