@@ -4,6 +4,8 @@ package transform.localopt
44import core .Constants .Constant
55import core .Contexts .Context
66import core .Decorators ._
7+ import core .Names .Name
8+ import core .Types .Type
79import core .NameOps ._
810import core .StdNames ._
911import core .Symbols ._
@@ -13,46 +15,67 @@ import scala.collection.mutable
1315import transform .SymUtils ._
1416import config .Printers .simplify
1517
16- /** Inline case classes as vals, this essentially (local) implements multi
17- * parameter value classes. The main motivation is to get ride of all the
18- * intermediate tuples coming from pattern matching expressions.
18+ /** Inline case classes as vals.
19+ *
20+ * In other words, implements (local) multi parameter value classes. The main
21+ * motivation is to get ride of all the intermediate tuples created by the
22+ * pattern matcher.
1923 */
2024class InlineLocalObjects extends Optimisation {
2125 import ast .tpd ._
2226
2327 // In the end only calls constructor. Reason for unconditional inlining
2428 val hasPerfectRHS = mutable.HashMap [Symbol , Boolean ]()
29+
2530 // If all values have perfect RHS than key has perfect RHS
2631 val checkGood = mutable.HashMap [Symbol , Set [Symbol ]]()
32+
2733 val forwarderWritesTo = mutable.HashMap [Symbol , Symbol ]()
34+
2835 val gettersCalled = mutable.HashSet [Symbol ]()
2936
37+ def symbolAccessors (s : Symbol )(implicit ctx : Context ): List [Symbol ] = {
38+ val accessors = s.info.classSymbol.caseAccessors.filter(_.isGetter)
39+ if (accessors.isEmpty)
40+ s.info.classSymbol.caseAccessors
41+ else accessors
42+ }
43+
3044 def followTailPerfect (t : Tree , symbol : Symbol )(implicit ctx : Context ): Unit = {
3145 t match {
32- case Block (_, expr) => followTailPerfect(expr, symbol)
33- case If (_, thenp, elsep) => followTailPerfect(thenp, symbol); followTailPerfect(elsep, symbol);
46+ case Block (_, expr) =>
47+ followTailPerfect(expr, symbol)
48+
49+ case If (_, thenp, elsep) =>
50+ followTailPerfect(thenp, symbol)
51+ followTailPerfect(elsep, symbol)
52+
3453 case Apply (fun, _) if fun.symbol.isConstructor && t.tpe.widenDealias == symbol.info.widenDealias.finalResultType.widenDealias =>
3554 hasPerfectRHS(symbol) = true
55+
3656 case Apply (fun, _) if fun.symbol.is(Label ) && (fun.symbol ne symbol) =>
3757 checkGood.put(symbol, checkGood.getOrElse(symbol, Set .empty) + fun.symbol)
3858 // assert(forwarderWritesTo.getOrElse(t.symbol, symbol) == symbol)
3959 forwarderWritesTo(t.symbol) = symbol
60+
4061 case t : Ident if ! t.symbol.owner.isClass && (t.symbol ne symbol) =>
4162 checkGood.put(symbol, checkGood.getOrElse(symbol, Set .empty) + t.symbol)
63+
4264 case _ =>
4365 }
4466 }
4567
4668 def visitor (implicit ctx : Context ): Tree => Unit = {
47- case vdef : ValDef if (vdef .symbol.info.classSymbol is CaseClass ) &&
48- ! vdef .symbol.is(Lazy ) &&
49- ! vdef .symbol.info.classSymbol.caseAccessors.exists(x => x .is(Mutable )) =>
50- followTailPerfect(vdef .rhs, vdef .symbol)
69+ case t : ValDef if (t .symbol.info.classSymbol is CaseClass ) &&
70+ ! t .symbol.is(Lazy ) &&
71+ ! t .symbol.info.classSymbol.caseAccessors.exists(_ .is(Mutable )) =>
72+ followTailPerfect(t .rhs, t .symbol)
5173
5274 case Assign (lhs, rhs) if ! lhs.symbol.owner.isClass =>
5375 checkGood.put(lhs.symbol, checkGood.getOrElse(lhs.symbol, Set .empty) + rhs.symbol)
5476
55- case t @ Select (qual, _) if (t.symbol.isGetter && ! t.symbol.is(Mutable )) ||
77+ case t @ Select (qual, _) if
78+ (t.symbol.isGetter && ! t.symbol.is(Mutable | Lazy )) ||
5679 (t.symbol.maybeOwner.derivesFrom(defn.ProductClass ) && t.symbol.maybeOwner.is(CaseClass ) && t.symbol.name.isSelectorName) ||
5780 (t.symbol.is(CaseAccessor ) && ! t.symbol.is(Mutable )) =>
5881 gettersCalled(qual.symbol) = true
@@ -65,9 +88,9 @@ class InlineLocalObjects extends Optimisation {
6588
6689 def transformer (implicit ctx : Context ): Tree => Tree = {
6790 var hasChanged = true
68- while (hasChanged) {
91+ while (hasChanged) {
6992 hasChanged = false
70- checkGood.foreach{ case (key, values) =>
93+ checkGood.foreach { case (key, values) =>
7194 values.foreach { value =>
7295 if (hasPerfectRHS.getOrElse(key, false )) {
7396 hasChanged = ! hasPerfectRHS.put(value, true ).getOrElse(false )
@@ -77,54 +100,70 @@ class InlineLocalObjects extends Optimisation {
77100 }
78101
79102 val newMappings : Map [Symbol , Map [Symbol , Symbol ]] =
80- hasPerfectRHS.iterator.map(x => x._1).filter(x => ! x.is(Method ) && ! x.is(Label ) && gettersCalled.contains(x.symbol) && (x.symbol.info.classSymbol is CaseClass ))
81- .map { refVal =>
82- simplify.println(s " replacing ${refVal.symbol.fullName} with stack-allocated fields " )
83- var accessors = refVal.info.classSymbol.caseAccessors.filter(_.isGetter) // TODO: drop mutable ones
84- if (accessors.isEmpty) accessors = refVal.info.classSymbol.caseAccessors
85- val productAccessors = (1 to accessors.length).map(i => refVal.info.member(nme.productAccessorName(i)).symbol) // TODO: disambiguate
86- val newLocals = accessors.map(x =>
87- // TODO: it would be nice to have an additional optimisation that
88- // TODO: is capable of turning those mutable ones into immutable in common cases
89- ctx.newSymbol(ctx.owner.enclosingMethod, (refVal.name + " $" + x.name).toTermName, Synthetic | Mutable , x.asSeenFrom(refVal.info).info.finalResultType.widenDealias)
90- )
91- val fieldMapping = accessors zip newLocals
92- val productMappings = productAccessors zip newLocals
93- (refVal, (fieldMapping ++ productMappings).toMap)
94- }.toMap
103+ hasPerfectRHS.iterator
104+ .map(_._1)
105+ .filter(x => ! x.is(Method | Label ) && gettersCalled.contains(x.symbol) && x.symbol.info.classSymbol.is(CaseClass ))
106+ .map { refVal =>
107+ simplify.println(s " replacing ${refVal.symbol.fullName} with stack-allocated fields " )
108+ var accessors = refVal.info.classSymbol.caseAccessors.filter(_.isGetter) // TODO: drop mutable ones
109+ if (accessors.isEmpty) accessors = refVal.info.classSymbol.caseAccessors
110+
111+ val productAccessors = (1 to accessors.length).map { i =>
112+ refVal.info.member(nme.productAccessorName(i)).symbol
113+ } // TODO: disambiguate
114+
115+ val newLocals = accessors.map { x =>
116+ // TODO: it would be nice to have an additional optimisation that
117+ // TODO: is capable of turning those mutable ones into immutable in common cases
118+ val owner : Symbol = ctx.owner.enclosingMethod
119+ val name : Name = (refVal.name + " $" + x.name).toTermName
120+ val flags : FlagSet = Synthetic | Mutable
121+ val info : Type = x.asSeenFrom(refVal.info).info.finalResultType.widenDealias
122+ ctx.newSymbol(owner, name, flags, info)
123+ }
124+ val fieldMapping = accessors zip newLocals
125+ val productMappings = productAccessors zip newLocals
126+ (refVal, (fieldMapping ++ productMappings).toMap)
127+ }.toMap
128+
95129 val toSplit : mutable.Set [Symbol ] = mutable.Set .empty ++ newMappings.keySet
96130
97131 def splitWrites (t : Tree , target : Symbol ): Tree = {
98132 t match {
99- case tree@ Block (stats, expr) => cpy.Block (tree)(stats, splitWrites(expr, target))
100- case tree@ If (_, thenp, elsep) => cpy.If (tree)(thenp = splitWrites(thenp, target), elsep = splitWrites(elsep, target))
101- case Apply (sel , args) if sel.symbol.isConstructor && t.tpe.widenDealias == target.info.widenDealias.finalResultType.widenDealias =>
133+ case tree @ Block (stats, expr) =>
134+ cpy.Block (tree)(stats, splitWrites(expr, target))
135+
136+ case tree @ If (_, thenp, elsep) =>
137+ cpy.If (tree)(thenp = splitWrites(thenp, target), elsep = splitWrites(elsep, target))
138+
139+ case Apply (sel, args) if sel.symbol.isConstructor && t.tpe.widenDealias == target.info.widenDealias.finalResultType.widenDealias =>
102140 val fieldsByAccessors = newMappings(target)
103- var accessors = target.info.classSymbol.caseAccessors.filter(_.isGetter) // TODO: when is this filter needed?
104- if (accessors.isEmpty) accessors = target.info.classSymbol.caseAccessors
105- val assigns = (accessors zip args) map (x => ref(fieldsByAccessors(x._1)).becomes(x._2))
141+ var accessors = symbolAccessors(target)
142+ val assigns = (accessors zip args).map(x => ref(fieldsByAccessors(x._1)).becomes(x._2))
106143 val recreate = sel.appliedToArgs(accessors.map(x => ref(fieldsByAccessors(x))))
107144 Block (assigns, recreate)
145+
108146 case Apply (fun, _) if fun.symbol.is(Label ) =>
109147 t // Do nothing. It will do on its own.
148+
110149 case t : Ident if ! t.symbol.owner.isClass && newMappings.contains(t.symbol) && t.symbol.info.classSymbol == target.info.classSymbol =>
111150 val fieldsByAccessorslhs = newMappings(target)
112151 val fieldsByAccessorsrhs = newMappings(t.symbol)
113- val accessors = target.info.classSymbol.caseAccessors.filter(_.isGetter )
114- val assigns = accessors map (x => ref(fieldsByAccessorslhs(x)).becomes(ref(fieldsByAccessorsrhs(x))))
152+ val accessors = symbolAccessors(target )
153+ val assigns = accessors. map(x => ref(fieldsByAccessorslhs(x)).becomes(ref(fieldsByAccessorsrhs(x))))
115154 Block (assigns, t)
116- // If `t` is itself split, push writes.
155+ // If `t` is itself split, push writes.
156+
117157 case _ =>
118158 evalOnce(t){ev =>
119159 if (ev.tpe.derivesFrom(defn.NothingClass )) ev
120160 else {
121161 val fieldsByAccessors = newMappings(target)
122- val accessors = target.info.classSymbol.caseAccessors.filter(_.isGetter )
123- val assigns = accessors map (x => ref(fieldsByAccessors(x)).becomes(ev.select(x)))
162+ val accessors = symbolAccessors(target )
163+ val assigns = accessors. map(x => ref(fieldsByAccessors(x)).becomes(ev.select(x)))
124164 Block (assigns, ev)
125165 }
126166 } // Need to eval-once and update fields.
127-
128167 }
129168 }
130169
@@ -142,33 +181,37 @@ class InlineLocalObjects extends Optimisation {
142181 gettersCalled.clear()
143182
144183 val res : Tree => Tree = {
145- case ddef : DefDef if ddef .symbol.is(Label ) =>
146- newMappings.get(followCases(ddef .symbol)) match {
184+ case t : DefDef if t .symbol.is(Label ) =>
185+ newMappings.get(followCases(t .symbol)) match {
147186 case Some (mappings) =>
148- cpy.DefDef (ddef )(rhs = splitWrites(ddef .rhs, followCases(ddef .symbol)))
149- case _ => ddef
187+ cpy.DefDef (t )(rhs = splitWrites(t .rhs, followCases(t .symbol)))
188+ case _ => t
150189 }
151- case a : ValDef if toSplit.contains(a.symbol) =>
152- toSplit -= a.symbol
190+
191+ case t : ValDef if toSplit.contains(t.symbol) =>
192+ toSplit -= t.symbol
153193 // Break ValDef apart into fields + boxed value
154- val newFields = newMappings(a .symbol).values.toSet
194+ val newFields = newMappings(t .symbol).values.toSet
155195 Thicket (
156196 newFields.map(x => ValDef (x.asTerm, defaultValue(x.symbol.info.widenDealias))).toList :::
157- List (cpy.ValDef (a)(rhs = splitWrites(a.rhs, a.symbol))))
158- case ass : Assign =>
159- newMappings.get(ass.lhs.symbol) match {
160- case None => ass
197+ List (cpy.ValDef (t)(rhs = splitWrites(t.rhs, t.symbol))))
198+
199+ case t : Assign =>
200+ newMappings.get(t.lhs.symbol) match {
201+ case None => t
161202 case Some (mapping) =>
162- val updates = mapping.filter(x => x._1.is(CaseAccessor )).map(x => ref(x._2).becomes(ref(ass .lhs.symbol).select(x._1))).toList
163- Thicket (ass :: updates)
203+ val updates = mapping.filter(x => x._1.is(CaseAccessor )).map(x => ref(x._2).becomes(ref(t .lhs.symbol).select(x._1))).toList
204+ Thicket (t :: updates)
164205 }
165- case t @ Select (rec, _) if (t.symbol.isGetter && ! t.symbol.is(Mutable )) ||
206+
207+ case t @ Select (rec, _) if (t.symbol.isGetter && ! t.symbol.is(Mutable | Lazy )) ||
166208 (t.symbol.maybeOwner.derivesFrom(defn.ProductClass ) && t.symbol.owner.is(CaseClass ) && t.symbol.name.isSelectorName) ||
167209 (t.symbol.is(CaseAccessor ) && ! t.symbol.is(Mutable )) =>
168210 newMappings.getOrElse(rec.symbol, Map .empty).get(t.symbol) match {
169211 case None => t
170212 case Some (newSym) => ref(newSym)
171213 }
214+
172215 case t => t
173216 }
174217 res
0 commit comments