Skip to content

Commit f5ed670

Browse files
committed
small cleanups, one more test
1 parent 0a52055 commit f5ed670

File tree

2 files changed

+43
-24
lines changed

2 files changed

+43
-24
lines changed

src/compiler/scala/tools/nsc/PhaseAssembly.scala

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ trait PhaseAssembly {
2929
*/
3030
def computePhaseAssembly(): List[SubComponent] = {
3131
require(phasesSet.exists(phase => phase.initial || phase.phaseName == DependencyGraph.Parser), "Missing initial phase")
32-
if (!phasesSet.exists(phase => phase.terminal || phase.phaseName == DependencyGraph.Terminal))
33-
if (phasesSet.add(terminal))
34-
reporter.warning(NoPosition, "Added default terminal phase")
32+
if (!phasesSet.exists(phase => phase.terminal || phase.phaseName == DependencyGraph.Terminal)) {
33+
phasesSet.add(terminal)
34+
reporter.warning(NoPosition, "Added default terminal phase")
35+
}
3536
val graph = DependencyGraph(phasesSet)
3637
for (n <- settings.genPhaseGraph.valueSetByUser; d <- settings.outputDirs.getSingleOutput if !d.isVirtual)
3738
DependencyGraph.graphToDotFile(graph, Path(d.file) / File(s"$n.dot"))
@@ -60,8 +61,8 @@ class DependencyGraph(order: Int, start: String, val components: Map[String, Sub
6061

6162
// phase names and their vertex index
6263
private val nodeCount = new AtomicInteger
63-
private val nodes = mutable.HashMap.empty[String, Int]
64-
private val names = Array.ofDim[String](order)
64+
private val nodes = mutable.HashMap.empty[String, Int] // name to index
65+
private val names = Array.ofDim[String](order) // index to name
6566

6667
/** Add the edge between named phases, where `to` follows `from`.
6768
*/
@@ -74,11 +75,11 @@ class DependencyGraph(order: Int, start: String, val components: Map[String, Sub
7475
}
7576
val v = getNode(from)
7677
val w = getNode(to)
77-
adjacency(v).find(e => e.from == v && e.to == w) match {
78+
adjacency(v).find(_.to == w) match {
7879
case None =>
7980
adjacency(v) ::= Edge(from = v, to = w, weight)
80-
case Some(e) if weight == FollowsNow => // retain runsRightAfter if there is a competing constraint
81-
adjacency(v) = Edge(from = v, to = w, weight) :: adjacency(v).filterNot(e => e.from == v && e.to == w)
81+
case Some(_) if weight == FollowsNow => // retain runsRightAfter if there is a competing constraint
82+
adjacency(v) = Edge(from = v, to = w, weight) :: adjacency(v).filterNot(_.to == w)
8283
case _ =>
8384
}
8485
}
@@ -157,9 +158,11 @@ class DependencyGraph(order: Int, start: String, val components: Map[String, Sub
157158
//if (debugging) println(s"deq ${names(v)}")
158159
for (e <- adjacency(v)) {
159160
val w = e.to
161+
/* cannot happen as `runsRightAfter: Option[String]` is the only way to introduce a `FollowsNow`
160162
val e2 = edgeTo(w)
161163
if (e.weight == FollowsNow && e2 != null && e2.weight == FollowsNow && e.from != e2.from)
162164
throw new FatalError(s"${names(w)} cannot follow right after both ${names(e.from)} and ${names(e2.from)}")
165+
*/
163166
if (distance(w) < distance(v) + e.weight) {
164167
distance(w) = distance(v) + e.weight
165168
edgeTo(w) = e
@@ -172,36 +175,38 @@ class DependencyGraph(order: Int, start: String, val components: Map[String, Sub
172175
}
173176
/** Put the vertices in a linear order.
174177
*
178+
* `Follows` edges increase the level, `FollowsNow` don't.
175179
* Partition by "level" or distance from start.
176-
* Partition the level into "anchors" that follow a node in the previous level, and "followers".
177-
* Starting with the "ends", which are followers without a follower in the level,
178-
* construct paths back to the anchors. The anchors are sorted by name only.
180+
* Partition the level into "anchors" that follow a node in the previous level, and "followers" (nodes
181+
* with a `FollowsNow` edge).
182+
* Starting at the "ends", build the chains of `FollowsNow` nodes within the level. Each chain leads to an anchor.
183+
* The anchors are sorted by name, then the chains are flattened.
179184
*/
180185
def traverse(): List[SubComponent] = {
181186
def componentOf(i: Int) = components(names(i))
182187
def sortComponents(c: SubComponent, d: SubComponent): Boolean =
188+
// sort by name only, like the old implementation (pre scala/scala#10687)
183189
/*c.internal && !d.internal ||*/ c.phaseName.compareTo(d.phaseName) < 0
184190
def sortVertex(i: Int, j: Int): Boolean = sortComponents(componentOf(i), componentOf(j))
185191

186192
distance.zipWithIndex.groupBy(_._1).toList.sortBy(_._1)
187193
.flatMap { case (d, dis) =>
188194
val vs = dis.map { case (_, i) => i }
189-
val (anchors, followers) = vs.partition(v => edgeTo(v) == null || distance(edgeTo(v).from) != d)
195+
val (anchors, followers) = vs.partition(v => edgeTo(v) == null || edgeTo(v).weight != FollowsNow)
190196
//if (debugging) println(s"d=$d, anchors=${anchors.toList.map(n => names(n))}, followers=${followers.toList.map(n => names(n))}")
191197
if (followers.isEmpty)
192198
anchors.toList.map(componentOf).sortWith(sortComponents)
193199
else {
194-
// find phases which are not the source of an edgeTo, then construct paths at this level distance
195200
val froms = followers.map(v => edgeTo(v).from).toSet
196201
val ends = followers.iterator.filterNot(froms).toList
197-
val followed: Array[ArrayDeque[Int]] = anchors.map(ArrayDeque(_))
202+
val chains: Array[ArrayDeque[Int]] = anchors.map(ArrayDeque(_))
198203
def drill(v: Int, path: List[Int]): Unit =
199204
edgeTo(v) match {
200-
case e if e != null && distance(e.from) == d => drill(e.from, v :: path)
201-
case _ => followed.find(_.apply(0) == v).foreach(deque => path.foreach(deque.append))
205+
case e if e != null && e.weight == FollowsNow => drill(e.from, v :: path)
206+
case _ => chains.find(_.apply(0) == v).foreach(deque => path.foreach(deque.append))
202207
}
203208
ends.foreach(drill(_, Nil))
204-
followed.sortWith((p, q) => sortVertex(p(0), q(0))).toList.flatten.map(componentOf)
209+
chains.sortWith((p, q) => sortVertex(p(0), q(0))).toList.flatten.map(componentOf)
205210
}
206211
}
207212
}
@@ -237,18 +242,18 @@ object DependencyGraph {
237242
for (p <- phases) {
238243
require(p.phaseName.nonEmpty, "Phase name must be non-empty.")
239244
def checkConstraint(name: String, constraint: String): Boolean =
240-
phases.exists(_.phaseName == name).tap(ok => if (!ok) graph.warning(s"No phase `$name` for ${p.phaseName}.$constraint"))
245+
graph.components.contains(name).tap(ok => if (!ok) graph.warning(s"No phase `$name` for ${p.phaseName}.$constraint"))
241246
for (after <- p.runsRightAfter if after.nonEmpty && checkConstraint(after, "runsRightAfter"))
242247
graph.addEdge(after, p.phaseName, FollowsNow)
243248
for (after <- p.runsAfter if after.nonEmpty && !p.runsRightAfter.contains(after) && checkConstraint(after, "runsAfter"))
244249
graph.addEdge(after, p.phaseName, Follows)
245250
for (before <- p.runsBefore if before.nonEmpty && checkConstraint(before, "runsBefore"))
246251
graph.addEdge(p.phaseName, before, Follows)
247252
if (p != start && p != end)
248-
if (p.runsRightAfter.find(!_.isEmpty).isEmpty && p.runsAfter.find(!_.isEmpty).isEmpty)
253+
if (p.runsRightAfter.forall(_.isEmpty) && p.runsAfter.forall(_.isEmpty))
249254
graph.addEdge(start.phaseName, p.phaseName, Follows)
250255
if (p != end || p == end && p == start)
251-
if (!p.runsBefore.contains(end.phaseName))
256+
if (p.runsBefore.forall(_.isEmpty))
252257
graph.addEdge(p.phaseName, end.phaseName, Follows)
253258
}
254259
}

test/junit/scala/tools/nsc/PhaseAssemblyTest.scala

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class PhaseAssemblyTest {
2424
phaseName: String,
2525
override val runsRightAfter: Option[String],
2626
override val runsAfter: List[String],
27-
override val runsBefore: List[String],
27+
override val runsBefore: List[String] = List("terminal"),
2828
) extends SubComponent {
2929
override val initial: Boolean = phaseName == "parser"
3030
override val terminal: Boolean = phaseName == "terminal"
@@ -42,9 +42,7 @@ class PhaseAssemblyTest {
4242
val settings = new Settings
4343
//settings.verbose.tryToSet(Nil)
4444
val global = new Global(settings)
45-
val N = 16 * 4096 // 65536 ~ 11-21 secs, 256 ~ 1-2 secs
46-
//val N = 256
47-
//val N = 16
45+
val N = 64 * 4096 // 262'144 ~ 1.6sec
4846
val random = new scala.util.Random(123502L)
4947
val names = Array.tabulate(N)(n => s"phase_${n+1}_${random.nextInt(1024)}")
5048
val beforeTerminal = List("terminal")
@@ -119,6 +117,22 @@ class PhaseAssemblyTest {
119117
val result: List[SubComponent] = graph.compilerPhaseList()
120118
assertEquals(List("parser", "phooey", "kerfuffle", "konflikt", "erasure", "posterasure", "terminal"), result.map(_.phaseName))
121119
}
120+
121+
@Test def `strict chains`: Unit = {
122+
val settings = new Settings
123+
val global = new Global(settings)
124+
val components =
125+
component(global, "p1", None, List("parser")) ::
126+
component(global, "p2", Some("p1"), Nil) ::
127+
component(global, "p3", Some("p2"), Nil) ::
128+
component(global, "u1", None, List("parser")) ::
129+
component(global, "u2", Some("u1"), Nil) ::
130+
component(global, "u3", Some("u2"), Nil) ::
131+
parserAndTerminal(global)
132+
val graph = DependencyGraph(components)
133+
val result: List[SubComponent] = graph.compilerPhaseList()
134+
assertEquals(List("parser", "p1", "p2", "p3", "u1", "u2", "u3", "terminal"), result.map(_.phaseName))
135+
}
122136
//phaseList: List(parser, namer, packageobjects, typer, superaccessors, extmethods,
123137
//pickler, xsbt-api, xsbt-dependency, refchecks, patmat, uncurry, fields, tailcalls,
124138
//specialize, explicitouter, erasure, posterasure, lambdalift, constructors, flatten,

0 commit comments

Comments
 (0)