Skip to content

Commit

Permalink
Merge pull request #2513 from dotty-staging/local-opt-rebased-2
Browse files Browse the repository at this point in the history
Local optimisations
  • Loading branch information
DarkDimius committed May 30, 2017
2 parents e51d44a + b7149c9 commit b583461
Show file tree
Hide file tree
Showing 34 changed files with 1,937 additions and 155 deletions.
104 changes: 14 additions & 90 deletions compiler/src/dotty/tools/backend/jvm/LabelDefs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,11 @@ import StdNames.nme
* Unreachable jumps will be eliminated by local dead code analysis.
* After JVM is smart enough to remove next-line jumps
*
* Note that Label DefDefs can be only nested in Block, otherwise no one would
* be able to call them Other DefDefs are eliminated
* Note that his phase Ychecking this phase required softening scoping rules
* as it intentionally allowed to break scoping rules inside methods for labels.
* This is modified by setting `labelsReordered` flag in Phases.
*
* @author Dmitry Petrashko
*/
class LabelDefs extends MiniPhaseTransform {
def phaseName: String = "labelDef"
Expand All @@ -90,76 +93,24 @@ class LabelDefs extends MiniPhaseTransform {
else {
collectLabelDefs.clear
val newRhs = collectLabelDefs.transform(tree.rhs)
val labelCalls = collectLabelDefs.labelCalls
var entryPoints = collectLabelDefs.parentLabelCalls
var labelDefs = collectLabelDefs.labelDefs
var callCounts = collectLabelDefs.callCounts

// make sure that for every label there's a single location it should return and single entry point
// if theres already a location that it returns to that's a failure
val disallowed = new mutable.HashMap[Symbol, Tree]()
queue.sizeHint(labelCalls.size + entryPoints.size)

def putLabelDefsNearCallees = new TreeMap() {

override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = {
tree match {
case t: Apply if (entryPoints.contains(t)) =>
entryPoints = entryPoints - t
labelLevel = labelLevel + 1
val r = Block(moveLabels(t), t)
labelLevel = labelLevel - 1
if (labelLevel == 0) beingAppended.clear()
r
case _ => if (entryPoints.nonEmpty && labelDefs.nonEmpty) super.transform(tree) else tree
}
case t: Apply if labelDefs.contains(t.symbol) =>
val labelDef = labelDefs(t.symbol)
labelDefs -= t.symbol

}
}
val labelDef2 = transform(labelDef)
Block(labelDef2:: Nil, t)

def moveLabels(entryPoint: Apply): List[Tree] = {
val entrySym = entryPoint.symbol
if ((entrySym is Flags.Label) && labelDefs.contains(entrySym)) {
val visitedNow = new mutable.HashMap[Symbol, Tree]()
val treesToAppend = new ArrayBuffer[Tree]() // order matters. parents should go first
treesToAppend += labelDefs(entrySym)
queue.clear()

var visited = 0
queue += entryPoint
while (visited < queue.size) {
val owningLabelDefSym = queue(visited).symbol
for (call <- labelCalls(owningLabelDefSym)) {
val callSym = call.symbol
if (!beingAppended.contains(callSym)) {
if (disallowed.contains(callSym)) {
val oldCall = disallowed(callSym)
ctx.error(s"Multiple return locations for Label $oldCall and $call", callSym.pos)
} else {
if ((!visitedNow.contains(callSym)) && labelDefs.contains(callSym)) {
val defTree = labelDefs(callSym)
visitedNow.put(callSym, defTree)
val callCount = callCounts(callSym)
if (callCount > 1) {
if (!treesToAppend.contains(defTree)) {
treesToAppend += defTree
queue += call

}
} else if (entryPoint.symbol ne callSym) entryPoints += call
}
}
}
}

visited += 1
case _ => if (labelDefs.nonEmpty) super.transform(tree) else tree
}
beingAppended ++= treesToAppend.map(_.symbol)
treesToAppend.toList.map(putLabelDefsNearCallees.transform)
} else Nil
}
}


val res = cpy.DefDef(tree)(rhs = putLabelDefsNearCallees.transform(newRhs))

res
Expand All @@ -168,22 +119,11 @@ class LabelDefs extends MiniPhaseTransform {

object collectLabelDefs extends TreeMap() {

// label calls from this DefDef
var parentLabelCalls: mutable.Set[Tree] = new mutable.HashSet[Tree]()
var callCounts: mutable.Map[Symbol, Int] = new mutable.HashMap[Symbol, Int]().withDefaultValue(0)

def shouldMoveLabel = true

// labelSymbol -> Defining tree
val labelDefs = new mutable.HashMap[Symbol, Tree]()
// owner -> all calls by this owner
val labelCalls = new mutable.HashMap[Symbol, mutable.Set[Tree]]()
var owner: Symbol = null

def clear = {
parentLabelCalls.clear()
labelDefs.clear()
labelCalls.clear()
}

override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
Expand All @@ -196,30 +136,14 @@ class LabelDefs extends MiniPhaseTransform {
}
case t: DefDef =>
assert(t.symbol is Flags.Label)

val st = parentLabelCalls
parentLabelCalls = new mutable.HashSet[Tree]()
val symt = owner
owner = t.symbol

val r = super.transform(tree)

owner = symt
labelCalls(r.symbol) = parentLabelCalls
parentLabelCalls = st

if (shouldMoveLabel) {
labelDefs(r.symbol) = r
EmptyTree
} else r
labelDefs(r.symbol) = r
EmptyTree
case t: Apply if t.symbol is Flags.Label =>
val sym = t.symbol
parentLabelCalls = parentLabelCalls + t
if (owner != sym) callCounts(sym) = callCounts(sym) + 1
super.transform(tree)
case _ =>
super.transform(tree)

}
}
}
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import core.DenotTransformers.DenotTransformer
import core.Denotations.SingleDenotation

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

/** The central class of the dotc compiler. The job of a compiler is to create
* runs, which process given `phases` in a given `rootContext`.
Expand Down Expand Up @@ -75,6 +76,7 @@ class Compiler {
new ElimByName, // Expand by-name parameter references
new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings
new ResolveSuper, // Implement super accessors and add forwarders to trait methods
new Simplify, // Perform local optimizations, simplified versions of what linker does.
new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives
new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method
new ArrayConstructors), // Intercept creation of (non-generic) arrays and intrinsify.
Expand All @@ -88,9 +90,10 @@ class Compiler {
new NonLocalReturns, // Expand non-local returns
new CapturedVars, // Represent vars captured by closures as heap objects
new Constructors, // Collect initialization code in primary constructors
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions.
new GetClass), // Rewrites getClass calls on primitive types.
new GetClass, // Rewrites getClass calls on primitive types.
new Simplify), // Perform local optimizations, simplified versions of what linker does.
List(new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments
// Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
new ElimStaticThis, // Replace `this` references to static objects by global identifiers
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1190,7 +1190,9 @@ object Trees {
}

abstract class TreeAccumulator[X] {
// Ties the knot of the traversal: call `foldOver(x, tree))` to dive in the `tree` node.
def apply(x: X, tree: Tree)(implicit ctx: Context): X

def apply(x: X, trees: Traversable[Tree])(implicit ctx: Context): X = (x /: trees)(apply)
def foldOver(x: X, tree: Tree)(implicit ctx: Context): X = {
def localCtx =
Expand Down Expand Up @@ -1301,7 +1303,7 @@ object Trees {
class ShallowFolder[X](f: (X, Tree) => X) extends TreeAccumulator[X] {
def apply(x: X, tree: Tree)(implicit ctx: Context): X = {
val x1 = f(x, tree)
if (x1.asInstanceOf[AnyRef] ne x1.asInstanceOf[AnyRef]) x1
if (x1.asInstanceOf[AnyRef] ne x.asInstanceOf[AnyRef]) x1
else foldOver(x1, tree)
}
}
Expand Down
59 changes: 47 additions & 12 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
override val cpy: TypedTreeCopier = // Type ascription needed to pick up any new members in TreeCopier (currently there are none)
new TypedTreeCopier

val cpyBetweenPhases = new TimeTravellingTreeCopier

class TypedTreeCopier extends TreeCopier {
def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[Type] =
copied.withTypeUnchecked(tree.tpe)
Expand All @@ -472,16 +474,25 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
}
}

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

override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply =
ta.assignType(untpd.cpy.TypeApply(tree)(fun, args), fun, args)
// Same remark as for Apply
override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = {
val tree1 = untpd.cpy.TypeApply(tree)(fun, args)
tree match {
case tree: TypeApply
if (fun.tpe eq tree.fun.tpe) && sameTypes(args, tree.args) =>
tree1.withTypeUnchecked(tree.tpe)
case _ => ta.assignType(tree1, fun, args)
}
}

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

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

override def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = {
val tree1 = untpd.cpy.Closure(tree)(env, meth, tpt)
tree match {
case tree: Closure if sameTypes(env, tree.env) && (meth.tpe eq tree.meth.tpe) && (tpt.tpe eq tree.tpt.tpe) =>
tree1.withTypeUnchecked(tree.tpe)
case _ => ta.assignType(tree1, meth, tpt)
}
}
override def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = {
val tree1 = untpd.cpy.Match(tree)(selector, cases)
tree match {
Expand Down Expand Up @@ -574,6 +590,25 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
Try(tree: Tree)(expr, cases, finalizer)
}

class TimeTravellingTreeCopier extends TypedTreeCopier {
override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply =
ta.assignType(untpd.cpy.Apply(tree)(fun, args), fun, args)
// Note: Reassigning the original type if `fun` and `args` have the same types as before
// does not work here: The computed type depends on the widened function type, not
// the function type itself. A treetransform may keep the function type the
// same but its widened type might change.

override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply =
ta.assignType(untpd.cpy.TypeApply(tree)(fun, args), fun, args)
// Same remark as for Apply

override def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure =
ta.assignType(untpd.cpy.Closure(tree)(env, meth, tpt), meth, tpt)

override def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt)(implicit ctx: Context): Closure =
Closure(tree: Tree)(env, meth, tpt)
}

override def skipTransform(tree: Tree)(implicit ctx: Context) = tree.tpe.isError

implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal {
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/config/CompilerCommand.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package dotty.tools.dotc
package config

Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/Printers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ object Printers {
val pickling: Printer = noPrinter
val inlining: Printer = noPrinter
val exhaustivity: Printer = noPrinter
val simplify: Printer = noPrinter
}
26 changes: 26 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,16 @@ class Definitions {
def Boolean_&& = Boolean_andR.symbol
lazy val Boolean_orR = BooleanClass.requiredMethodRef(nme.ZOR)
def Boolean_|| = Boolean_orR.symbol
lazy val Boolean_eqeqR = BooleanClass.info.member(nme.EQ).suchThat(_.info.firstParamTypes match {
case List(pt) => (pt isRef BooleanClass)
case _ => false
})
def Boolean_== = Boolean_eqeqR.symbol
lazy val Boolean_neqeqR = BooleanClass.info.member(nme.NE).suchThat(_.info.firstParamTypes match {
case List(pt) => (pt isRef BooleanClass)
case _ => false
})
def Boolean_!= = Boolean_neqeqR.symbol

lazy val ByteType: TypeRef = valueTypeRef("scala.Byte", BoxedByteType, java.lang.Byte.TYPE, ByteEnc)
def ByteClass(implicit ctx: Context) = ByteType.symbol.asClass
Expand Down Expand Up @@ -431,6 +441,13 @@ class Definitions {
lazy val Long_LSR_Int = LongType.member(nme.LSR).requiredSymbol(
x => (x is Method) && (x.info.firstParamTypes.head isRef defn.IntClass)
)
lazy val Long_plusR = LongClass.requiredMethodRef(nme.PLUS, List(LongType))
def Long_+ = Long_plusR.symbol
lazy val Long_mulR = LongClass.requiredMethodRef(nme.MUL, List(LongType))
def Long_* = Long_mulR.symbol
lazy val Long_divR = LongClass.requiredMethodRef(nme.DIV, List(LongType))
def Long_/ = Long_divR.symbol

lazy val FloatType: TypeRef = valueTypeRef("scala.Float", BoxedFloatType, java.lang.Float.TYPE, FloatEnc)
def FloatClass(implicit ctx: Context) = FloatType.symbol.asClass
lazy val DoubleType: TypeRef = valueTypeRef("scala.Double", BoxedDoubleType, java.lang.Double.TYPE, DoubleEnc)
Expand Down Expand Up @@ -491,6 +508,11 @@ class Definitions {
lazy val BoxedNumberClass = ctx.requiredClass("java.lang.Number")
lazy val ThrowableClass = ctx.requiredClass("java.lang.Throwable")
lazy val ClassCastExceptionClass = ctx.requiredClass("java.lang.ClassCastException")
lazy val ArithmeticExceptionClass = ctx.requiredClass("java.lang.ArithmeticException")
lazy val ArithmeticExceptionClass_stringConstructor = ArithmeticExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match {
case List(pt) => (pt isRef StringClass)
case _ => false
}).symbol.asTerm
lazy val JavaSerializableClass = ctx.requiredClass("java.io.Serializable")
lazy val ComparableClass = ctx.requiredClass("java.lang.Comparable")

Expand Down Expand Up @@ -522,6 +544,10 @@ class Definitions {
def DynamicClass(implicit ctx: Context) = DynamicType.symbol.asClass
lazy val OptionType: TypeRef = ctx.requiredClassRef("scala.Option")
def OptionClass(implicit ctx: Context) = OptionType.symbol.asClass
lazy val SomeType: TypeRef = ctx.requiredClassRef("scala.Some")
def SomeClass(implicit ctx: Context) = SomeType.symbol.asClass
lazy val NoneModuleRef: TermRef = ctx.requiredModuleRef("scala.None")
def NoneClass(implicit ctx: Context) = NoneModuleRef.symbol.moduleClass.asClass
lazy val EnumType: TypeRef = ctx.requiredClassRef("scala.Enum")
def EnumClass(implicit ctx: Context) = EnumType.symbol.asClass
lazy val EnumValuesType: TypeRef = ctx.requiredClassRef("scala.runtime.EnumValues")
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/core/NameKinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ object NameKinds {
val PatMatCaseName = new UniqueNameKind("case")
val PatMatMatchFailName = new UniqueNameKind("matchFail")
val PatMatSelectorName = new UniqueNameKind("selector")
val LocalOptFact = new UniqueNameKind("fact")
val LocalOptSelector = new UniqueNameKind("selector")
val LocalOptFallback = new UniqueNameKind("fallback")

/** The kind of names of default argument getters */
val DefaultGetterName = new NumberedNameKind(DEFAULTGETTER, "DefaultGetter") {
Expand Down Expand Up @@ -384,4 +387,4 @@ object NameKinds {
def qualifiedNameKindOfTag : collection.Map[Int, QualifiedNameKind] = qualifiedNameKinds
def numberedNameKindOfTag : collection.Map[Int, NumberedNameKind] = numberedNameKinds
def uniqueNameKindOfSeparator: collection.Map[String, UniqueNameKind] = uniqueNameKinds
}
}
8 changes: 7 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Scopes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,10 @@ object Scopes {
/** Is the scope empty? */
def isEmpty: Boolean = lastEntry eq null

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

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

/** Tests whether a predicate holds for at least one Symbol of this Scope. */
def exists(p: Symbol => Boolean)(implicit ctx: Context): Boolean = filter(p).isEmpty

/** Finds the first Symbol of this Scope satisfying a predicate, if any. */
def find(p: Symbol => Boolean)(implicit ctx: Context): Symbol = filter(p) match {
case sym :: _ => sym
case _ => NoSymbol
Expand Down
Loading

0 comments on commit b583461

Please sign in to comment.