Skip to content

Commit 3797ff9

Browse files
authored
Merge pull request scala#8397 from retronym/backport/phase-order-regression
[backport] Backport phase assembly regression fix
2 parents f93c5c3 + f85b387 commit 3797ff9

File tree

4 files changed

+38
-116
lines changed

4 files changed

+38
-116
lines changed

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

Lines changed: 35 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,14 @@ trait PhaseAssembly {
4040
var phaseobj: Option[List[SubComponent]] = None
4141
val after = new mutable.HashSet[Edge]()
4242
var before = new mutable.HashSet[Edge]()
43-
var visited: VisitStatus = NotVisited
43+
var visited = false
4444
var level = 0
4545

4646
def allPhaseNames(): String = phaseobj match {
4747
case None => phasename
4848
case Some(lst) => lst.map(_.phaseName).reduceLeft(_+","+_)
4949
}
5050
}
51-
sealed abstract class VisitStatus
52-
case object NotVisited extends VisitStatus
53-
case object Visiting extends VisitStatus
54-
case object Visited extends VisitStatus
5551

5652
val nodes = new mutable.HashMap[String,Node]()
5753
val edges = new mutable.HashSet[Edge]()
@@ -108,69 +104,45 @@ trait PhaseAssembly {
108104
/* Test if there are cycles in the graph, assign levels to the nodes
109105
* and collapse hard links into nodes
110106
*/
111-
def collapseHardLinks() {
112-
for (node <- nodes.valuesIterator.toList) {
113-
val hardBefores = node.before.iterator.filter(_.hard).toList
114-
for (hl <- hardBefores) {
115-
val effectiveNode: Node = if (nodes.contains(node.name)) node else {
116-
nodes.find(_._2.phaseobj.exists(_.exists(_.phaseName == node.name))).get._2
117-
}
118-
effectiveNode.phaseobj = Some(effectiveNode.phaseobj.get ++ hl.frm.phaseobj.get)
119-
effectiveNode.before = hl.frm.before
120-
nodes -= hl.frm.phasename
121-
edges -= hl
122-
}
107+
def collapseHardLinksAndLevels(node: Node, lvl: Int) {
108+
if (node.visited) {
109+
dump("phase-cycle")
110+
throw new FatalError(s"Cycle in phase dependencies detected at ${node.phasename}, created phase-cycle.dot")
123111
}
124-
}
125112

126-
/* Test if there are cycles in the graph, assign levels to the nodes
127-
* and collapse hard links into nodes
128-
*/
129-
def assignLevelsAndDetectCycles(node: Node) {
130-
val stack = mutable.ArrayStack[Node]()
131-
def visitNode(node: Node): Unit = {
132-
node.visited = Visiting
133-
for (edge <- node.before) {
134-
val from = edge.frm
135-
from.visited match {
136-
case NotVisited =>
137-
visitNode(edge.frm)
138-
case Visiting =>
139-
dump("phase-cycle")
140-
throw new FatalError(s"Cycle in phase dependencies detected at ${node.phasename}, created phase-cycle.dot")
141-
case Visited =>
142-
}
113+
val initLevel = node.level
114+
val levelUp = initLevel < lvl
115+
if (levelUp) {
116+
node.level = lvl
117+
}
118+
if (initLevel != 0) {
119+
if (!levelUp) {
120+
// no need to revisit
121+
node.visited = false
122+
return
143123
}
144-
node.visited = Visited
145-
stack.push(node)
146124
}
147-
try {
148-
visitNode(node)
149-
} finally {
150-
nodes.values.foreach(_.visited = NotVisited)
125+
var befores = node.before
126+
def hasHardLinks() = befores.exists(_.hard)
127+
while (hasHardLinks()) {
128+
for (hl <- befores) {
129+
if (hl.hard) {
130+
node.phaseobj = Some(node.phaseobj.get ++ hl.frm.phaseobj.get)
131+
node.before = hl.frm.before
132+
nodes -= hl.frm.phasename
133+
edges -= hl
134+
for (edge <- node.before) edge.to = node
135+
}
136+
}
137+
befores = node.before
151138
}
139+
node.visited = true
152140

153-
val topoSort: Map[Node, Int] = stack.zipWithIndex.toMap
154-
val root = node
155-
assert(stack.head == root, stack)
156-
root.level = 1
157-
158-
// Nodes that have been collapsed into their hard-linked predecessor
159-
val collapsed: Map[String, Node] = stack.iterator.flatMap(p => p.phaseobj.toList.flatMap(_.map(x => (x.phaseName, p)))).toMap
160-
def followHard(node: Node): Node = collapsed.getOrElse(node.phasename, node)
161-
162-
// find the longest path to the root node to assign as the level.
163-
stack.iterator.drop(1).foreach { node =>
164-
var n = node
165-
var level = 1
166-
while (n != root && n.after.nonEmpty) {
167-
n = n.after.maxBy(edge => topoSort.get(followHard(edge.to))).to
168-
level += 1
169-
}
170-
node.level = level
141+
for (edge <- node.before) {
142+
collapseHardLinksAndLevels( edge.frm, lvl + 1)
171143
}
172-
def phaseDebug = stack.toSeq.groupBy(_.level).toList.sortBy(_._1).map { case (level, nodes) => (level, nodes.sortBy(_.phasename).map(_.phaseobj.map(_.map(_.phaseName).mkString(":")).mkString(" ")).mkString(" "))}.mkString("\n")
173-
debuglog("Phase assembly levels: " + phaseDebug)
144+
145+
node.visited = false
174146
}
175147

176148
/* Find all edges in the given graph that are hard links. For each hard link we
@@ -263,16 +235,11 @@ trait PhaseAssembly {
263235

264236
dump(3)
265237

266-
// collapse hard links into nodes
267-
graph.collapseHardLinks()
238+
// test for cycles, assign levels and collapse hard links into nodes
239+
graph.collapseHardLinksAndLevels(graph.getNodeByPhase("parser"), 1)
268240

269241
dump(4)
270242

271-
// test for cycles, assign levels
272-
graph.assignLevelsAndDetectCycles(graph.getNodeByPhase("parser"))
273-
274-
dump(5)
275-
276243
// assemble the compiler
277244
graph.compilerPhaseList()
278245
}

test/benchmarks/src/main/scala/scala/tools/nsc/PhaseAssemblyBenchmark.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class PhaseAssemblyBenchmark {
1919
var data: Data[_] = _
2020

2121
@Param(Array("1", "4", "8", "16"))
22-
var size: Int = 16
22+
var size: Int = 0
2323

2424
@Setup
2525
def setup(): Unit = {
@@ -47,8 +47,7 @@ class PhaseAssemblyBenchmark {
4747
val g = s.global
4848
val graph = g.phasesSetToDepGraph(s.components.reverse)
4949
graph.removeDanglingNodes()
50-
graph.collapseHardLinks()
51-
graph.assignLevelsAndDetectCycles(graph.getNodeByPhase("parser"))
50+
graph.collapseHardLinksAndLevels(graph.getNodeByPhase("parser"), 1)
5251
graph
5352
}
5453
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
error: Cycle in phase dependencies detected at cyclicdependency2, created phase-cycle.dot
1+
error: Cycle in phase dependencies detected at cyclicdependency1, created phase-cycle.dot

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

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)