@@ -6,6 +6,7 @@ import dotty.tools.dotc.core.Contexts.Context
66import dotty .tools .dotc .core .Phases .Phase
77import dotty .tools .dotc .core .Symbols .Symbol
88import dotty .tools .dotc .core .Flags .PackageVal
9+ import dotty .tools .dotc .typer .Mode
910import dotty .tools .dotc .ast .Trees ._
1011import dotty .tools .dotc .core .Decorators ._
1112import 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