diff options
| author | Albert Chen | 2020-05-28 09:33:58 -0700 |
|---|---|---|
| committer | GitHub | 2020-05-28 09:33:58 -0700 |
| commit | 0845fcdb0c25e73c3299fc0463790f57a2219a0c (patch) | |
| tree | 9b8055e6c2604980ca663a0a2db1ed0fe2acba20 /src/main/scala/firrtl/annotations/transforms | |
| parent | 01919d31422c73a4b71daa405ddbe37f81e709c0 (diff) | |
Implement InstanceTarget Behavior for Dedup + EliminateTargetPaths (#1539)
- RenameMap Behavior
-- Prevent transitive renaming A -> B -> C (continueRenaming)
-- Prevent transitive renaming for self-renames
- Target
-- Override toString as serialize for CompleteTarget
-- Expansion of stripHierarchy to enable stripping InstanceTargets to become ModuleTargets
Annotations
-- Bugfix in extractComponents where Products were not iterated over
-- Converts renamed targets to local targets using Target.referringModule to preserve sticky behavior
- Eliminate Target Paths
-- Make DuplicationHelper use LinkedHashMap, as we iterate over its contents and convert to Seq in def makePathless
-- Add DupedResult to map original module to new module targets
-- Update renaming to record a map from all relative instance paths to original module, to new module target
-- Consumes DedupedResult to give better name to new duplicated module if it was originally deduplicated
-- Reorder modules in attempt to preserve original ordering, pre-deduplication
-- Move utility functions to object
-- Bugfix: add self-renames to prevent ofModule _ of target _ cannot be renamed to Vector(_, _, _, ...) errors
- Dedup
-- Changed NoDedupAnnotation to contain ModuleTarget, rather than ModuleName
-- Added DedupedResult to map original module to the duplicate module
-- Consumes DupedResult to pick better name, if it existed
-- Updates renaming to chain the following: instancify deduped modules, remap differently named internal signals, then remap AST modules
-- Move utility functions to object
-- Remove annotations as part of determination of dedup correctness
-- Bugfix: add instance renames so that deduped modules have their instances properly renamed
- Dead Code Elimination
-- Add deletion of ASTModules
- Tests
-- Morphism Spec to ensure Dedup -> EliminateTargetPaths and EliminateTargetPaths -> Dedup patterns work properly
-- Update existing tests to make sure they work properly
-- Add Dedup tests to demonstrate instance renaming bug, EliminateTargetPaths for ofModule rename bug, and update RenameMap tests
Co-authored-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
Co-authored-by: Adam Izraelevitz <adam.izraelevitz@sifive.com>
Co-authored-by: Adam Izraelevitz <azidar@gmail.com>
Co-authored-by: Jack Koenig <koenig@sifive.com>
Diffstat (limited to 'src/main/scala/firrtl/annotations/transforms')
| -rw-r--r-- | src/main/scala/firrtl/annotations/transforms/EliminateTargetPaths.scala | 194 |
1 files changed, 163 insertions, 31 deletions
diff --git a/src/main/scala/firrtl/annotations/transforms/EliminateTargetPaths.scala b/src/main/scala/firrtl/annotations/transforms/EliminateTargetPaths.scala index a4cd2f3d..6bafa071 100644 --- a/src/main/scala/firrtl/annotations/transforms/EliminateTargetPaths.scala +++ b/src/main/scala/firrtl/annotations/transforms/EliminateTargetPaths.scala @@ -9,9 +9,10 @@ import firrtl.annotations.TargetToken.{Instance, OfModule, fromDefModuleToTarget import firrtl.annotations.analysis.DuplicationHelper import firrtl.annotations._ import firrtl.ir._ -import firrtl.{CircuitState, DependencyAPIMigration, FirrtlInternalException, RenameMap, Transform, WDefInstance} +import firrtl.{AnnotationSeq, CircuitState, DependencyAPIMigration, FirrtlInternalException, RenameMap, Transform} import firrtl.options.PreservesAll import firrtl.stage.Forms +import firrtl.transforms.DedupedResult import scala.collection.mutable @@ -26,8 +27,66 @@ case class ResolvePaths(targets: Seq[CompleteTarget]) extends Annotation { } } +/** Holds the mapping from original module to the new, duplicated modules + * The original module target is unaffected by renaming + * @param newModules Instance target of what the original module now points to + * @param originalModule Original module + */ +case class DupedResult(newModules: Set[IsModule], originalModule: ModuleTarget) extends MultiTargetAnnotation { + override val targets: Seq[Seq[Target]] = Seq(newModules.toSeq) + override def duplicate(n: Seq[Seq[Target]]): Annotation = { + n.toList match { + case Seq(newMods) => DupedResult(newMods.collect { case x: IsModule => x }.toSet, originalModule) + case _ => DupedResult(Set.empty, originalModule) + } + } +} + case class NoSuchTargetException(message: String) extends FirrtlInternalException(message) +object EliminateTargetPaths { + + def renameModules(c: Circuit, toRename: Map[String, String], renameMap: RenameMap): Circuit = { + val ct = CircuitTarget(c.main) + val cx = if(toRename.contains(c.main)) { + renameMap.record(ct, CircuitTarget(toRename(c.main))) + c.copy(main = toRename(c.main)) + } else { + c + } + def onMod(m: DefModule): DefModule = { + m map onStmt match { + case e: ExtModule if toRename.contains(e.name) => + renameMap.record(ct.module(e.name), ct.module(toRename(e.name))) + e.copy(name = toRename(e.name)) + case e: Module if toRename.contains(e.name) => + renameMap.record(ct.module(e.name), ct.module(toRename(e.name))) + e.copy(name = toRename(e.name)) + case o => o + } + } + def onStmt(s: Statement): Statement = s map onStmt match { + case w@DefInstance(info, name, module, _) if toRename.contains(module) => w.copy(module = toRename(module)) + case other => other + } + cx map onMod + } + + def reorderModules(c: Circuit, toReorder: Map[String, Double]): Circuit = { + val newOrderMap = c.modules.zipWithIndex.map { + case (m, _) if toReorder.contains(m.name) => m.name -> toReorder(m.name) + case (m, i) if c.modules.size > 1 => m.name -> i.toDouble / (c.modules.size - 1) + case (m, _) => m.name -> 1.0 + }.toMap + + val newOrder = c.modules.sortBy { m => newOrderMap(m.name) } + + c.copy(modules = newOrder) + } + + +} + /** For a set of non-local targets, modify the instance/module hierarchy of the circuit such that * the paths in each non-local target can be removed * @@ -44,6 +103,7 @@ case class NoSuchTargetException(message: String) extends FirrtlInternalExceptio * C/x -> (C/x, C_/x) // where x is any reference in C */ class EliminateTargetPaths extends Transform with DependencyAPIMigration with PreservesAll[Transform] { + import EliminateTargetPaths._ override def prerequisites = Forms.MinimalHighForm override def optionalPrerequisites = Seq.empty @@ -62,9 +122,6 @@ class EliminateTargetPaths extends Transform with DependencyAPIMigration with Pr case d@DefInstance(_, name, module, _) => val ofModule = dupMap.getNewOfModule(originalModule, newModule, Instance(name), OfModule(module)).value d.copy(module = ofModule) - case d@WDefInstance(_, name, module, _) => - val ofModule = dupMap.getNewOfModule(originalModule, newModule, Instance(name), OfModule(module)).value - d.copy(module = ofModule) case other => other map onStmt(dupMap)(originalModule, newModule) } @@ -73,7 +130,10 @@ class EliminateTargetPaths extends Transform with DependencyAPIMigration with Pr * @param targets * @return */ - def run(cir: Circuit, targets: Seq[IsMember]): (Circuit, RenameMap) = { + def run(cir: Circuit, + targets: Seq[IsMember], + iGraph: InstanceGraph + ): (Circuit, RenameMap, AnnotationSeq) = { val dupMap = DuplicationHelper(cir.modules.map(_.name).toSet) @@ -85,8 +145,11 @@ class EliminateTargetPaths extends Transform with DependencyAPIMigration with Pr // Foreach module, calculate the unique names of its duplicates // Then, update the ofModules of instances that it encapsulates - cir.modules.foreach { m => - dupMap.getDuplicates(m.name).foreach { newName => + + val ct = CircuitTarget(cir.main) + val annos = cir.modules.map { m => + val newNames = dupMap.getDuplicates(m.name) + newNames.foreach { newName => val newM = m match { case e: ExtModule => e.copy(name = newName) case o: Module => @@ -94,6 +157,7 @@ class EliminateTargetPaths extends Transform with DependencyAPIMigration with Pr } duplicatedModuleList += newM } + DupedResult(newNames.map(ct.module), ct.module(m.name)) } val finalModuleList = duplicatedModuleList @@ -105,34 +169,74 @@ class EliminateTargetPaths extends Transform with DependencyAPIMigration with Pr /* Foreach target, calculate the pathless version and only rename targets that are instantiated. Additionally, rename * module targets */ + def addRecord(old: IsMember, newPathless: IsMember): Unit = old match { + case x: ModuleTarget => + renameMap.record(x, newPathless) + case x: IsComponent if x.path.isEmpty => + renameMap.record(x, newPathless) + case x: IsComponent => + renameMap.record(x, newPathless) + addRecord(x.stripHierarchy(1), newPathless) + } + val duplicatedParents = mutable.Set[OfModule]() targets.foreach { t => - val newTsx = dupMap.makePathless(t) - val newTs = newTsx - if(newTs.nonEmpty) { - renameMap.record(t, newTs) - val m = Target.referringModule(t) - val duplicatedModules = newTs.map(Target.referringModule) - val oldModule: Option[ModuleTarget] = m match { - case a: ModuleTarget if finalModuleSet(a.module) => Some(a) - case _ => None + val newTs = dupMap.makePathless(t) + val path = t.asPath + if (path.nonEmpty) { duplicatedParents += path(0)._2 } + newTs.toList match { + case Seq(pathless) => + val mt = Target.referringModule(pathless) + addRecord(t, pathless) + renameMap.record(Target.referringModule(t), mt) + case _ => + } + } + + def addSelfRecord(mod: IsModule): Unit = mod match { + case m: ModuleTarget => + case i: InstanceTarget if renameMap.underlying.contains(i) => + case i: InstanceTarget => + renameMap.record(i, i) + addSelfRecord(i.stripHierarchy(1)) + } + val topMod = ModuleTarget(cir.main, cir.main) + duplicatedParents.foreach { parent => + val paths = iGraph.findInstancesInHierarchy(parent.value) + val newTargets = paths.map { path => + path.tail.foldLeft(topMod: IsModule) { case (mod, wDefInst) => + mod.instOf(wDefInst.name, wDefInst.module) } - renameMap.record(m, (duplicatedModules).distinct) } + newTargets.foreach(addSelfRecord(_)) } // Return modified circuit and associated renameMap - (cir.copy(modules = finalModuleList), renameMap) + (cir.copy(modules = finalModuleList), renameMap, annos) } override def execute(state: CircuitState): CircuitState = { + val moduleNames = state.circuit.modules.map(_.name).toSet + + val (remainingAnnotations, targetsToEliminate, previouslyDeduped) = + state.annotations.foldLeft( + ( Vector.empty[Annotation], + Seq.empty[CompleteTarget], + Map.empty[IsModule, (ModuleTarget, Double)] + ) + ) { case ((remainingAnnos, targets, dedupedResult), anno) => + anno match { + case ResolvePaths(ts) => + (remainingAnnos, ts ++ targets, dedupedResult) + case DedupedResult(orig, dups, idx) if dups.nonEmpty => + (remainingAnnos, targets, dedupedResult ++ dups.map(_ -> (orig, idx)).toMap) + case other => + (remainingAnnos :+ other, targets, dedupedResult) + } + } - val (annotations, annotationsx) = state.annotations.partition{ - case a: ResolvePaths => true - case _ => false - } // Collect targets that are not local - val targets = annotations.map(_.asInstanceOf[ResolvePaths]).flatMap(_.targets.collect { case x: IsMember => x }) + val targets = targetsToEliminate.collect { case x: IsMember => x } // Check validity of paths in targets val iGraph = new InstanceGraph(state.circuit) @@ -140,7 +244,7 @@ class EliminateTargetPaths extends Transform with DependencyAPIMigration with Pr val targetsWithInvalidPaths = mutable.ArrayBuffer[IsMember]() targets.foreach { t => val path = t match { - case m: ModuleTarget => Nil + case _: ModuleTarget => Nil case i: InstanceTarget => i.asPath case r: ReferenceTarget => r.path } @@ -157,14 +261,18 @@ class EliminateTargetPaths extends Transform with DependencyAPIMigration with Pr throw NoSuchTargetException(s"""Some targets have illegal paths that cannot be resolved/eliminated: $string""") } - // get rid of path prefixes of modules with only one instance so we don't rename them + /* get rid of path prefixes of modules with only one instance so we don't rename them + * If instance targeted is in fact the only instance of a module, then it should not be renamed + * E.g. if Eliminate Target Paths on ~Top|Top/foo:Foo, but that is the only instance of Foo, then should return + * ~Top|Top/foo:Foo, not ~Top|Top/foo:Foo___Top_foo + */ val isSingleInstMod: String => Boolean = { val cache = mutable.Map.empty[String, Boolean] mod => cache.getOrElseUpdate(mod, iGraph.findInstancesInHierarchy(mod).size == 1) } val firstRenameMap = RenameMap() - val nonSingletonTargets = targets.foldLeft(Seq.empty[IsMember]) { - case (acc, t: IsComponent) if t.asPath.nonEmpty => + val nonSingletonTargets = targets.foldRight(Seq.empty[IsMember]) { + case (t: IsComponent, acc) if t.asPath.nonEmpty => val origPath = t.asPath val (singletonPrefix, rest) = origPath.partition { case (_, OfModule(mod)) => @@ -191,14 +299,14 @@ class EliminateTargetPaths extends Transform with DependencyAPIMigration with Pr } else { t +: acc } - case (acc, t) => t +: acc + case (t, acc) => t +: acc } - val (newCircuit, nextRenameMap) = run(state.circuit, nonSingletonTargets) + val (newCircuit, nextRenameMap, newAnnos) = run(state.circuit, nonSingletonTargets, iGraph) val renameMap = if (firstRenameMap.hasChanges) { - firstRenameMap andThen nextRenameMap + firstRenameMap.andThen(nextRenameMap) } else { nextRenameMap } @@ -217,6 +325,30 @@ class EliminateTargetPaths extends Transform with DependencyAPIMigration with Pr newCircuit.copy(modules = modulesx) } - state.copy(circuit = newCircuitGC, renames = Some(renameMap), annotations = annotationsx) + val renamedModuleMap = RenameMap() + + // If previous instance target mapped to a single previously deduped module, return original name + // E.g. if previously ~Top|Top/foo:Foo was deduped to ~Top|Top/foo:Bar, then + // Eliminate target paths on ~Top|Top/foo:Bar should rename to ~Top|Top/foo:Foo, not + // ~Top|Top/foo:Bar___Top_foo + val newModuleNameMapping = previouslyDeduped.flatMap { + case (current: IsModule, (orig: ModuleTarget, idx)) => + renameMap.get(current).collect { case Seq(ModuleTarget(_, m)) => m -> orig.name } + } + + val renamedCircuit = renameModules(newCircuitGC, newModuleNameMapping, renamedModuleMap) + + val reorderedCircuit = reorderModules(renamedCircuit, + previouslyDeduped.map { + case (current: IsModule, (orig: ModuleTarget, idx)) => + orig.name -> idx + } + ) + + state.copy( + circuit = reorderedCircuit, + renames = Some(renameMap.andThen(renamedModuleMap)), + annotations = remainingAnnotations ++ newAnnos + ) } } |
