From 2585bf986347e264f4c06c38dbcc83aaa554ec4f Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Tue, 12 Feb 2019 20:59:56 -0500 Subject: 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 --- src/main/scala/firrtl/options/Phase.scala | 88 ++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 12 deletions(-) (limited to 'src') 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))) } -- cgit v1.2.3