@@ -775,32 +775,76 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
775775 else lhs GEN_!= (ZERO , kind)
776776 }
777777 }
778+
779+ def mkSlowPathDef (clazz : Symbol , lzyVal : Symbol , cond : Tree , syncBody : List [Tree ],
780+ stats : List [Tree ], retVal : Tree , attrThis : Tree , args : List [Tree ]): Symbol = {
781+ val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name), lzyVal.pos, PRIVATE )
782+ val params = defSym newSyntheticValueParams args.map(_.symbol.tpe)
783+ defSym setInfoAndEnter MethodType (params, lzyVal.tpe.resultType)
784+ val rhs : Tree = (gen.mkSynchronizedCheck(attrThis, cond, syncBody, stats)).changeOwner(currentOwner -> defSym)
785+ val strictSubst = new SlowPathTreeSymSubstituter (args.map(_.symbol), params)
786+ addDef(position(defSym), DEF (defSym).mkTree(strictSubst(BLOCK (rhs, retVal))) setSymbol defSym)
787+ defSym
788+ }
789+
790+ // Always copy the tree if we are going to perform sym substitution,
791+ // otherwise we will side-effect on the tree that is used in the fast path
792+ class SlowPathTreeSymSubstituter (from : List [Symbol ], to : List [Symbol ]) extends TreeSymSubstituter (from, to) {
793+ override def transform (tree : Tree ): Tree = {
794+ if (tree.hasSymbol && from.contains(tree.symbol)) {
795+ super .transform(tree.duplicate)
796+ } else super .transform(tree.duplicate)
797+ }
798+ override def apply [T <: Tree ](tree : T ): T = if (from.isEmpty) tree else super .apply(tree)
799+ }
800+
801+ def mkFastPathLazyBody (clazz : Symbol , lzyVal : Symbol , cond : Tree , syncBody : List [Tree ],
802+ stats : List [Tree ], retVal : Tree ): Tree = {
803+ mkFastPathBody(clazz, lzyVal, cond, syncBody, stats, retVal, gen.mkAttributedThis(clazz), List ())
804+ }
805+
806+ def mkFastPathBody (clazz : Symbol , lzyVal : Symbol , cond : Tree , syncBody : List [Tree ],
807+ stats : List [Tree ], retVal : Tree , attrThis : Tree , args : List [Tree ]): Tree = {
808+ val slowPathSym : Symbol = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal, attrThis, args)
809+ If (cond, fn (This (clazz), slowPathSym, args.map(arg => Ident (arg.symbol)): _* ), retVal)
810+ }
778811
779812 /** return a 'lazified' version of rhs. It uses double-checked locking to ensure
780- * initialization is performed at most once. Private fields used only in this
781- * initializer are subsequently set to null.
813+ * initialization is performed at most once. For performance reasons the double-checked
814+ * locking is split into two parts, the first (fast) path checks the bitmap without
815+ * synchronizing, and if that fails it initializes the lazy val within the
816+ * synchronization block (slow path). This way the inliner should optimize
817+ * the fast path because the method body is small enough.
818+ * Private fields used only in this initializer are subsequently set to null.
782819 *
783820 * @param clazz The class symbol
784821 * @param init The tree which initializes the field ( f = <rhs> )
785822 * @param fieldSym The symbol of this lazy field
786823 * @param offset The offset of this field in the flags bitmap
787824 *
788825 * The result will be a tree of the form
789- * {
790- * if ((bitmap$n & MASK) == 0) {
791- * synchronized(this) {
792- * if ((bitmap$n & MASK) == 0) {
793- * init // l$ = <rhs>
794- * bitmap$n = bimap$n | MASK
795- * }
796- * }
797- * this.f1 = null
798- * ... this.fn = null
826+ * { if ((bitmap&n & MASK) == 0) this.l$compute()
827+ * else l$
828+ *
829+ * ...
830+ * def l$compute() = { synchronized(this) {
831+ * if (( bitmap$n & MASK) == 0) {
832+ * init // l$ = <rhs>
833+ * bitmap$n = bimap$n | MASK
834+ * }}
835+ * l$
799836 * }
800- * l$
837+ *
838+ * ...
839+ * this.f1 = null
840+ * ... this.fn = null
801841 * }
802- * where bitmap$n is an int value acting as a bitmap of initialized values. It is
803- * the 'n' is (offset / 32), the MASK is (1 << (offset % 32)).
842+ * where bitmap$n is a byte, int or long value acting as a bitmap of initialized values.
843+ * The kind of the bitmap determines how many bit indicators for lazy vals are stored in it.
844+ * For Int bitmap it is 32 and then 'n' in the above code is: (offset / 32),
845+ * the MASK is (1 << (offset % 32)).
846+ * If the class contains only a single lazy val then the bitmap is represented
847+ * as a Boolean and the condition checking is a simple bool test.
804848 */
805849 def mkLazyDef (clazz : Symbol , lzyVal : Symbol , init : List [Tree ], retVal : Tree , offset : Int ): Tree = {
806850 def nullify (sym : Symbol ) = Select (This (clazz), sym.accessedOrSelf) === LIT (null )
@@ -815,17 +859,15 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
815859 if (nulls.nonEmpty)
816860 log(" nulling fields inside " + lzyVal + " : " + nulls)
817861
818- val result = gen.mkDoubleCheckedLocking(clazz, cond, syncBody, nulls)
819- typedPos(init.head.pos)(BLOCK (result, retVal))
862+ typedPos(init.head.pos)(mkFastPathLazyBody(clazz, lzyVal, cond, syncBody, nulls, retVal))
820863 }
821864
822- def mkInnerClassAccessorDoubleChecked (attrThis : Tree , rhs : Tree ): Tree =
865+ def mkInnerClassAccessorDoubleChecked (attrThis : Tree , rhs : Tree , moduleSym : Symbol , args : List [ Tree ] ): Tree =
823866 rhs match {
824867 case Block (List (assign), returnTree) =>
825868 val Assign (moduleVarRef, _) = assign
826869 val cond = Apply (Select (moduleVarRef, nme.eq), List (NULL ))
827- val doubleSynchrTree = gen.mkDoubleCheckedLocking(attrThis, cond, List (assign), Nil )
828- Block (List (doubleSynchrTree), returnTree)
870+ mkFastPathBody(clazz, moduleSym, cond, List (assign), List (NULL ), returnTree, attrThis, args)
829871 case _ =>
830872 assert(false , " Invalid getter " + rhs + " for module in class " + clazz)
831873 EmptyTree
@@ -893,7 +935,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
893935 // Martin to Hubert: I think this can be replaced by selfRef(tree.pos)
894936 // @PP: It does not seem so, it crashes for me trying to bootstrap.
895937 if (clazz.isImplClass) gen.mkAttributedIdent(stat.vparamss.head.head.symbol) else gen.mkAttributedThis(clazz),
896- rhs
938+ rhs, sym, stat.vparamss.head
897939 )
898940 )
899941 )
@@ -1032,7 +1074,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
10321074 val rhs = gen.newModule(sym, vdef.symbol.tpe)
10331075 val assignAndRet = gen.mkAssignAndReturn(vdef.symbol, rhs)
10341076 val attrThis = gen.mkAttributedThis(clazz)
1035- val rhs1 = mkInnerClassAccessorDoubleChecked(attrThis, assignAndRet)
1077+ val rhs1 = mkInnerClassAccessorDoubleChecked(attrThis, assignAndRet, sym, List () )
10361078
10371079 addDefDef(sym, rhs1)
10381080 }
0 commit comments