Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Feb 14, 2024
1 parent b1080e2 commit b205cad
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 83 deletions.
116 changes: 47 additions & 69 deletions src/compiler/scala/tools/nsc/PhaseAssembly.scala
Expand Up @@ -12,43 +12,27 @@

package scala.tools.nsc

import scala.collection.mutable, mutable.ArrayDeque
import scala.collection.mutable, mutable.ArrayDeque, mutable.ListBuffer
import scala.reflect.io.{File, Path}
import scala.tools.nsc.Reporting.WarningCategory
import scala.util.chaining._
import scala.util.control.ControlThrowable

import DependencyGraph.Messaging

/** Converts an unordered morass of components into an order that
* satisfies their mutual constraints.
/** Sorts the global phasesSet according to SubComponent constraints.
*/
trait PhaseAssembly {
self: Global =>

/** Called by Global#computePhaseDescriptors to compute phase order. */
def computePhaseAssembly(): List[SubComponent] = {

implicit val messaging: Messaging = Messaging(informProgress,
runReporting.warning(NoPosition, _, WarningCategory.Other, site=""),
globalError,
(g, f) => for (d <- settings.outputDirs.getSingleOutput if !d.isVirtual) DependencyGraph.graphToDotFile(g, Path(d.file) / File(f))
)

val graph = DependencyGraph(phasesSet)

// Output the phase dependency graph at this stage
def dump(stage: Int) = settings.genPhaseGraph.valueSetByUser.foreach(n => graph.dump(s"$n-$stage.dot"))

dump(1)

for (n <- settings.genPhaseGraph.valueSetByUser; d <- settings.outputDirs.getSingleOutput if !d.isVirtual)
DependencyGraph.graphToDotFile(graph, Path(d.file) / File(s"$n.dot"))
graph.compilerPhaseList()
}
}

