@@ -6,6 +6,7 @@ import dotty.tools.dotc.core.Contexts.Context
6
6
import dotty .tools .dotc .core .Phases .Phase
7
7
import dotty .tools .dotc .core .Symbols .Symbol
8
8
import dotty .tools .dotc .core .Flags .PackageVal
9
+ import dotty .tools .dotc .typer .Mode
9
10
import dotty .tools .dotc .ast .Trees ._
10
11
import dotty .tools .dotc .core .Decorators ._
11
12
import scala .annotation .tailrec
@@ -15,47 +16,48 @@ object TreeTransforms {
15
16
import tpd ._
16
17
17
18
/** 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
+ */
52
53
abstract class TreeTransform extends Phase {
53
54
54
55
/** id of this treeTransform in group */
55
56
var idx : Int = _
56
57
57
58
/** 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
+ */
59
61
def runsAfterGroupsOf : Set [String ] = Set .empty
60
62
61
63
def prepareForIdent (tree : Ident )(implicit ctx : Context ) = this
@@ -121,6 +123,7 @@ object TreeTransforms {
121
123
def transformTemplate (tree : Template )(implicit ctx : Context , info : TransformerInfo ): Tree = tree
122
124
def transformPackageDef (tree : PackageDef )(implicit ctx : Context , info : TransformerInfo ): Tree = tree
123
125
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
124
127
125
128
/** Transform tree using all transforms of current group (including this one) */
126
129
def transform (tree : Tree )(implicit ctx : Context , info : TransformerInfo ): Tree = info.group.transform(tree, info, 0 )
@@ -132,7 +135,7 @@ object TreeTransforms {
132
135
def transformFollowing (tree : Tree )(implicit ctx : Context , info : TransformerInfo ): Tree = info.group.transformSingle(tree, idx + 1 )
133
136
134
137
/** perform context-dependant initialization */
135
- def init (implicit ctx: Context , info : TransformerInfo ): Unit = {}
138
+ def init (implicit ctx : Context , info : TransformerInfo ): Unit = {}
136
139
137
140
protected def mkTreeTransformer = new TreeTransformer {
138
141
override def name : String = TreeTransform .this .name
@@ -156,12 +159,11 @@ object TreeTransforms {
156
159
157
160
type Mutator [T ] = (TreeTransform , T , Context ) => TreeTransform
158
161
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 )
160
163
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
165
167
*/
166
168
class NXTransformations {
167
169
@@ -170,11 +172,11 @@ object TreeTransforms {
170
172
else hasRedefinedMethod(cls.getSuperclass, name)
171
173
172
174
/** 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
+ */
178
180
private def index (transformations : Array [TreeTransform ], name : String ): Array [Int ] = {
179
181
val len = transformations.length
180
182
val next = new Array [Int ](len + 1 )
@@ -281,6 +283,7 @@ object TreeTransforms {
281
283
nxTransTemplate = index(transformations, " transformTemplate" )
282
284
nxTransPackageDef = index(transformations, " transformPackageDef" )
283
285
nxTransStats = index(transformations, " transformStats" )
286
+ nxTransOther = index(transformations, " transformOther" )
284
287
}
285
288
286
289
def this (prev : NXTransformations , changedTansformation : TreeTransform , transformationIndex : Int , reuse : Boolean = false ) = {
@@ -349,12 +352,12 @@ object TreeTransforms {
349
352
nxTransTemplate = indexUpdate(prev.nxTransTemplate, changedTansformation, transformationIndex, " transformTemplate" , copy)
350
353
nxTransPackageDef = indexUpdate(prev.nxTransPackageDef, changedTansformation, transformationIndex, " transformPackageDef" , copy)
351
354
nxTransStats = indexUpdate(prev.nxTransStats, changedTansformation, transformationIndex, " transformStats" , copy)
355
+ nxTransOther = indexUpdate(prev.nxTransOther, changedTansformation, transformationIndex, " transformOther" , copy)
352
356
}
353
357
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
358
361
*/
359
362
var nxPrepIdent : Array [Int ] = _
360
363
var nxPrepSelect : Array [Int ] = _
@@ -419,6 +422,7 @@ object TreeTransforms {
419
422
var nxTransTemplate : Array [Int ] = _
420
423
var nxTransPackageDef : Array [Int ] = _
421
424
var nxTransStats : Array [Int ] = _
425
+ var nxTransOther : Array [Int ] = _
422
426
}
423
427
424
428
/** A group of tree transforms that are applied in sequence during the same phase */
@@ -490,12 +494,12 @@ object TreeTransforms {
490
494
val prepForTypeDef : Mutator [TypeDef ] = (trans, tree, ctx) => trans.prepareForTypeDef(tree)(ctx)
491
495
val prepForTemplate : Mutator [Template ] = (trans, tree, ctx) => trans.prepareForTemplate(tree)(ctx)
492
496
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)
494
498
495
499
def transform (t : Tree )(implicit ctx : Context ): Tree = {
496
500
val initialTransformations = transformations
497
501
val info = new TransformerInfo (initialTransformations, new NXTransformations (initialTransformations), this )
498
- initialTransformations.zipWithIndex.foreach{
502
+ initialTransformations.zipWithIndex.foreach {
499
503
case (transform, id) =>
500
504
transform.idx = id
501
505
transform.init(ctx, info)
@@ -833,6 +837,14 @@ object TreeTransforms {
833
837
} else tree
834
838
}
835
839
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
+
836
848
final private [TreeTransforms ] def goNamed (tree : NameTree , cur : Int )(implicit ctx : Context , info : TransformerInfo ): Tree =
837
849
tree match {
838
850
case tree : Ident => goIdent(tree, info.nx.nxTransIdent(cur))
@@ -872,7 +884,7 @@ object TreeTransforms {
872
884
case tree : Template => goTemplate(tree, info.nx.nxTransTemplate(cur))
873
885
case tree : PackageDef => goPackageDef(tree, info.nx.nxTransPackageDef(cur))
874
886
case Thicket (trees) => cpy.Thicket (tree, transformTrees(trees, info, cur))
875
- case tree => tree
887
+ case tree => goOther( tree, info.nx.nxTransOther(cur))
876
888
}
877
889
878
890
final private [TreeTransforms ] def transformSingle (tree : Tree , cur : Int )(implicit ctx : Context , info : TransformerInfo ): Tree =
@@ -1052,7 +1064,7 @@ object TreeTransforms {
1052
1064
implicit val mutatedInfo : TransformerInfo = mutateTransformers(info, prepForCaseDef, info.nx.nxPrepCaseDef, tree, cur)
1053
1065
if (mutatedInfo eq null ) tree
1054
1066
else {
1055
- val pat = transform(tree.pat, mutatedInfo, cur)
1067
+ val pat = transform(tree.pat, mutatedInfo, cur)(ctx.withMode( Mode . Pattern ))
1056
1068
val guard = transform(tree.guard, mutatedInfo, cur)
1057
1069
val body = transform(tree.body, mutatedInfo, cur)
1058
1070
goCaseDef(cpy.CaseDef (tree, pat, guard, body), mutatedInfo.nx.nxTransCaseDef(cur))
@@ -1130,12 +1142,10 @@ object TreeTransforms {
1130
1142
val stats = transformStats(tree.stats, tree.symbol, mutatedInfo, cur)(nestedCtx)
1131
1143
goPackageDef(cpy.PackageDef (tree, pid, stats), mutatedInfo.nx.nxTransPackageDef(cur))
1132
1144
}
1133
- case tree : Import => EmptyTree
1134
- case tree : NamedArg => transform(tree.arg, info, cur)
1135
1145
case Thicket (trees) => cpy.Thicket (tree, transformTrees(trees, info, cur))
1136
1146
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))
1139
1149
}
1140
1150
1141
1151
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