diff options
| author | Schuyler Eldridge | 2019-02-12 20:59:56 -0500 |
|---|---|---|
| committer | Schuyler Eldridge | 2019-07-03 19:59:51 -0400 |
| commit | 2585bf986347e264f4c06c38dbcc83aaa554ec4f (patch) | |
| tree | 772bc97b4f8e26cd200cb45b61f05f318ba3527e /src | |
| parent | a514408c77c137de4a825ae243ac39ecabd9e3a3 (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.scala | 88 |
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))) } |