/** A graph with the given number of vertices.
*/
class DependencyGraph(order: Int, components: Map[String, SubComponent]) {
class DependencyGraph(order: Int, val components: Map[String, SubComponent]) {
import DependencyGraph.{FollowsNow, Start, Weight}

/** Number of edges. */
Expand Down Expand Up @@ -89,32 +73,42 @@ class DependencyGraph(order: Int, components: Map[String, SubComponent]) {

// input must be acyclic and only one FollowsNow edge is allowed between a pair of vertices
def validate(): Unit = {
def checkFollowsNow(v: Int): Unit =
adjacency(v).foldLeft(-1) { (w, e) =>
if (e.weight != FollowsNow) w
else if (w == -1) e.to
else throw new FatalError(s"Phases ${names(w)} and ${names(e.to)} both immediately follow ${names(v)}")
}
val seen = Array.ofDim[Boolean](order)
val onPath = Array.ofDim[Boolean](order)
def walk(v: Int): Unit = {
def checkFollowsNow(): Unit =
adjacency(v).foldLeft(-1) { (w, e) =>
if (e.weight != FollowsNow) w
else if (w == -1) e.to
else throw new FatalError(s"Phases ${names(w)} and ${names(e.to)} both immediately follow ${names(v)}")
val stack = mutable.Stack.empty[(Int, List[Edge])] // a vertex and list of edges remaining to follow
def walk(): Unit = {
nodes(Start).tap { start =>
stack.push(start -> adjacency(start))
}
while (!stack.isEmpty) {
val (v, edges) = stack.pop()
if (!seen(v)) {
checkFollowsNow(v)
seen(v) = true
}
onPath(v) = true
edges match {
case Edge(_, to, _) :: edges =>
if (onPath(to)) {
var path = v :: to :: Nil
while (path.head != to)
path ::= stack.pop()._1
throw new FatalError(s"Phases form a cycle: ${path.map(names(_)).mkString(" -> ")}")
}
stack.push(v -> edges)
if (!seen(to))
stack.push(to -> adjacency(to))
case _ => onPath(v) = false
}
case class Cycle(v: Int) extends ControlThrowable {
var stack = v :: Nil
}
seen(v) = true
onPath(v) = true
checkFollowsNow()
try for (e <- adjacency(v))
if (!seen(e.to))
try walk(e.to)
catch {
case cycle @ Cycle(`v`) => throw new FatalError(s"Phases form a cycle: ${(v :: cycle.stack).map(names(_)).mkString(" -> ")}")
case cycle @ Cycle(_) => throw cycle.tap(_.stack ::= v)
}
else if (onPath(e.to)) throw Cycle(e.to).tap(_.stack ::= v)
finally onPath(v) = false
}
walk(nodes(Start))
walk()
}

def compilerPhaseList(): List[SubComponent] = {
Expand Down Expand Up @@ -192,8 +186,6 @@ class DependencyGraph(order: Int, components: Map[String, SubComponent]) {
relax()
traverse()
}

def dump(title: String)(implicit messaging: Messaging): Unit = messaging.dump(this, s"$title.dot")
}
object DependencyGraph {

Expand Down Expand Up @@ -227,32 +219,18 @@ object DependencyGraph {
}
}

/* This is a helper method, that given a dependency graph will generate a graphviz dot
* file showing its structure.
* Plug-in supplied phases are marked as green nodes and hard links are marked as blue edges.
/** Emit a graphviz dot file for the graph.
* Plug-in supplied phases are marked as green nodes and hard links are marked as blue edges.
*/
def graphToDotFile(graph: DependencyGraph, file: File): Unit = {
/*
val edges = graph.edges.toSeq
val extnodes = edges.map(_.frm).filter(!_.phaseobj.get.head.internal)
val fatnodes = edges.flatMap(e => List(e.frm, e.to)).filter(_.phaseobj.exists(_.sizeIs > 1))
def color(hex: String) = s""" [color="#$hex"]"""
def node(n: graph.Node) = s""""${n.allPhaseNames}(${n.level})""""
file.printlnAll("digraph G {")
file.printlnAll(edges.map(e => s"${node(e.frm)}->${node(e.to)}" + color(if (e.hard) "0000ff" else "000000")): _*)
file.printlnAll(extnodes.distinct.map(n => node(n) + color("00ff00")): _*)
file.printlnAll(fatnodes.distinct.map(n => node(n) + color("0000ff")): _*)
file.printlnAll("}")
*/
}

case class Messaging(informProgress: String => Unit, warning: String => Unit, error: String => Unit, dump: (DependencyGraph, String) => Unit)
object Messaging {
val silent = Messaging(_ => (), _ => (), _ => (), (_, _) => ())
val stdout = Messaging(s => println(s), s => println(s), s => println(s), (_, _) => ())
val throws = Messaging(s => fail(s), s => fail(s), s => fail(s), (_, _) => ())
private def fail(s: String) = throw new Exception(s)
def color(hex: String) = s""" [color="#$hex"]"""
val sb = ListBuffer.empty[String]
sb.addOne("digraph G {")
for (edges <- graph.adjacency; e <- edges)
sb.addOne(s"${graph.names(e.from)} -> ${graph.names(e.to)}${if (e.weight == FollowsNow) color("0000ff") else ""}")
for (n <- graph.names)
sb.addOne(s"${n}${if (graph.components(n).internal) "" else color("00ff00")}")
sb.addOne("}")
file.printlnAll(sb.toList: _*)
}
}
Expand Up @@ -47,11 +47,7 @@ class PhaseAssemblyBenchmark {
@Benchmark def assemble(): Object = {
val s = data.asInstanceOf[Data[Global with Singleton]]
val g = s.global
implicit val messaging: DependencyGraph.Messaging = DependencyGraph.Messaging.silent
val graph = DependencyGraph(s.components.reverse)
//graph.removeDanglingNodes()
//graph.validateAndEnforceHardlinks()
//graph.collapseHardLinksAndLevels(graph.getNodeByPhase("parser"), 1)
graph.compilerPhaseList()
}
}
Expand Down
13 changes: 3 additions & 10 deletions test/junit/scala/tools/nsc/PhaseAssemblyTest.scala
Expand Up @@ -36,21 +36,18 @@ class PhaseAssemblyTest {
)
@Test def multipleRunsRightAfter: Unit = {
val settings = new Settings
//settings.verbose.tryToSet(Nil)
settings.verbose.tryToSet(Nil)
val global = new Global(settings)
//val N = 16 * 4096 // 65536 ~ 11-21 secs, 256 ~ 1-2 secs
val N = 16 * 4096 // 65536 ~ 11-21 secs, 256 ~ 1-2 secs
//val N = 256
val N = 16
//val N = 16
val random = new scala.util.Random(123502L)
val names = Array.tabulate(N)(n => s"phase_${n+1}_${random.nextInt(1024)}")
val beforeTerminal = List("terminal")
val components = names.foldLeft(parserAndTerminal(global)) { (comps, nm) =>
component(global, nm, runsRightAfter = comps.headOption.map(_.phaseName), runsAfter = Nil, runsBefore = beforeTerminal) :: comps
}
val inputs = random.shuffle(components)
//implicit val messaging: DependencyGraph.Messaging = DependencyGraph.Messaging.throws
//implicit val messaging: DependencyGraph.Messaging = DependencyGraph.Messaging.silent
//implicit val messaging: DependencyGraph.Messaging = DependencyGraph.Messaging.stdout
val graph = DependencyGraph(inputs)
val phases: List[SubComponent] = graph.compilerPhaseList()
val result: List[String] = phases.map(_.phaseName).filter(_.startsWith("phase_"))
Expand All @@ -60,17 +57,13 @@ class PhaseAssemblyTest {
}
@Test def trivial: Unit = {
val settings = new Settings
//settings.verbose.tryToSet(Nil)
val global = new Global(settings)
val beforeTerminal = List("terminal")
val names = Array("phooey", "kerfuffle")
val components = names.foldLeft(parserAndTerminal(global)) { (comps, nm) =>
component(global, nm, runsRightAfter = None, runsAfter = comps.headOption.map(_.phaseName).toList, runsBefore = beforeTerminal) :: comps
}
val inputs = components
//implicit val messaging: DependencyGraph.Messaging = DependencyGraph.Messaging.throws
//implicit val messaging: DependencyGraph.Messaging = DependencyGraph.Messaging.silent
//implicit val messaging: DependencyGraph.Messaging = DependencyGraph.Messaging.stdout
val graph = DependencyGraph(inputs)
val result: List[SubComponent] = graph.compilerPhaseList()
assertEquals("parser", result.head.phaseName)
Expand Down

0 comments on commit b205cad

Please sign in to comment.