diff options
author | Hubert Plociniczak <hubert.plociniczak@gmail.com> | 2012-05-08 00:55:23 +0200 |
---|---|---|
committer | Hubert Plociniczak <hubert.plociniczak@gmail.com> | 2012-05-08 12:12:55 +0200 |
commit | 3769f4dc1007a9296f65536ffca49b8001d4eae4 (patch) | |
tree | c1d249632d7b4887db36201b14e62f755d1f45c4 /src/compiler | |
parent | a3d4d17b7750fdc121cdb3a6b394bb0489c914cb (diff) | |
download | scala-3769f4dc1007a9296f65536ffca49b8001d4eae4.tar.gz scala-3769f4dc1007a9296f65536ffca49b8001d4eae4.tar.bz2 scala-3769f4dc1007a9296f65536ffca49b8001d4eae4.zip |
Part IV of the Lazy Vals Saga: Optimized local vals. That's all folks.
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/TreeGen.scala | 11 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/LazyVals.scala | 79 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Mixin.scala | 30 |
3 files changed, 75 insertions, 45 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 4cfe2e5cb3..a93d9efded 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -352,19 +352,16 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { else Block(prefix, containing) setPos (prefix.head.pos union containing.pos) } - /** Return a double-checked locking idiom around the syncBody tree. It guards with `cond` and + /** Return the synchronized part of the double-checked locking idiom around the syncBody tree. It guards with `cond` and * synchronizez on `clazz.this`. Additional statements can be included after initialization, * (outside the synchronized block). * * The idiom works only if the condition is using a volatile field. * @see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html */ - def mkDoubleCheckedLocking(clazz: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = - mkDoubleCheckedLocking(mkAttributedThis(clazz), cond, syncBody, stats) - - def mkDoubleCheckedLocking(attrThis: Tree, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = - If(cond, mkSynchronizedCheck(attrThis, cond, syncBody, stats), EmptyTree) - + def mkSynchronizedCheck(clazz: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = + mkSynchronizedCheck(mkAttributedThis(clazz), cond, syncBody, stats) + def mkSynchronizedCheck(attrThis: Tree, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = Block(mkSynchronized( attrThis, diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index 3024f3db28..d97dc36131 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -54,7 +54,6 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD private val lazyVals = perRunCaches.newMap[Symbol, Int]() withDefaultValue 0 import symtab.Flags._ - import lazyVals._ /** Perform the following transformations: * - for a lazy accessor inside a method, make it check the initialization bitmap @@ -69,8 +68,19 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD curTree = tree tree match { + + case Block(_, _) => + val block1 = super.transform(tree) + val Block(stats, expr) = block1 + val stats1 = stats.flatMap(_ match { + case Block(List(d1@DefDef(_, n1, _, _, _, _)), d2@DefDef(_, n2, _, _, _, _)) if (nme.newLazyValSlowComputeName(n2) == n1) => + List(d1, d2) + case stat => List(stat) + }) + treeCopy.Block(block1, stats1, expr) + case DefDef(_, _, _, _, _, rhs) => atOwner(tree.symbol) { - val res = if (!sym.owner.isClass && sym.isLazy) { + val (res, slowPathDef) = if (!sym.owner.isClass && sym.isLazy) { val enclosingClassOrDummyOrMethod = { val enclMethod = sym.enclMethod @@ -85,13 +95,14 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD } val idx = lazyVals(enclosingClassOrDummyOrMethod) lazyVals(enclosingClassOrDummyOrMethod) = idx + 1 - val rhs1 = mkLazyDef(enclosingClassOrDummyOrMethod, super.transform(rhs), idx, sym) + val (rhs1, sDef) = mkLazyDef(enclosingClassOrDummyOrMethod, transform(rhs), idx, sym) sym.resetFlag((if (lazyUnit(sym)) 0 else LAZY) | ACCESSOR) - rhs1 - } else - super.transform(rhs) - - deriveDefDef(tree)(_ => if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res) + (rhs1, sDef) + } else + (transform(rhs), EmptyTree) + + val ddef1 = deriveDefDef(tree)(_ => if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res) + if (slowPathDef != EmptyTree) Block(slowPathDef, ddef1) else ddef1 } case Template(_, _, body) => atOwner(currentOwner) { @@ -176,6 +187,24 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD case _ => prependStats(bmps, rhs) } } + + def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], + stats: List[Tree], retVal: Tree): Tree = { + val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name), lzyVal.pos, STABLE | PRIVATE) + defSym setInfo MethodType(List(), lzyVal.tpe.resultType) + defSym.owner = lzyVal.owner + if (bitmaps.contains(lzyVal)) + bitmaps(lzyVal).map(_.owner = defSym) + val rhs: Tree = (gen.mkSynchronizedCheck(clazz, cond, syncBody, stats)).changeOwner(currentOwner -> defSym) + DEF(defSym).mkTree(addBitmapDefs(lzyVal, BLOCK(rhs, retVal))) setSymbol defSym + } + + + def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], + stats: List[Tree], retVal: Tree): (Tree, Tree) = { + val slowPathDef: Tree = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal) + (If(cond, Apply(ID(slowPathDef.symbol), List()), retVal), slowPathDef) + } /** return a 'lazified' version of rhs. Rhs should conform to the * following schema: @@ -186,33 +215,38 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD * <rhs> when the lazy value has type Unit (for which there is no field * to cache it's value. * - * The result will be a tree of the form - * { - * if ((bitmap$n & MASK) == 0) { + * Similarly as for normal lazy val members (see Mixin), the result will be a tree of the form + * { if ((bitmap&n & MASK) == 0) this.l$compute() + * else l$ + * + * def l$compute() = { synchronized(enclosing_class_or_dummy) { + * if ((bitmap$n & MASK) == 0) { * l$ = <rhs> * bitmap$n = bimap$n | MASK + * }} + * l$ * } - * l$ * } - * where bitmap$n is an int value acting as a bitmap of initialized values. It is - * the 'n' is (offset / 32), the MASK is (1 << (offset % 32)). If the value has type - * unit, no field is used to cache the value, so the resulting code is: + * where bitmap$n is a byte value acting as a bitmap of initialized values. It is + * the 'n' is (offset / 8), the MASK is (1 << (offset % 8)). If the value has type + * unit, no field is used to cache the value, so the l$compute will now look as following: * { - * if ((bitmap$n & MASK) == 0) { + * def l$compute() = { synchronized(enclosing_class_or_dummy) { + * if ((bitmap$n & MASK) == 0) { * <rhs>; * bitmap$n = bimap$n | MASK - * } + * }} * () + * } * } */ - private def mkLazyDef(methOrClass: Symbol, tree: Tree, offset: Int, lazyVal: Symbol): Tree = { + private def mkLazyDef(methOrClass: Symbol, tree: Tree, offset: Int, lazyVal: Symbol): (Tree, Tree) = { val bitmapSym = getBitmapFor(methOrClass, offset) 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) - val (block, res) = tree match { case Block(List(assignment), res) if !lazyUnit(lazyVal) => (mkBlock(assignment), res) @@ -221,11 +255,8 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD } val cond = (bitmapRef GEN_& (mask, bitmapKind)) GEN_== (ZERO, bitmapKind) - - atPos(tree.pos)(localTyper.typed { - def body = gen.mkDoubleCheckedLocking(methOrClass.enclClass, cond, List(block), Nil) - BLOCK(body, res) - }) + val lazyDefs = mkFastPathBody(methOrClass.enclClass, lazyVal, cond, List(block), Nil, res) + (atPos(tree.pos)(localTyper.typed {lazyDefs._1 }), atPos(tree.pos)(localTyper.typed {lazyDefs._2 })) } private def mkSetFlag(bmp: Symbol, mask: Tree, bmpRef: Tree): Tree = diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 5452656e9b..4ce5985af8 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -493,7 +493,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ private val fieldOffset = perRunCaches.newMap[Symbol, Int]() - private val bitmapKindForCategory = perRunCaches.newMap[Name, ClassSymbol]() // TODO: make it a list + private val bitmapKindForCategory = perRunCaches.newMap[Name, ClassSymbol]() // ByteClass, IntClass, LongClass private def bitmapKind(field: Symbol): ClassSymbol = bitmapKindForCategory(bitmapCategory(field)) @@ -782,22 +782,11 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val params = defSym newSyntheticValueParams args.map(_.symbol.tpe) defSym setInfoAndEnter MethodType(params, lzyVal.tpe.resultType) val rhs: Tree = (gen.mkSynchronizedCheck(attrThis, cond, syncBody, stats)).changeOwner(currentOwner -> defSym) - val strictSubst = new SlowPathTreeSymSubstituter(args.map(_.symbol), params) + val strictSubst = new TreeSymSubstituterWithCopying(args.map(_.symbol), params) addDef(position(defSym), DEF(defSym).mkTree(strictSubst(BLOCK(rhs, retVal))) setSymbol defSym) defSym } - - // Always copy the tree if we are going to perform sym substitution, - // otherwise we will side-effect on the tree that is used in the fast path - class SlowPathTreeSymSubstituter(from: List[Symbol], to: List[Symbol]) extends TreeSymSubstituter(from, to) { - override def transform(tree: Tree): Tree = { - if (tree.hasSymbol && from.contains(tree.symbol)) { - super.transform(tree.duplicate) - } else super.transform(tree.duplicate) - } - override def apply[T <: Tree](tree: T): T = if (from.isEmpty) tree else super.apply(tree) - } - + def mkFastPathLazyBody(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree): Tree = { mkFastPathBody(clazz, lzyVal, cond, syncBody, stats, retVal, gen.mkAttributedThis(clazz), List()) @@ -808,6 +797,19 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val slowPathSym: Symbol = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal, attrThis, args) If(cond, fn (This(clazz), slowPathSym, args.map(arg => Ident(arg.symbol)): _*), retVal) } + + + /** Always copy the tree if we are going to perform sym substitution, + * otherwise we will side-effect on the tree that is used in the fast path + */ + class TreeSymSubstituterWithCopying(from: List[Symbol], to: List[Symbol]) extends TreeSymSubstituter(from, to) { + override def transform(tree: Tree): Tree = + if (tree.hasSymbol && from.contains(tree.symbol)) + super.transform(tree.duplicate) + else super.transform(tree.duplicate) + + override def apply[T <: Tree](tree: T): T = if (from.isEmpty) tree else super.apply(tree) + } /** return a 'lazified' version of rhs. It uses double-checked locking to ensure * initialization is performed at most once. For performance reasons the double-checked |