aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSchuyler Eldridge2019-02-12 20:59:56 -0500
committerSchuyler Eldridge2019-07-03 19:59:51 -0400
commit2585bf986347e264f4c06c38dbcc83aaa554ec4f (patch)
tree772bc97b4f8e26cd200cb45b61f05f318ba3527e /src
parenta514408c77c137de4a825ae243ac39ecabd9e3a3 (diff)
Add a DependencyAPI to firrtl.options.Phase
This adds a TransformLike mixin, DependencyAPI, that defines the basis of the Dependency API for Stage/Phase. DependencyAPI defines three members that define dependency relationships for some TransformLike "Foo": - "Prerequisites" define TransformLikes that should run before Foo - "Dependents" define TransformLikes that should run after Foo. This allows Foo to inject prerequisites into some other TransformLike. - "Invalidates" define a function that will return true if a specific TransformLike would be invalidated by Foo Prerequisites and Dependents are not Sets due to lack of a fast, immutable Set that preserves insertion order. Internally, these are converted to a private LinkedHashSet. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/firrtl/options/Phase.scala88
1 files changed, 76 insertions, 12 deletions
diff --git a/src/main/scala/firrtl/options/Phase.scala b/src/main/scala/firrtl/options/Phase.scala
index 7ed964e8..e4bb6f0d 100644
--- a/src/main/scala/firrtl/options/Phase.scala
+++ b/src/main/scala/firrtl/options/Phase.scala
@@ -6,6 +6,7 @@ import firrtl.AnnotationSeq
import logger.LazyLogging
+import scala.collection.mutable.LinkedHashSet
/** A polymorphic mathematical transform
* @tparam A the transformed type
@@ -23,15 +24,78 @@ trait TransformLike[A] extends LazyLogging {
}
+/** Mixin that defines dependencies between [[firrtl.options.TransformLike TransformLike]]s (hereafter referred to as
+ * "transforms")
+ *
+ * This trait forms the basis of the Dependency API of the Chisel/FIRRTL Hardware Compiler Framework. Dependencies are
+ * defined in terms of prerequisistes, dependents, and invalidates. A prerequisite is a transform that must run before
+ * this transform. A dependent is a transform that must run ''after'' this transform. (This can be viewed as a means of
+ * injecting a prerequisite into some other transform.) Finally, invalidates define the set of transforms whose effects
+ * this transform undos/invalidates. (Invalidation then implies that a transform that is invalidated by this transform
+ * and needed by another transform will need to be re-run.)
+ *
+ * This Dependency API only defines dependencies. A concrete [[DependencyManager]] is expected to be used to statically
+ * resolve a linear ordering of transforms that satisfies dependency requirements.
+ * @tparam A some transform
+ * @define seqNote @note The use of a Seq here is to preserve input order. Internally, this will be converted to a private,
+ * ordered Set.
+ */
+trait DependencyAPI[A <: DependencyAPI[A]] { this: TransformLike[_] =>
+
+ /** All transform that must run before this transform
+ * $seqNote
+ */
+ def prerequisites: Seq[Class[A]] = Seq.empty
+ private[options] lazy val _prerequisites: LinkedHashSet[Class[A]] = new LinkedHashSet() ++ prerequisites.toSet
+
+ /** All transforms that must run ''after'' this transform
+ *
+ * ''This is a means of prerequisite injection into some other transform.'' Normally a transform will define its own
+ * prerequisites. Dependents exist for two main situations:
+ *
+ * First, they improve the composition of optional transforms. If some first transform is optional (e.g., an
+ * expensive validation check), you would like to be able to conditionally cause it to run. If it is listed as a
+ * prerequisite on some other, second transform then it must always run before that second transform. There's no way
+ * to turn it off. However, by listing the second transform as a dependent of the first transform, the first
+ * transform will only run (and be treated as a prerequisite of the second transform) if included in a list of target
+ * transforms that should be run.
+ *
+ * Second, an external library would like to inject some first transform before a second transform inside FIRRTL. In
+ * this situation, the second transform cannot have any knowledge of external libraries. The use of a dependent here
+ * allows for prerequisite injection into FIRRTL proper.
+ *
+ * @see [[firrtl.passes.CheckTypes]] for an example of an optional checking [[firrtl.Transform]]
+ * $seqNote
+ */
+ def dependents: Seq[Class[A]] = Seq.empty
+ private[options] lazy val _dependents: LinkedHashSet[Class[A]] = new LinkedHashSet() ++ dependents.toSet
+
+ /** A function that, given a transform will return true if this transform invalidates/undos the effects of the input
+ * transform
+ * @note Can a [[firrtl.options.Phase Phase]] ever invalidate itself?
+ */
+ def invalidates(a: A): Boolean = true
+
+ /** Helper method to return the underlying class */
+ final def asClass: Class[A] = this.getClass.asInstanceOf[Class[A]]
+
+ /** Implicit conversion that allows for terser specification of [[DependencyAPI.prerequisites prerequisites]] and
+ * [[DependencyAPI.dependents dependents]].
+ */
+ implicit def classHelper(a: Class[_ <: A]): Class[A] = a.asInstanceOf[Class[A]]
+
+}
+
/** A mathematical transformation of an [[AnnotationSeq]].
*
- * A [[Phase]] forms one unit in the Chisel/FIRRTL Hardware Compiler Framework (HCF). The HCF is built from a sequence
- * of [[Phase]]s applied to an [[AnnotationSeq]]. Note that a [[Phase]] may consist of multiple phases internally.
+ * A [[firrtl.options.Phase Phase]] forms one unit in the Chisel/FIRRTL Hardware Compiler Framework (HCF). The HCF is
+ * built from a sequence of [[firrtl.options.Phase Phase]]s applied to an [[AnnotationSeq]]. Note that a
+ * [[firrtl.options.Phase Phase]] may consist of multiple phases internally.
*/
-abstract class Phase extends TransformLike[AnnotationSeq] {
+trait Phase extends TransformLike[AnnotationSeq] with DependencyAPI[Phase] {
- /** The name of this [[Phase]]. This will be used to generate debug/error messages or when deleting annotations. This
- * will default to the `simpleName` of the class.
+ /** The name of this [[firrtl.options.Phase Phase]]. This will be used to generate debug/error messages or when deleting
+ * annotations. This will default to the `simpleName` of the class.
* @return this phase's name
* @note Override this with your own implementation for different naming behavior.
*/
@@ -39,15 +103,15 @@ abstract class Phase extends TransformLike[AnnotationSeq] {
}
-/** A [[TransformLike]] that internally ''translates'' the input type to some other type, transforms the internal type,
- * and converts back to the original type.
+/** A [[firrtl.options.TransformLike TransformLike]] that internally ''translates'' the input type to some other type,
+ * transforms the internal type, and converts back to the original type.
*
- * This is intended to be used to insert a [[TransformLike]] parameterized by type `B` into a sequence of
- * [[TransformLike]]s parameterized by type `A`.
- * @tparam A the type of the [[TransformLike]]
+ * This is intended to be used to insert a [[firrtl.options.TransformLike TransformLike]] parameterized by type `B`
+ * into a sequence of [[firrtl.options.TransformLike TransformLike]]s parameterized by type `A`.
+ * @tparam A the type of the [[firrtl.options.TransformLike TransformLike]]
* @tparam B the internal type
*/
-trait Translator[A, B] { this: TransformLike[A] =>
+trait Translator[A, B] extends TransformLike[A] {
/** A method converting type `A` into type `B`
* @param an object of type `A`
@@ -69,6 +133,6 @@ trait Translator[A, B] { this: TransformLike[A] =>
/** Convert the input object to the internal type, transform the internal type, and convert back to the original type
*/
- final def transform(a: A): A = internalTransform(a)
+ override final def transform(a: A): A = bToA(internalTransform(aToB(a)))
}