Skip to content

Commit b583461

Browse files
authored
Merge pull request #2513 from dotty-staging/local-opt-rebased-2
Local optimisations
2 parents e51d44a + b7149c9 commit b583461

34 files changed

+1937
-155
lines changed

compiler/src/dotty/tools/backend/jvm/LabelDefs.scala

Lines changed: 14 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,11 @@ import StdNames.nme
7575
* Unreachable jumps will be eliminated by local dead code analysis.
7676
* After JVM is smart enough to remove next-line jumps
7777
*
78-
* Note that Label DefDefs can be only nested in Block, otherwise no one would
79-
* be able to call them Other DefDefs are eliminated
78+
* Note that his phase Ychecking this phase required softening scoping rules
79+
* as it intentionally allowed to break scoping rules inside methods for labels.
80+
* This is modified by setting `labelsReordered` flag in Phases.
81+
*
82+
* @author Dmitry Petrashko
8083
*/
8184
class LabelDefs extends MiniPhaseTransform {
8285
def phaseName: String = "labelDef"
@@ -90,76 +93,24 @@ class LabelDefs extends MiniPhaseTransform {
9093
else {
9194
collectLabelDefs.clear
9295
val newRhs = collectLabelDefs.transform(tree.rhs)
93-
val labelCalls = collectLabelDefs.labelCalls
94-
var entryPoints = collectLabelDefs.parentLabelCalls
9596
var labelDefs = collectLabelDefs.labelDefs
96-
var callCounts = collectLabelDefs.callCounts
97-
98-
// make sure that for every label there's a single location it should return and single entry point
99-
// if theres already a location that it returns to that's a failure
100-
val disallowed = new mutable.HashMap[Symbol, Tree]()
101-
queue.sizeHint(labelCalls.size + entryPoints.size)
10297

10398
def putLabelDefsNearCallees = new TreeMap() {
10499

105100
override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = {
106101
tree match {
107-
case t: Apply if (entryPoints.contains(t)) =>
108-
entryPoints = entryPoints - t
109-
labelLevel = labelLevel + 1
110-
val r = Block(moveLabels(t), t)
111-
labelLevel = labelLevel - 1
112-
if (labelLevel == 0) beingAppended.clear()
113-
r
114-
case _ => if (entryPoints.nonEmpty && labelDefs.nonEmpty) super.transform(tree) else tree
115-
}
102+
case t: Apply if labelDefs.contains(t.symbol) =>
103+
val labelDef = labelDefs(t.symbol)
104+
labelDefs -= t.symbol
116105

117-
}
118-
}
106+
val labelDef2 = transform(labelDef)
107+
Block(labelDef2:: Nil, t)
119108

120-
def moveLabels(entryPoint: Apply): List[Tree] = {
121-
val entrySym = entryPoint.symbol
122-
if ((entrySym is Flags.Label) && labelDefs.contains(entrySym)) {
123-
val visitedNow = new mutable.HashMap[Symbol, Tree]()
124-
val treesToAppend = new ArrayBuffer[Tree]() // order matters. parents should go first
125-
treesToAppend += labelDefs(entrySym)
126-
queue.clear()
127-
128-
var visited = 0
129-
queue += entryPoint
130-
while (visited < queue.size) {
131-
val owningLabelDefSym = queue(visited).symbol
132-
for (call <- labelCalls(owningLabelDefSym)) {
133-
val callSym = call.symbol
134-
if (!beingAppended.contains(callSym)) {
135-
if (disallowed.contains(callSym)) {
136-
val oldCall = disallowed(callSym)
137-
ctx.error(s"Multiple return locations for Label $oldCall and $call", callSym.pos)
138-
} else {
139-
if ((!visitedNow.contains(callSym)) && labelDefs.contains(callSym)) {
140-
val defTree = labelDefs(callSym)
141-
visitedNow.put(callSym, defTree)
142-
val callCount = callCounts(callSym)
143-
if (callCount > 1) {
144-
if (!treesToAppend.contains(defTree)) {
145-
treesToAppend += defTree
146-
queue += call
147-
148-
}
149-
} else if (entryPoint.symbol ne callSym) entryPoints += call
150-
}
151-
}
152-
}
153-
}
154-
155-
visited += 1
109+
case _ => if (labelDefs.nonEmpty) super.transform(tree) else tree
156110
}
157-
beingAppended ++= treesToAppend.map(_.symbol)
158-
treesToAppend.toList.map(putLabelDefsNearCallees.transform)
159-
} else Nil
111+
}
160112
}
161113

162-
163114
val res = cpy.DefDef(tree)(rhs = putLabelDefsNearCallees.transform(newRhs))
164115

165116
res
@@ -168,22 +119,11 @@ class LabelDefs extends MiniPhaseTransform {
168119

169120
object collectLabelDefs extends TreeMap() {
170121

171-
// label calls from this DefDef
172-
var parentLabelCalls: mutable.Set[Tree] = new mutable.HashSet[Tree]()
173-
var callCounts: mutable.Map[Symbol, Int] = new mutable.HashMap[Symbol, Int]().withDefaultValue(0)
174-
175-
def shouldMoveLabel = true
176-
177122
// labelSymbol -> Defining tree
178123
val labelDefs = new mutable.HashMap[Symbol, Tree]()
179-
// owner -> all calls by this owner
180-
val labelCalls = new mutable.HashMap[Symbol, mutable.Set[Tree]]()
181-
var owner: Symbol = null
182124

183125
def clear = {
184-
parentLabelCalls.clear()
185126
labelDefs.clear()
186-
labelCalls.clear()
187127
}
188128

189129
override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
@@ -196,30 +136,14 @@ class LabelDefs extends MiniPhaseTransform {
196136
}
197137
case t: DefDef =>
198138
assert(t.symbol is Flags.Label)
199-
200-
val st = parentLabelCalls
201-
parentLabelCalls = new mutable.HashSet[Tree]()
202-
val symt = owner
203-
owner = t.symbol
204-
205139
val r = super.transform(tree)
206-
207-
owner = symt
208-
labelCalls(r.symbol) = parentLabelCalls
209-
parentLabelCalls = st
210-
211-
if (shouldMoveLabel) {
212-
labelDefs(r.symbol) = r
213-
EmptyTree
214-
} else r
140+
labelDefs(r.symbol) = r
141+
EmptyTree
215142
case t: Apply if t.symbol is Flags.Label =>
216143
val sym = t.symbol
217-
parentLabelCalls = parentLabelCalls + t
218-
if (owner != sym) callCounts(sym) = callCounts(sym) + 1
219144
super.transform(tree)
220145
case _ =>
221146
super.transform(tree)
222-
223147
}
224148
}
225149
}

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import core.DenotTransformers.DenotTransformer
1717
import core.Denotations.SingleDenotation
1818

1919
import dotty.tools.backend.jvm.{LabelDefs, GenBCode, CollectSuperCalls}
20+
import dotty.tools.dotc.transform.linker.Simplify
2021

2122
/** The central class of the dotc compiler. The job of a compiler is to create
2223
* runs, which process given `phases` in a given `rootContext`.
@@ -75,6 +76,7 @@ class Compiler {
7576
new ElimByName, // Expand by-name parameter references
7677
new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings
7778
new ResolveSuper, // Implement super accessors and add forwarders to trait methods
79+
new Simplify, // Perform local optimizations, simplified versions of what linker does.
7880
new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives
7981
new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method
8082
new ArrayConstructors), // Intercept creation of (non-generic) arrays and intrinsify.
@@ -88,9 +90,10 @@ class Compiler {
8890
new NonLocalReturns, // Expand non-local returns
8991
new CapturedVars, // Represent vars captured by closures as heap objects
9092
new Constructors, // Collect initialization code in primary constructors
91-
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
93+
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
9294
new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions.
93-
new GetClass), // Rewrites getClass calls on primitive types.
95+
new GetClass, // Rewrites getClass calls on primitive types.
96+
new Simplify), // Perform local optimizations, simplified versions of what linker does.
9497
List(new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments
9598
// Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
9699
new ElimStaticThis, // Replace `this` references to static objects by global identifiers

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1190,7 +1190,9 @@ object Trees {
11901190
}
11911191

11921192
abstract class TreeAccumulator[X] {
1193+
// Ties the knot of the traversal: call `foldOver(x, tree))` to dive in the `tree` node.
11931194
def apply(x: X, tree: Tree)(implicit ctx: Context): X
1195+
11941196
def apply(x: X, trees: Traversable[Tree])(implicit ctx: Context): X = (x /: trees)(apply)
11951197
def foldOver(x: X, tree: Tree)(implicit ctx: Context): X = {
11961198
def localCtx =
@@ -1301,7 +1303,7 @@ object Trees {
13011303
class ShallowFolder[X](f: (X, Tree) => X) extends TreeAccumulator[X] {
13021304
def apply(x: X, tree: Tree)(implicit ctx: Context): X = {
13031305
val x1 = f(x, tree)
1304-
if (x1.asInstanceOf[AnyRef] ne x1.asInstanceOf[AnyRef]) x1
1306+
if (x1.asInstanceOf[AnyRef] ne x.asInstanceOf[AnyRef]) x1
13051307
else foldOver(x1, tree)
13061308
}
13071309
}

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
454454
override val cpy: TypedTreeCopier = // Type ascription needed to pick up any new members in TreeCopier (currently there are none)
455455
new TypedTreeCopier
456456

457+
val cpyBetweenPhases = new TimeTravellingTreeCopier
458+
457459
class TypedTreeCopier extends TreeCopier {
458460
def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[Type] =
459461
copied.withTypeUnchecked(tree.tpe)
@@ -472,16 +474,25 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
472474
}
473475
}
474476

475-
override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply =
476-
ta.assignType(untpd.cpy.Apply(tree)(fun, args), fun, args)
477-
// Note: Reassigning the original type if `fun` and `args` have the same types as before
478-
// does not work here: The computed type depends on the widened function type, not
479-
// the function type itself. A treetransform may keep the function type the
480-
// same but its widened type might change.
477+
override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply = {
478+
val tree1 = untpd.cpy.Apply(tree)(fun, args)
479+
tree match {
480+
case tree: Apply
481+
if (fun.tpe eq tree.fun.tpe) && sameTypes(args, tree.args) =>
482+
tree1.withTypeUnchecked(tree.tpe)
483+
case _ => ta.assignType(tree1, fun, args)
484+
}
485+
}
481486

482-
override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply =
483-
ta.assignType(untpd.cpy.TypeApply(tree)(fun, args), fun, args)
484-
// Same remark as for Apply
487+
override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = {
488+
val tree1 = untpd.cpy.TypeApply(tree)(fun, args)
489+
tree match {
490+
case tree: TypeApply
491+
if (fun.tpe eq tree.fun.tpe) && sameTypes(args, tree.args) =>
492+
tree1.withTypeUnchecked(tree.tpe)
493+
case _ => ta.assignType(tree1, fun, args)
494+
}
495+
}
485496

486497
override def Literal(tree: Tree)(const: Constant)(implicit ctx: Context): Literal =
487498
ta.assignType(untpd.cpy.Literal(tree)(const))
@@ -514,10 +525,15 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
514525
}
515526
}
516527

517-
override def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure =
518-
ta.assignType(untpd.cpy.Closure(tree)(env, meth, tpt), meth, tpt)
519-
// Same remark as for Apply
520528

529+
override def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = {
530+
val tree1 = untpd.cpy.Closure(tree)(env, meth, tpt)
531+
tree match {
532+
case tree: Closure if sameTypes(env, tree.env) && (meth.tpe eq tree.meth.tpe) && (tpt.tpe eq tree.tpt.tpe) =>
533+
tree1.withTypeUnchecked(tree.tpe)
534+
case _ => ta.assignType(tree1, meth, tpt)
535+
}
536+
}
521537
override def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = {
522538
val tree1 = untpd.cpy.Match(tree)(selector, cases)
523539
tree match {
@@ -574,6 +590,25 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
574590
Try(tree: Tree)(expr, cases, finalizer)
575591
}
576592

593+
class TimeTravellingTreeCopier extends TypedTreeCopier {
594+
override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply =
595+
ta.assignType(untpd.cpy.Apply(tree)(fun, args), fun, args)
596+
// Note: Reassigning the original type if `fun` and `args` have the same types as before
597+
// does not work here: The computed type depends on the widened function type, not
598+
// the function type itself. A treetransform may keep the function type the
599+
// same but its widened type might change.
600+
601+
override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply =
602+
ta.assignType(untpd.cpy.TypeApply(tree)(fun, args), fun, args)
603+
// Same remark as for Apply
604+
605+
override def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure =
606+
ta.assignType(untpd.cpy.Closure(tree)(env, meth, tpt), meth, tpt)
607+
608+
override def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt)(implicit ctx: Context): Closure =
609+
Closure(tree: Tree)(env, meth, tpt)
610+
}
611+
577612
override def skipTransform(tree: Tree)(implicit ctx: Context) = tree.tpe.isError
578613

579614
implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal {

compiler/src/dotty/tools/dotc/config/CompilerCommand.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
package dotty.tools.dotc
32
package config
43

compiler/src/dotty/tools/dotc/config/Printers.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ object Printers {
3232
val pickling: Printer = noPrinter
3333
val inlining: Printer = noPrinter
3434
val exhaustivity: Printer = noPrinter
35+
val simplify: Printer = noPrinter
3536
}

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,16 @@ class Definitions {
400400
def Boolean_&& = Boolean_andR.symbol
401401
lazy val Boolean_orR = BooleanClass.requiredMethodRef(nme.ZOR)
402402
def Boolean_|| = Boolean_orR.symbol
403+
lazy val Boolean_eqeqR = BooleanClass.info.member(nme.EQ).suchThat(_.info.firstParamTypes match {
404+
case List(pt) => (pt isRef BooleanClass)
405+
case _ => false
406+
})
407+
def Boolean_== = Boolean_eqeqR.symbol
408+
lazy val Boolean_neqeqR = BooleanClass.info.member(nme.NE).suchThat(_.info.firstParamTypes match {
409+
case List(pt) => (pt isRef BooleanClass)
410+
case _ => false
411+
})
412+
def Boolean_!= = Boolean_neqeqR.symbol
403413

404414
lazy val ByteType: TypeRef = valueTypeRef("scala.Byte", BoxedByteType, java.lang.Byte.TYPE, ByteEnc)
405415
def ByteClass(implicit ctx: Context) = ByteType.symbol.asClass
@@ -431,6 +441,13 @@ class Definitions {
431441
lazy val Long_LSR_Int = LongType.member(nme.LSR).requiredSymbol(
432442
x => (x is Method) && (x.info.firstParamTypes.head isRef defn.IntClass)
433443
)
444+
lazy val Long_plusR = LongClass.requiredMethodRef(nme.PLUS, List(LongType))
445+
def Long_+ = Long_plusR.symbol
446+
lazy val Long_mulR = LongClass.requiredMethodRef(nme.MUL, List(LongType))
447+
def Long_* = Long_mulR.symbol
448+
lazy val Long_divR = LongClass.requiredMethodRef(nme.DIV, List(LongType))
449+
def Long_/ = Long_divR.symbol
450+
434451
lazy val FloatType: TypeRef = valueTypeRef("scala.Float", BoxedFloatType, java.lang.Float.TYPE, FloatEnc)
435452
def FloatClass(implicit ctx: Context) = FloatType.symbol.asClass
436453
lazy val DoubleType: TypeRef = valueTypeRef("scala.Double", BoxedDoubleType, java.lang.Double.TYPE, DoubleEnc)
@@ -491,6 +508,11 @@ class Definitions {
491508
lazy val BoxedNumberClass = ctx.requiredClass("java.lang.Number")
492509
lazy val ThrowableClass = ctx.requiredClass("java.lang.Throwable")
493510
lazy val ClassCastExceptionClass = ctx.requiredClass("java.lang.ClassCastException")
511+
lazy val ArithmeticExceptionClass = ctx.requiredClass("java.lang.ArithmeticException")
512+
lazy val ArithmeticExceptionClass_stringConstructor = ArithmeticExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match {
513+
case List(pt) => (pt isRef StringClass)
514+
case _ => false
515+
}).symbol.asTerm
494516
lazy val JavaSerializableClass = ctx.requiredClass("java.io.Serializable")
495517
lazy val ComparableClass = ctx.requiredClass("java.lang.Comparable")
496518

@@ -522,6 +544,10 @@ class Definitions {
522544
def DynamicClass(implicit ctx: Context) = DynamicType.symbol.asClass
523545
lazy val OptionType: TypeRef = ctx.requiredClassRef("scala.Option")
524546
def OptionClass(implicit ctx: Context) = OptionType.symbol.asClass
547+
lazy val SomeType: TypeRef = ctx.requiredClassRef("scala.Some")
548+
def SomeClass(implicit ctx: Context) = SomeType.symbol.asClass
549+
lazy val NoneModuleRef: TermRef = ctx.requiredModuleRef("scala.None")
550+
def NoneClass(implicit ctx: Context) = NoneModuleRef.symbol.moduleClass.asClass
525551
lazy val EnumType: TypeRef = ctx.requiredClassRef("scala.Enum")
526552
def EnumClass(implicit ctx: Context) = EnumType.symbol.asClass
527553
lazy val EnumValuesType: TypeRef = ctx.requiredClassRef("scala.runtime.EnumValues")

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,9 @@ object NameKinds {
308308
val PatMatCaseName = new UniqueNameKind("case")
309309
val PatMatMatchFailName = new UniqueNameKind("matchFail")
310310
val PatMatSelectorName = new UniqueNameKind("selector")
311+
val LocalOptFact = new UniqueNameKind("fact")
312+
val LocalOptSelector = new UniqueNameKind("selector")
313+
val LocalOptFallback = new UniqueNameKind("fallback")
311314

312315
/** The kind of names of default argument getters */
313316
val DefaultGetterName = new NumberedNameKind(DEFAULTGETTER, "DefaultGetter") {
@@ -384,4 +387,4 @@ object NameKinds {
384387
def qualifiedNameKindOfTag : collection.Map[Int, QualifiedNameKind] = qualifiedNameKinds
385388
def numberedNameKindOfTag : collection.Map[Int, NumberedNameKind] = numberedNameKinds
386389
def uniqueNameKindOfSeparator: collection.Map[String, UniqueNameKind] = uniqueNameKinds
387-
}
390+
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,10 @@ object Scopes {
9191
/** Is the scope empty? */
9292
def isEmpty: Boolean = lastEntry eq null
9393

94-
def foreach[U](p: Symbol => U)(implicit ctx: Context): Unit = toList foreach p
94+
/** Applies a function f to all Symbols of this Scope. */
95+
def foreach[U](f: Symbol => U)(implicit ctx: Context): Unit = toList.foreach(f)
9596

97+
/** Selects all Symbols of this Scope which satisfy a predicate. */
9698
def filter(p: Symbol => Boolean)(implicit ctx: Context): List[Symbol] = {
9799
ensureComplete()
98100
var syms: List[Symbol] = Nil
@@ -105,6 +107,10 @@ object Scopes {
105107
syms
106108
}
107109

110+
/** Tests whether a predicate holds for at least one Symbol of this Scope. */
111+
def exists(p: Symbol => Boolean)(implicit ctx: Context): Boolean = filter(p).isEmpty
112+
113+
/** Finds the first Symbol of this Scope satisfying a predicate, if any. */
108114
def find(p: Symbol => Boolean)(implicit ctx: Context): Symbol = filter(p) match {
109115
case sym :: _ => sym
110116
case _ => NoSymbol

0 commit comments

Comments
 (0)