Skip to content

Commit 9748c9b

Browse files
committed
Changed first phase normalization and improvements to TreeTransform
Two improvements to TreeTransform: 1) Added transformOther functionality which handles trees not handled by other parts 2) Passes down Mode.Pattern in the context when in a pattern. TreeTransform no longer normalizes unknown trees but passes them to transformOther. The former Companions phase has been renamed to FirstTransform. It now performs the following optimizations: - adds companion objects (this was done before) - other normalizations that were formally done in TreeTransform, - rewrite native methods to stubs (this was formally done in RefChecks)
1 parent 85044e4 commit 9748c9b

File tree

4 files changed

+106
-64
lines changed

4 files changed

+106
-64
lines changed

src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ class Compiler {
1919
def phases: List[List[Phase]] =
2020
List(
2121
List(new FrontEnd),
22-
List(new Companions),
22+
List(new FirstTransform),
2323
List(new SuperAccessors),
2424
// pickling and refchecks goes here
25-
List(new ElimRepeated, new ElimLocals),
25+
List(/*new RefChecks,*/ new ElimRepeated, new ElimLocals),
2626
List(new ExtensionMethods),
2727
List(new TailRec),
2828
List(new PatternMatcher,

src/dotty/tools/dotc/transform/Companions.scala renamed to src/dotty/tools/dotc/transform/FirstTransform.scala

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,26 @@ package transform
44
import core._
55
import Names._
66
import TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
7-
import ast.Trees.flatten
7+
import ast.Trees._
88
import Flags._
9+
import Types._
10+
import Constants.Constant
911
import Contexts.Context
1012
import Symbols._
1113
import scala.collection.mutable
1214
import DenotTransformers._
15+
import typer.Checking
1316
import Names.Name
1417
import NameOps._
1518

1619

17-
/** A transformer that creates companion objects for all classes except module classes. */
18-
class Companions extends TreeTransform with IdentityDenotTransformer { thisTransformer =>
20+
/** The first tree transform
21+
* - ensures there are companion objects for all classes except module classes
22+
* - eliminates some kinds of trees: Imports, NamedArgs, all TypTrees other than TypeTree
23+
* - checks the bounds of AppliedTypeTrees
24+
* - stubs out native methods
25+
*/
26+
class FirstTransform extends TreeTransform with IdentityDenotTransformer { thisTransformer =>
1927
import ast.tpd._
2028

2129
override def name = "companions"
@@ -61,6 +69,30 @@ class Companions extends TreeTransform with IdentityDenotTransformer { thisTrans
6169
addMissingCompanions(reorder(stats))
6270
}
6371

72+
override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo) =
73+
if (ddef.symbol.hasAnnotation(defn.NativeAnnot)) {
74+
ddef.symbol.resetFlag(Deferred)
75+
DefDef(ddef.symbol.asTerm,
76+
_ => ref(defn.Sys_error).withPos(ddef.pos)
77+
.appliedTo(Literal(Constant("native method stub"))))
78+
} else ddef
79+
6480
override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] =
6581
ast.Trees.flatten(reorderAndComplete(trees)(ctx.withPhase(thisTransformer.next)))
82+
83+
override def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match {
84+
case tree: Import => EmptyTree
85+
case tree: NamedArg => tree.arg
86+
/* not yet enabled
87+
case AppliedTypeTree(tycon, args) =>
88+
89+
val tparams = tycon.tpe.typeSymbol.typeParams
90+
Checking.checkBounds(
91+
args, tparams.map(_.info.bounds), (tp, argTypes) => tp.substDealias(tparams, argTypes))
92+
TypeTree(tree.tpe).withPos(tree.pos)
93+
*/
94+
case tree =>
95+
if (tree.isType) TypeTree(tree.tpe, tree).withPos(tree.pos)
96+
else tree
97+
}
6698
}

src/dotty/tools/dotc/transform/SuperAccessors.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this
220220

221221
try tree match {
222222
// Don't transform patterns or strange trees will reach the matcher (ticket #4062)
223-
// TODO Drop once this runs after pattern matcher
223+
// TODO Query `ctx.mode is Pattern` instead.
224224
case CaseDef(pat, guard, body) =>
225225
cpy.CaseDef(tree, pat, transform(guard), transform(body))
226226

src/dotty/tools/dotc/transform/TreeTransform.scala

Lines changed: 68 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import dotty.tools.dotc.core.Contexts.Context
66
import dotty.tools.dotc.core.Phases.Phase
77
import dotty.tools.dotc.core.Symbols.Symbol
88
import dotty.tools.dotc.core.Flags.PackageVal
9+
import dotty.tools.dotc.typer.Mode
910
import dotty.tools.dotc.ast.Trees._
1011
import dotty.tools.dotc.core.Decorators._
1112
import scala.annotation.tailrec
@@ -15,47 +16,48 @@ object TreeTransforms {
1516
import tpd._
1617

1718
/** The base class of tree transforms. For each kind of tree K, there are
18-
* two methods which can be overridden:
19-
*
20-
* prepareForK // return a new TreeTransform which gets applied to the K
21-
* // node and its children
22-
* transformK // transform node of type K
23-
*
24-
* If a transform does not need to visit a node or any of its children, it
25-
* signals this fact by returning a NoTransform from a prepare method.
26-
*
27-
* If all transforms in a group are NoTransforms, the tree is no longer traversed.
28-
*
29-
*
30-
* Performance analysis: Taking the dotty compiler frontend as a use case, we are aiming for a warm performance of
31-
* about 4000 lines / sec. This means 6 seconds for a codebase of 24'000 lines. Of these the frontend consumes
32-
* over 2.5 seconds, erasure and code generation will most likely consume over 1 second each. So we would have
33-
* about 1 sec for all other transformations in our budget. Of this second, let's assume a maximum of 20% for
34-
* the general dispatch overhead as opposed to the concrete work done in transformations. So that leaves us with
35-
* 0.2sec, or roughly 600M processor cycles.
36-
*
37-
* Now, to the amount of work that needs to be done. The codebase produces of about 250'000 trees after typechecking.
38-
* Transformations are likely to make this bigger so let's assume 300K trees on average. We estimate to have about 100
39-
* micro-transformations. Let's say 5 transformation groups of 20 micro-transformations each. (by comparison,
40-
* scalac has in excess of 20 phases, and most phases do multiple transformations). There are then 30M visits
41-
* of a node by a transformation. Each visit has a budget of 20 processor cycles.
42-
*
43-
* A more detailed breakdown: I assume that about one third of all transformations have real work to do for each node.
44-
* This might look high, but keep in mind that the most common nodes are Idents and Selects, and most transformations
45-
* touch these. By contrast the amount of work for generating new transformations should be negligible.
46-
*
47-
* So, in 400 clock cycles we need to (1) perform a pattern match according to the type of node, (2) generate new
48-
* transformations if applicable, (3) reconstitute the tree node from the result of transforming the children, and
49-
* (4) chain 7 out of 20 transformations over the resulting tree node. I believe the current algorithm is suitable
50-
* for achieving this goal, but there can be no wasted cycles anywhere.
51-
*/
19+
* two methods which can be overridden:
20+
*
21+
* prepareForK // return a new TreeTransform which gets applied to the K
22+
* // node and its children
23+
* transformK // transform node of type K
24+
*
25+
* If a transform does not need to visit a node or any of its children, it
26+
* signals this fact by returning a NoTransform from a prepare method.
27+
*
28+
* If all transforms in a group are NoTransforms, the tree is no longer traversed.
29+
*
30+
*
31+
* Performance analysis: Taking the dotty compiler frontend as a use case, we are aiming for a warm performance of
32+
* about 4000 lines / sec. This means 6 seconds for a codebase of 24'000 lines. Of these the frontend consumes
33+
* over 2.5 seconds, erasure and code generation will most likely consume over 1 second each. So we would have
34+
* about 1 sec for all other transformations in our budget. Of this second, let's assume a maximum of 20% for
35+
* the general dispatch overhead as opposed to the concrete work done in transformations. So that leaves us with
36+
* 0.2sec, or roughly 600M processor cycles.
37+
*
38+
* Now, to the amount of work that needs to be done. The codebase produces of about 250'000 trees after typechecking.
39+
* Transformations are likely to make this bigger so let's assume 300K trees on average. We estimate to have about 100
40+
* micro-transformations. Let's say 5 transformation groups of 20 micro-transformations each. (by comparison,
41+
* scalac has in excess of 20 phases, and most phases do multiple transformations). There are then 30M visits
42+
* of a node by a transformation. Each visit has a budget of 20 processor cycles.
43+
*
44+
* A more detailed breakdown: I assume that about one third of all transformations have real work to do for each node.
45+
* This might look high, but keep in mind that the most common nodes are Idents and Selects, and most transformations
46+
* touch these. By contrast the amount of work for generating new transformations should be negligible.
47+
*
48+
* So, in 400 clock cycles we need to (1) perform a pattern match according to the type of node, (2) generate new
49+
* transformations if applicable, (3) reconstitute the tree node from the result of transforming the children, and
50+
* (4) chain 7 out of 20 transformations over the resulting tree node. I believe the current algorithm is suitable
51+
* for achieving this goal, but there can be no wasted cycles anywhere.
52+
*/
5253
abstract class TreeTransform extends Phase {
5354

5455
/** id of this treeTransform in group */
5556
var idx: Int = _
5657

5758
/** List of names of phases that should have finished their processing of all compilation units
58-
* before this phase starts */
59+
* before this phase starts
60+
*/
5961
def runsAfterGroupsOf: Set[String] = Set.empty
6062

6163
def prepareForIdent(tree: Ident)(implicit ctx: Context) = this
@@ -121,6 +123,7 @@ object TreeTransforms {
121123
def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = tree
122124
def transformPackageDef(tree: PackageDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree
123125
def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees
126+
def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = tree
124127

125128
/** Transform tree using all transforms of current group (including this one) */
126129
def transform(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, 0)
@@ -132,7 +135,7 @@ object TreeTransforms {
132135
def transformFollowing(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transformSingle(tree, idx + 1)
133136

134137
/** perform context-dependant initialization */
135-
def init(implicit ctx:Context, info: TransformerInfo): Unit = {}
138+
def init(implicit ctx: Context, info: TransformerInfo): Unit = {}
136139

137140
protected def mkTreeTransformer = new TreeTransformer {
138141
override def name: String = TreeTransform.this.name
@@ -156,12 +159,11 @@ object TreeTransforms {
156159

157160
type Mutator[T] = (TreeTransform, T, Context) => TreeTransform
158161

159-
class TransformerInfo(val transformers: Array[TreeTransform], val nx: NXTransformations, val group:TreeTransformer)
162+
class TransformerInfo(val transformers: Array[TreeTransform], val nx: NXTransformations, val group: TreeTransformer)
160163

161-
/**
162-
* This class maintains track of which methods are redefined in MiniPhases and creates execution plans for transformXXX and prepareXXX
163-
* Thanks to Martin for this idea
164-
* @see NXTransformations.index for format of plan
164+
/** This class maintains track of which methods are redefined in MiniPhases and creates execution plans for transformXXX and prepareXXX
165+
* Thanks to Martin for this idea
166+
* @see NXTransformations.index for format of plan
165167
*/
166168
class NXTransformations {
167169

@@ -170,11 +172,11 @@ object TreeTransforms {
170172
else hasRedefinedMethod(cls.getSuperclass, name)
171173

172174
/** Create an index array `next` of size one larger than teh size of `transforms` such that
173-
* for each index i, `next(i)` is the smallest index j such that
174-
*
175-
* i <= j
176-
* j == transforms.length || transform(j) defines a non-default method with given `name`
177-
*/
175+
* for each index i, `next(i)` is the smallest index j such that
176+
*
177+
* i <= j
178+
* j == transforms.length || transform(j) defines a non-default method with given `name`
179+
*/
178180
private def index(transformations: Array[TreeTransform], name: String): Array[Int] = {
179181
val len = transformations.length
180182
val next = new Array[Int](len + 1)
@@ -281,6 +283,7 @@ object TreeTransforms {
281283
nxTransTemplate = index(transformations, "transformTemplate")
282284
nxTransPackageDef = index(transformations, "transformPackageDef")
283285
nxTransStats = index(transformations, "transformStats")
286+
nxTransOther = index(transformations, "transformOther")
284287
}
285288

286289
def this(prev: NXTransformations, changedTansformation: TreeTransform, transformationIndex: Int, reuse: Boolean = false) = {
@@ -349,12 +352,12 @@ object TreeTransforms {
349352
nxTransTemplate = indexUpdate(prev.nxTransTemplate, changedTansformation, transformationIndex, "transformTemplate", copy)
350353
nxTransPackageDef = indexUpdate(prev.nxTransPackageDef, changedTansformation, transformationIndex, "transformPackageDef", copy)
351354
nxTransStats = indexUpdate(prev.nxTransStats, changedTansformation, transformationIndex, "transformStats", copy)
355+
nxTransOther = indexUpdate(prev.nxTransOther, changedTansformation, transformationIndex, "transformOther", copy)
352356
}
353357

354-
/**
355-
* Those arrays are used as "execution plan" in order to only execute non-tivial transformations\preparations
356-
* for every integer i array(i) contains first non trivial transformation\preparation on particular tree subtype.
357-
* If no nontrivial transformation are left stored value is greater than transformers.size
358+
/** Those arrays are used as "execution plan" in order to only execute non-tivial transformations\preparations
359+
* for every integer i array(i) contains first non trivial transformation\preparation on particular tree subtype.
360+
* If no nontrivial transformation are left stored value is greater than transformers.size
358361
*/
359362
var nxPrepIdent: Array[Int] = _
360363
var nxPrepSelect: Array[Int] = _
@@ -419,6 +422,7 @@ object TreeTransforms {
419422
var nxTransTemplate: Array[Int] = _
420423
var nxTransPackageDef: Array[Int] = _
421424
var nxTransStats: Array[Int] = _
425+
var nxTransOther: Array[Int] = _
422426
}
423427

424428
/** A group of tree transforms that are applied in sequence during the same phase */
@@ -490,12 +494,12 @@ object TreeTransforms {
490494
val prepForTypeDef: Mutator[TypeDef] = (trans, tree, ctx) => trans.prepareForTypeDef(tree)(ctx)
491495
val prepForTemplate: Mutator[Template] = (trans, tree, ctx) => trans.prepareForTemplate(tree)(ctx)
492496
val prepForPackageDef: Mutator[PackageDef] = (trans, tree, ctx) => trans.prepareForPackageDef(tree)(ctx)
493-
val prepForStats: Mutator[List[Tree]]= (trans, trees, ctx) => trans.prepareForStats(trees)(ctx)
497+
val prepForStats: Mutator[List[Tree]] = (trans, trees, ctx) => trans.prepareForStats(trees)(ctx)
494498

495499
def transform(t: Tree)(implicit ctx: Context): Tree = {
496500
val initialTransformations = transformations
497501
val info = new TransformerInfo(initialTransformations, new NXTransformations(initialTransformations), this)
498-
initialTransformations.zipWithIndex.foreach{
502+
initialTransformations.zipWithIndex.foreach {
499503
case (transform, id) =>
500504
transform.idx = id
501505
transform.init(ctx, info)
@@ -833,6 +837,14 @@ object TreeTransforms {
833837
} else tree
834838
}
835839

840+
final private[TreeTransforms] def goOther(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
841+
if (cur < info.transformers.length) {
842+
val trans = info.transformers(cur)
843+
val t = trans.transformOther(tree)(ctx.withPhase(trans), info)
844+
transformSingle(t, cur + 1)
845+
} else tree
846+
}
847+
836848
final private[TreeTransforms] def goNamed(tree: NameTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree =
837849
tree match {
838850
case tree: Ident => goIdent(tree, info.nx.nxTransIdent(cur))
@@ -872,7 +884,7 @@ object TreeTransforms {
872884
case tree: Template => goTemplate(tree, info.nx.nxTransTemplate(cur))
873885
case tree: PackageDef => goPackageDef(tree, info.nx.nxTransPackageDef(cur))
874886
case Thicket(trees) => cpy.Thicket(tree, transformTrees(trees, info, cur))
875-
case tree => tree
887+
case tree => goOther(tree, info.nx.nxTransOther(cur))
876888
}
877889

878890
final private[TreeTransforms] def transformSingle(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree =
@@ -1052,7 +1064,7 @@ object TreeTransforms {
10521064
implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForCaseDef, info.nx.nxPrepCaseDef, tree, cur)
10531065
if (mutatedInfo eq null) tree
10541066
else {
1055-
val pat = transform(tree.pat, mutatedInfo, cur)
1067+
val pat = transform(tree.pat, mutatedInfo, cur)(ctx.withMode(Mode.Pattern))
10561068
val guard = transform(tree.guard, mutatedInfo, cur)
10571069
val body = transform(tree.body, mutatedInfo, cur)
10581070
goCaseDef(cpy.CaseDef(tree, pat, guard, body), mutatedInfo.nx.nxTransCaseDef(cur))
@@ -1130,12 +1142,10 @@ object TreeTransforms {
11301142
val stats = transformStats(tree.stats, tree.symbol, mutatedInfo, cur)(nestedCtx)
11311143
goPackageDef(cpy.PackageDef(tree, pid, stats), mutatedInfo.nx.nxTransPackageDef(cur))
11321144
}
1133-
case tree: Import => EmptyTree
1134-
case tree: NamedArg => transform(tree.arg, info, cur)
11351145
case Thicket(trees) => cpy.Thicket(tree, transformTrees(trees, info, cur))
11361146
case tree =>
1137-
if (tree.isType) transform(TypeTree(tree.tpe).withPos(tree.pos), info, cur)
1138-
else tree
1147+
implicit val originalInfo: TransformerInfo = info
1148+
goOther(tree, info.nx.nxTransOther(cur))
11391149
}
11401150

11411151
def transform(tree: Tree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree = ctx.traceIndented(s"transforming ${tree.show} at ${ctx.phase}", transforms, show = true) {

0 commit comments

Comments
 (0)