aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSchuyler Eldridge2021-06-16 23:15:40 -0400
committerGitHub2021-06-17 03:15:40 +0000
commitc7eaa67d21e6e27c020ec18d88baf25a721d14de (patch)
tree05f7060da02e5e605ac15bbef9530f9f1030619e /src
parentd708d3f0555bb940ff417d3574c6902bb7a9c853 (diff)
Add --start-from option (#2273)
Add a new option to the FIRRTL compiler, "--start-from = <form>". If used, this will cause the compiler to assume that the input FIRRTL circuit is already in the specific form. It will then skip unnecessary passes given this information. E.g., if a user requests to run "firrtl -X verilog --start-from low" then the compiler will only run transforms necessary to get from low FIRRTL to Verilog. Transforms necessary for ingesting FIRRTL IR will be run if needed (checks and type/kind/flow resolution). To implement this, a CurrentFirrtlStateAnnotation is added. Advanced users can use this directly to tell the FIRRTL compiler exactly what transforms have already been run, including the ability to ignore checks or type/kind/flow resolution if they so desire. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/firrtl/stage/FirrtlAnnotations.scala45
-rw-r--r--src/main/scala/firrtl/stage/FirrtlCli.scala3
-rw-r--r--src/main/scala/firrtl/stage/package.scala1
-rw-r--r--src/main/scala/firrtl/stage/phases/Compiler.scala44
-rw-r--r--src/test/scala/firrtl/stage/CurrentFirrtlStateAnnotationSpec.scala66
5 files changed, 144 insertions, 15 deletions
diff --git a/src/main/scala/firrtl/stage/FirrtlAnnotations.scala b/src/main/scala/firrtl/stage/FirrtlAnnotations.scala
index 8f5ee3e1..9e6fefca 100644
--- a/src/main/scala/firrtl/stage/FirrtlAnnotations.scala
+++ b/src/main/scala/firrtl/stage/FirrtlAnnotations.scala
@@ -5,7 +5,7 @@ package firrtl.stage
import firrtl._
import firrtl.ir.Circuit
import firrtl.annotations.{Annotation, NoTargetAnnotation}
-import firrtl.options.{HasShellOptions, OptionsException, ShellOption, Unserializable}
+import firrtl.options.{Dependency, HasShellOptions, OptionsException, ShellOption, Unserializable}
import java.io.FileNotFoundException
import java.nio.file.NoSuchFileException
@@ -313,3 +313,46 @@ object DisableFold extends HasShellOptions {
)
}
+
+/** Indicate to the FIRRTL compiler that specific transforms have already been run.
+ *
+ * The intended use of this is for advanced users who want to skip specific transforms in the FIRRTL compiler. It is
+ * far safer for users to use the command line options to the FIRRTL compiler via `--start-from = <form>`.
+ * @param currentState a sequence of transforms that have already been run on the circuit
+ */
+case class CurrentFirrtlStateAnnotation(currentState: Seq[TransformDependency])
+ extends NoTargetAnnotation
+ with FirrtlOption
+
+private[stage] object CurrentFirrtlStateAnnotation extends HasShellOptions {
+
+ /** This is just the transforms necessary for resolving types and checking that everything is okay. */
+ private val dontSkip: Set[TransformDependency] = Set(
+ Dependency[firrtl.stage.transforms.CheckScalaVersion],
+ Dependency(passes.ResolveKinds),
+ Dependency(passes.InferTypes),
+ Dependency(passes.ResolveFlows)
+ ) ++ Forms.Checks
+
+ override val options = Seq(
+ new ShellOption[String](
+ longOption = "start-from",
+ toAnnotationSeq = a =>
+ (a match {
+ case "chirrtl" => Seq.empty
+ case "mhigh" => Forms.MinimalHighForm
+ case "high" => Forms.HighForm
+ case "middle" => Forms.MidForm
+ case "low" => Forms.LowForm
+ case "low-opt" => Forms.LowFormOptimized
+ case _ => throw new OptionsException(s"Unknown start-from argument '$a'! (Did you misspell it?)")
+ }).filterNot(dontSkip) match {
+ case b if a.isEmpty => Seq.empty
+ case b => Seq(CurrentFirrtlStateAnnotation(b))
+ },
+ helpText = "",
+ helpValueName = Some("<chirrtl|mhigh|high|middle|low|low-opt>")
+ )
+ )
+
+}
diff --git a/src/main/scala/firrtl/stage/FirrtlCli.scala b/src/main/scala/firrtl/stage/FirrtlCli.scala
index 9cfa6be9..8f84ff18 100644
--- a/src/main/scala/firrtl/stage/FirrtlCli.scala
+++ b/src/main/scala/firrtl/stage/FirrtlCli.scala
@@ -23,7 +23,8 @@ trait FirrtlCli { this: Shell =>
WarnNoScalaVersionDeprecation,
PrettyNoExprInlining,
DisableFold,
- OptimizeForFPGA
+ OptimizeForFPGA,
+ CurrentFirrtlStateAnnotation
)
.map(_.addOptions(parser))
diff --git a/src/main/scala/firrtl/stage/package.scala b/src/main/scala/firrtl/stage/package.scala
index 68e7a9c5..a22d299a 100644
--- a/src/main/scala/firrtl/stage/package.scala
+++ b/src/main/scala/firrtl/stage/package.scala
@@ -35,6 +35,7 @@ package object stage {
case WarnNoScalaVersionDeprecation => c
case PrettyNoExprInlining => c
case _: DisableFold => c
+ case CurrentFirrtlStateAnnotation(a) => c
}
}
}
diff --git a/src/main/scala/firrtl/stage/phases/Compiler.scala b/src/main/scala/firrtl/stage/phases/Compiler.scala
index 24848f36..d24775e7 100644
--- a/src/main/scala/firrtl/stage/phases/Compiler.scala
+++ b/src/main/scala/firrtl/stage/phases/Compiler.scala
@@ -4,23 +4,31 @@ package firrtl.stage.phases
import firrtl.{AnnotationSeq, ChirrtlForm, CircuitState, Compiler => FirrtlCompiler, Transform, seqToAnnoSeq}
import firrtl.options.{Dependency, Phase, PhasePrerequisiteException, Translator}
-import firrtl.stage.{CompilerAnnotation, FirrtlCircuitAnnotation, Forms, RunFirrtlTransformAnnotation}
+import firrtl.stage.{
+ CompilerAnnotation,
+ CurrentFirrtlStateAnnotation,
+ FirrtlCircuitAnnotation,
+ Forms,
+ RunFirrtlTransformAnnotation
+}
import firrtl.stage.TransformManager.TransformDependency
import scala.collection.mutable
/** An encoding of the information necessary to run the FIRRTL compiler once */
private[stage] case class CompilerRun(
- stateIn: CircuitState,
- stateOut: Option[CircuitState],
- transforms: Seq[Transform],
- compiler: Option[FirrtlCompiler])
+ stateIn: CircuitState,
+ stateOut: Option[CircuitState],
+ transforms: Seq[Transform],
+ compiler: Option[FirrtlCompiler],
+ currentState: Seq[TransformDependency])
/** An encoding of possible defaults for a [[CompilerRun]] */
private[stage] case class Defaults(
- annotations: AnnotationSeq = Seq.empty,
- transforms: Seq[Transform] = Seq.empty,
- compiler: Option[FirrtlCompiler] = None)
+ annotations: AnnotationSeq = Seq.empty,
+ transforms: Seq[Transform] = Seq.empty,
+ compiler: Option[FirrtlCompiler] = None,
+ currentState: Seq[TransformDependency] = Seq.empty)
/** Runs the FIRRTL compilers on an [[AnnotationSeq]]. If the input [[AnnotationSeq]] contains more than one circuit
* (i.e., more than one [[firrtl.stage.FirrtlCircuitAnnotation FirrtlCircuitAnnotation]]), then annotations will be
@@ -64,7 +72,13 @@ class Compiler extends Phase with Translator[AnnotationSeq, Seq[CompilerRun]] {
a.foldLeft(Defaults()) {
case (d, FirrtlCircuitAnnotation(circuit)) =>
foundFirstCircuit = true
- CompilerRun(CircuitState(circuit, ChirrtlForm, d.annotations, None), None, d.transforms, d.compiler) +=: c
+ CompilerRun(
+ CircuitState(circuit, ChirrtlForm, d.annotations, None),
+ None,
+ d.transforms,
+ d.compiler,
+ d.currentState
+ ) +=: c
d
case (d, a) if foundFirstCircuit =>
a match {
@@ -74,6 +88,9 @@ class Compiler extends Phase with Translator[AnnotationSeq, Seq[CompilerRun]] {
case CompilerAnnotation(compiler) =>
c(0) = c(0).copy(compiler = Some(compiler))
d
+ case CurrentFirrtlStateAnnotation(currentState) =>
+ c(0) = c(0).copy(currentState = currentState ++ c(0).currentState)
+ d
case annotation =>
val state = c(0).stateIn
c(0) = c(0).copy(stateIn = state.copy(annotations = annotation +: state.annotations))
@@ -81,9 +98,10 @@ class Compiler extends Phase with Translator[AnnotationSeq, Seq[CompilerRun]] {
}
case (d, a) if !foundFirstCircuit =>
a match {
- case RunFirrtlTransformAnnotation(transform) => d.copy(transforms = transform +: d.transforms)
- case CompilerAnnotation(compiler) => d.copy(compiler = Some(compiler))
- case annotation => d.copy(annotations = annotation +: d.annotations)
+ case RunFirrtlTransformAnnotation(transform) => d.copy(transforms = transform +: d.transforms)
+ case CompilerAnnotation(compiler) => d.copy(compiler = Some(compiler))
+ case CurrentFirrtlStateAnnotation(currentState) => d.copy(currentState = currentState ++ d.currentState)
+ case annotation => d.copy(annotations = annotation +: d.annotations)
}
}
c.toSeq
@@ -110,7 +128,7 @@ class Compiler extends Phase with Translator[AnnotationSeq, Seq[CompilerRun]] {
c.transforms.reverse.map(Dependency.fromTransform)
}
}
- val tm = new firrtl.stage.transforms.Compiler(targets)
+ val tm = new firrtl.stage.transforms.Compiler(targets, c.currentState)
/* Transform order is lazily evaluated. Force it here to remove its resolution time from actual compilation. */
val (timeResolveDependencies, _) = firrtl.Utils.time { tm.flattenedTransformOrder }
logger.info(f"Computed transform order in: $timeResolveDependencies%.1f ms")
diff --git a/src/test/scala/firrtl/stage/CurrentFirrtlStateAnnotationSpec.scala b/src/test/scala/firrtl/stage/CurrentFirrtlStateAnnotationSpec.scala
new file mode 100644
index 00000000..121a4e81
--- /dev/null
+++ b/src/test/scala/firrtl/stage/CurrentFirrtlStateAnnotationSpec.scala
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package firrtl.stage
+
+import firrtl.options.Dependency
+import firrtl.stage.transforms.Compiler
+import firrtl.stage.TransformManager.TransformDependency
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.matchers.should.Matchers
+
+class CurrentFirrtlStateAnnotationSpec extends AnyFlatSpec with Matchers {
+
+ def getTransforms(input: String): Seq[TransformDependency] = {
+ val currentState = CurrentFirrtlStateAnnotation
+ .options(0)
+ .toAnnotationSeq(input)
+ .collectFirst {
+ case CurrentFirrtlStateAnnotation(currentState) => currentState
+ }
+ .get
+ new Compiler(Forms.VerilogOptimized, currentState).flattenedTransformOrder.map(Dependency.fromTransform)
+ }
+
+ behavior.of("CurrentFirrtlStateAnnotation")
+
+ it should "produce an expected transform order for CHIRRTL -> Verilog" in {
+ getTransforms("chirrtl") should contain(Dependency(firrtl.passes.CheckChirrtl))
+ }
+
+ it should "produce an expected transform order for minimum high FIRRTL -> Verilog" in {
+ val transforms = getTransforms("mhigh")
+ transforms should not contain noneOf(Dependency(firrtl.passes.CheckChirrtl), Dependency(firrtl.passes.InferTypes))
+ transforms should contain(Dependency(firrtl.passes.CheckHighForm))
+ }
+
+ it should "produce an expected transform order for high FIRRTL -> Verilog" in {
+ val transforms = getTransforms("high")
+ transforms should not contain (Dependency[firrtl.transforms.DedupModules])
+ (transforms should contain).allOf(
+ Dependency(firrtl.passes.InferTypes),
+ Dependency[firrtl.passes.ExpandWhensAndCheck]
+ )
+ }
+
+ it should "produce an expected transform order for middle FIRRTL -> Verilog" in {
+ val transforms = getTransforms("middle")
+ transforms should not contain (Dependency[firrtl.passes.ExpandWhensAndCheck])
+ (transforms should contain).allOf(Dependency(firrtl.passes.InferTypes), Dependency(firrtl.passes.LowerTypes))
+ }
+
+ it should "produce an expected transform order for low FIRRTL -> Verilog" in {
+ val transforms = getTransforms("low")
+ transforms should not contain (Dependency(firrtl.passes.LowerTypes))
+ (transforms should contain).allOf(
+ Dependency(firrtl.passes.InferTypes),
+ Dependency(firrtl.passes.CommonSubexpressionElimination)
+ )
+ }
+
+ it should "produce an expected transform order for optimized low FIRRTL -> Verilog" in {
+ val transforms = getTransforms("low-opt")
+ transforms should not contain (Dependency(firrtl.passes.CommonSubexpressionElimination))
+ (transforms should contain).allOf(Dependency(firrtl.passes.InferTypes), Dependency[firrtl.transforms.VerilogRename])
+ }
+
+}