Skip to content

Commit b791979

Browse files
authored
Merge pull request #2813 from dotty-staging/inline-local-objects
Local optimisation: Inline local objects
2 parents 23e64a3 + 23c74b3 commit b791979

17 files changed

+266
-242
lines changed

compiler/src/dotty/tools/dotc/core/NameKinds.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,8 @@ object NameKinds {
310310
val PatMatCaseName = new UniqueNameKind("case")
311311
val PatMatMatchFailName = new UniqueNameKind("matchFail")
312312
val PatMatSelectorName = new UniqueNameKind("selector")
313-
val LocalOptFact = new UniqueNameKind("fact")
314-
val LocalOptSelector = new UniqueNameKind("selector")
315-
val LocalOptFallback = new UniqueNameKind("fallback")
313+
314+
val LocalOptInlineLocalObj = new UniqueNameKind("ilo")
316315

317316
/** The kind of names of default argument getters */
318317
val DefaultGetterName = new NumberedNameKind(DEFAULTGETTER, "DefaultGetter") {

compiler/src/dotty/tools/dotc/transform/localopt/BubbleUpNothing.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class BubbleUpNothing extends Optimisation {
2323
import ast.tpd._
2424

2525
def visitor(implicit ctx: Context) = NoVisitor
26+
def clear(): Unit = ()
2627

2728
def transformer(implicit ctx: Context): Tree => Tree = {
2829
case t @ Apply(Select(Notathing(qual), _), args) =>

compiler/src/dotty/tools/dotc/transform/localopt/ConstantFold.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import Simplify.desugarIdent
3030
import ast.tpd._
3131

3232
def visitor(implicit ctx: Context) = NoVisitor
33+
def clear(): Unit = ()
3334

3435
def transformer(implicit ctx: Context): Tree => Tree = { x => preEval(x) match {
3536
// TODO: include handling of isInstanceOf similar to one in IsInstanceOfEvaluator

compiler/src/dotty/tools/dotc/transform/localopt/Devalify.scala

Lines changed: 42 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ package transform.localopt
44
import core.Constants.Constant
55
import core.Contexts.Context
66
import core.Flags._
7-
import core.NameOps._
87
import core.Symbols._
98
import core.Types._
109
import ast.Trees._
1110
import scala.collection.mutable
1211
import config.Printers.simplify
13-
import Simplify.{desugarIdent, isEffectivelyMutable}
12+
import Simplify._
1413
import transform.SymUtils._
1514

1615
/** Inline vals and remove vals that are aliases to other vals
@@ -32,6 +31,14 @@ class Devalify extends Optimisation {
3231
// Either a duplicate or a read through series of immutable fields
3332
val copies = mutable.HashMap[Symbol, Tree]()
3433

34+
def clear(): Unit = {
35+
timesUsed.clear()
36+
timesUsedAsType.clear()
37+
defined.clear()
38+
usedInInnerClass.clear()
39+
copies.clear()
40+
}
41+
3542
def visitType(tp: Type)(implicit ctx: Context): Unit = {
3643
tp.foreachPart(x => x match {
3744
case TermRef(NoPrefix, _) =>
@@ -164,66 +171,38 @@ class Devalify extends Optimisation {
164171
case _ => t
165172
}
166173

167-
def readingOnlyVals(t: Tree)(implicit ctx: Context): Boolean = {
168-
def isGetterOfAImmutableField = t.symbol.isGetter && !t.symbol.is(Mutable)
169-
def isCaseClassWithVar = t.symbol.info.decls.exists(_.is(Mutable))
170-
def isAccessingProductField = t.symbol.exists &&
171-
t.symbol.owner.derivesFrom(defn.ProductClass) &&
172-
t.symbol.owner.is(CaseClass) &&
173-
t.symbol.name.isSelectorName &&
174-
!isCaseClassWithVar // Conservatively covers case class A(var x: Int)
175-
def isImmutableCaseAccessor = t.symbol.is(CaseAccessor) && !t.symbol.is(Mutable)
176-
177-
dropCasts(t) match {
178-
case Typed(exp, _) => readingOnlyVals(exp)
179-
180-
case TypeApply(fun @ Select(rec, _), List(tp)) =>
181-
if ((fun.symbol eq defn.Any_asInstanceOf) && rec.tpe.derivesFrom(tp.tpe.classSymbol))
182-
readingOnlyVals(rec)
183-
else false
184-
185-
case Apply(Select(rec, _), Nil) =>
186-
if (isGetterOfAImmutableField || isAccessingProductField || isImmutableCaseAccessor)
187-
readingOnlyVals(rec)
188-
else false
189-
190-
case Select(rec, _) if t.symbol.is(Method) =>
191-
if (isGetterOfAImmutableField)
192-
readingOnlyVals(rec) // Getter of an immutable field
193-
else if (isAccessingProductField) {
194-
def isImmutableField = {
195-
val fieldId = t.symbol.name.toString.drop(1).toInt - 1
196-
!t.symbol.owner.caseAccessors(ctx)(fieldId).is(Mutable)
197-
}
198-
if (isImmutableField) readingOnlyVals(rec) // Accessing a field of a product
199-
else false
200-
} else if (isImmutableCaseAccessor)
201-
readingOnlyVals(rec)
202-
else false
203-
204-
case t @ Select(qual, _) if !isEffectivelyMutable(t) =>
205-
readingOnlyVals(qual)
206-
207-
case t: Ident if !t.symbol.is(Mutable | Method) && !t.symbol.info.dealias.isInstanceOf[ExprType] =>
208-
desugarIdent(t) match {
209-
case Some(t) => readingOnlyVals(t)
210-
case None => true
211-
}
212-
213-
case t: This => true
214-
// null => false, or the following fails devalify:
215-
// trait I {
216-
// def foo: Any = null
217-
// }
218-
// object Main {
219-
// def main = {
220-
// val s: I = null
221-
// s.foo
222-
// }
223-
// }
224-
case Literal(Constant(null)) => false
225-
case t: Literal => true
226-
case _ => false
227-
}
174+
def readingOnlyVals(t: Tree)(implicit ctx: Context): Boolean = dropCasts(t) match {
175+
case Typed(exp, _) => readingOnlyVals(exp)
176+
177+
case TypeApply(fun @ Select(rec, _), List(tp)) =>
178+
val isAsInstanceOf = fun.symbol == defn.Any_asInstanceOf && rec.tpe.derivesFrom(tp.tpe.classSymbol)
179+
isAsInstanceOf && readingOnlyVals(rec)
180+
181+
case t @ Apply(Select(rec, _), Nil) =>
182+
isImmutableAccessor(t) && readingOnlyVals(rec)
183+
184+
case t @ Select(rec, _) if t.symbol.is(Method) =>
185+
isImmutableAccessor(t) && readingOnlyVals(rec)
186+
187+
case t @ Select(qual, _) if !isEffectivelyMutable(t) =>
188+
readingOnlyVals(qual)
189+
190+
case t: Ident if !t.symbol.is(Mutable | Method) && !t.symbol.info.dealias.isInstanceOf[ExprType] =>
191+
desugarIdent(t).forall(readingOnlyVals)
192+
193+
case t: This => true
194+
// null => false, or the following fails devalify:
195+
// trait I {
196+
// def foo: Any = null
197+
// }
198+
// object Main {
199+
// def main = {
200+
// val s: I = null
201+
// s.foo
202+
// }
203+
// }
204+
case Literal(Constant(null)) => false
205+
case t: Literal => true
206+
case _ => false
228207
}
229208
}

compiler/src/dotty/tools/dotc/transform/localopt/DropGoodCasts.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import Simplify.isEffectivelyMutable
2424
import ast.tpd._
2525

2626
def visitor(implicit ctx: Context) = NoVisitor
27+
def clear(): Unit = ()
2728

2829
def transformer(implicit ctx: Context): Tree => Tree = {
2930
case t @ If(cond, thenp, elsep) =>

compiler/src/dotty/tools/dotc/transform/localopt/DropNoEffects.scala

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ package transform.localopt
33

44
import core.TypeErasure
55
import core.Contexts.Context
6-
import core.NameOps._
76
import core.Symbols._
87
import core.Types._
98
import core.Flags._
109
import ast.Trees._
11-
import Simplify.desugarIdent
10+
import Simplify._
1211

1312
/** Removes side effect free statements in blocks and Defdef.
1413
* Flattens blocks (except Closure-blocks)
@@ -20,6 +19,7 @@ class DropNoEffects(val simplifyPhase: Simplify) extends Optimisation {
2019
import ast.tpd._
2120

2221
def visitor(implicit ctx: Context) = NoVisitor
22+
def clear(): Unit = ()
2323

2424
def transformer(implicit ctx: Context): Tree => Tree = {
2525
// Remove empty blocks
@@ -79,11 +79,7 @@ class DropNoEffects(val simplifyPhase: Simplify) extends Optimisation {
7979
elsep = nelsep.orElse(if (elsep.isInstanceOf[Literal]) elsep else unitLiteral))
8080

8181
// Accessing a field of a product
82-
case t @ Select(rec, _)
83-
if (t.symbol.isGetter && !t.symbol.is(Mutable | Lazy)) ||
84-
(t.symbol.owner.derivesFrom(defn.ProductClass) && t.symbol.owner.is(CaseClass) && t.symbol.name.isSelectorName) ||
85-
(t.symbol.is(CaseAccessor) && !t.symbol.is(Mutable)) =>
86-
82+
case t @ Select(rec, _) if isImmutableAccessor(t) =>
8783
keepOnlySideEffects(rec)
8884

8985
// !name.eq(nme.TYPE_) && // Keep the .TYPE added by ClassOf, would be needed for AfterErasure

compiler/src/dotty/tools/dotc/transform/localopt/InlineCaseIntrinsics.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class InlineCaseIntrinsics(val simplifyPhase: Simplify) extends Optimisation {
2424
import ast.tpd._
2525

2626
def visitor(implicit ctx: Context): Tree => Unit = NoVisitor
27+
def clear(): Unit = ()
2728

2829
def transformer(implicit ctx: Context): Tree => Tree = {
2930
// For synthetic applies on case classes (both dotty/scalac)

compiler/src/dotty/tools/dotc/transform/localopt/InlineLabelsCalledOnce.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ class InlineLabelsCalledOnce extends Optimisation {
1818
val timesUsed = mutable.HashMap[Symbol, Int]()
1919
val defined = mutable.HashMap[Symbol, DefDef]()
2020

21+
def clear(): Unit = {
22+
timesUsed.clear()
23+
defined.clear()
24+
}
25+
2126
def visitor(implicit ctx: Context): Tree => Unit = {
2227
case d: DefDef if d.symbol.is(Label) =>
2328
var isRecursive = false

0 commit comments

Comments
 (0)