@@ -775,32 +775,76 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
775
775
else lhs GEN_!= (ZERO , kind)
776
776
}
777
777
}
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
+ }
778
811
779
812
/** 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.
782
819
*
783
820
* @param clazz The class symbol
784
821
* @param init The tree which initializes the field ( f = <rhs> )
785
822
* @param fieldSym The symbol of this lazy field
786
823
* @param offset The offset of this field in the flags bitmap
787
824
*
788
825
* 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$
799
836
* }
800
- * l$
837
+ *
838
+ * ...
839
+ * this.f1 = null
840
+ * ... this.fn = null
801
841
* }
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.
804
848
*/
805
849
def mkLazyDef (clazz : Symbol , lzyVal : Symbol , init : List [Tree ], retVal : Tree , offset : Int ): Tree = {
806
850
def nullify (sym : Symbol ) = Select (This (clazz), sym.accessedOrSelf) === LIT (null )
@@ -815,17 +859,15 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
815
859
if (nulls.nonEmpty)
816
860
log(" nulling fields inside " + lzyVal + " : " + nulls)
817
861
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))
820
863
}
821
864
822
- def mkInnerClassAccessorDoubleChecked (attrThis : Tree , rhs : Tree ): Tree =
865
+ def mkInnerClassAccessorDoubleChecked (attrThis : Tree , rhs : Tree , moduleSym : Symbol , args : List [ Tree ] ): Tree =
823
866
rhs match {
824
867
case Block (List (assign), returnTree) =>
825
868
val Assign (moduleVarRef, _) = assign
826
869
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)
829
871
case _ =>
830
872
assert(false , " Invalid getter " + rhs + " for module in class " + clazz)
831
873
EmptyTree
@@ -893,7 +935,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
893
935
// Martin to Hubert: I think this can be replaced by selfRef(tree.pos)
894
936
// @PP: It does not seem so, it crashes for me trying to bootstrap.
895
937
if (clazz.isImplClass) gen.mkAttributedIdent(stat.vparamss.head.head.symbol) else gen.mkAttributedThis(clazz),
896
- rhs
938
+ rhs, sym, stat.vparamss.head
897
939
)
898
940
)
899
941
)
@@ -1032,7 +1074,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
1032
1074
val rhs = gen.newModule(sym, vdef.symbol.tpe)
1033
1075
val assignAndRet = gen.mkAssignAndReturn(vdef.symbol, rhs)
1034
1076
val attrThis = gen.mkAttributedThis(clazz)
1035
- val rhs1 = mkInnerClassAccessorDoubleChecked(attrThis, assignAndRet)
1077
+ val rhs1 = mkInnerClassAccessorDoubleChecked(attrThis, assignAndRet, sym, List () )
1036
1078
1037
1079
addDefDef(sym, rhs1)
1038
1080
}
0 commit comments