diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/scala/firrtl/transforms/ManipulateNames.scala | 408 | ||||
| -rw-r--r-- | src/main/scala/firrtl/transforms/RemoveKeywordCollisions.scala | 216 |
2 files changed, 415 insertions, 209 deletions
diff --git a/src/main/scala/firrtl/transforms/ManipulateNames.scala b/src/main/scala/firrtl/transforms/ManipulateNames.scala new file mode 100644 index 00000000..51c0a06f --- /dev/null +++ b/src/main/scala/firrtl/transforms/ManipulateNames.scala @@ -0,0 +1,408 @@ +// See LICENSE for license details. + +package firrtl.transforms + +import firrtl._ +import firrtl.analyses.InstanceGraph +import firrtl.Mappers._ + +import firrtl.annotations.{ + CircuitTarget, + CompleteTarget, + InstanceTarget, + ModuleTarget, + MultiTargetAnnotation, + ReferenceTarget, + Target, + TargetToken +} +import firrtl.options.Dependency +import firrtl.stage.Forms +import firrtl.stage.TransformManager.TransformDependency + +import scala.collection.mutable +import scala.reflect.ClassTag + +/** Base trait for annotations that control the behavior of transforms that sub-class ManipulateNames + * @see [[ManipulateNamesBlocklistAnnotation]] + * @see [[ManipulateNamesAllowlistAnnotation]] + * @define noteLocalTargets All targets must be local. Name modification in a non-local target (e.g., a node in a + * specific instance) makes no structural modification and will be ignored during deduplication. If you want this + * behavior, use a combination of a sub-class of this annotation and a [[firrtl.transforms.NoDedupAnnotation + * NoDedupAnnotation]]. + */ +sealed trait ManipulateNamesListAnnotation[A <: ManipulateNames[_]] extends MultiTargetAnnotation { + + def transform: Dependency[A] + + /* Throw an exception if targets are non-local */ + targets.flatten.collect { + case a if !a.isLocal => a + } match { + case Nil => + case a => + val aString = a.map(_.serialize).mkString("\n - ", "\n - ", "") + throw new IllegalArgumentException(s"""'${this.getClass.getName}' given non-local targets: $aString""") + } + +} + +/** Annotation to prevent name manipulation of[[firrtl.annotations.Target Target]]s in a transform that subclasses + * [[ManipulateNames]]. All listed targets will not be modified. + * + * @param targets FIRRTL IR targets to exclude from name manipulation + * @param transform the transform that this should apply to + * @tparam A a sub-type of [[ManipulateNames]] + * @throws java.lang.IllegalArgumentException if any non-local targets are given + * @note $noteLocalTargets + */ +case class ManipulateNamesBlocklistAnnotation[A <: ManipulateNames[_]]( + targets: Seq[Seq[Target]], + transform: Dependency[A]) extends ManipulateNamesListAnnotation[A] { + + override def duplicate(a: Seq[Seq[Target]]) = this.copy(targets = a) + +} + +/** Annotation to filter name manipulation to only manipulate specific [[firrtl.annotations.Target Target]]s in a + * transform that subclasses [[ManipulateNames]]. Targets will be renamed if they are not listed in a + * [[ManipulateNamesBlocklistAnnotation]] and also listed in this annotation. + * + * Not providing a [[ManipulateNamesAllowlistAnnotation]] means that all targets in a circuit may be renamed. + * + * @param targets FIRRTL IR targets to include in name manipulation + * @param transform the transform that this should apply to + * @tparam A a sub-type of [[ManipulateNames]] + * @throws java.lang.IllegalArgumentException if any non-local targets are given + * @note $noteLocalTargets + */ +case class ManipulateNamesAllowlistAnnotation[A <: ManipulateNames[_]]( + targets: Seq[Seq[Target]], + transform: Dependency[A]) extends ManipulateNamesListAnnotation[A] { + + override def duplicate(a: Seq[Seq[Target]]) = this.copy(targets = a) + +} + +/** A datastructure used to do single-pass name manipulation + * @param circuit the [[ir.Circuit]] that will be manipulated + * @param renames a rename map + * @param block a function that returns true if a [[firrtl.annotations.Target Target]] should not be renamed + * @param allow a function that returns true if a [[firrtl.annotations.Target Target]] should be renamed + */ +private class RenameDataStructure( + circuit: ir.Circuit, + val renames: RenameMap, + val block: Target => Boolean, + val allow: Target => Boolean) { + + /** A mapping of targets to associated namespaces */ + val namespaces: mutable.HashMap[CompleteTarget, Namespace] = + mutable.HashMap(CircuitTarget(circuit.main) -> Namespace(circuit)) + + /** A mapping of a reference to either an instance or a memory (encoded as a [[ReferenceTarget]] */ + val instanceMap: mutable.HashMap[ReferenceTarget, Either[ReferenceTarget, InstanceTarget]] = + new mutable.HashMap[ReferenceTarget, Either[ReferenceTarget, InstanceTarget]] { + override def apply(a: ReferenceTarget) = try { + super.apply(a) + } catch { + case t: NoSuchElementException => throw new FirrtlUserException( + s"""|Reference target '${a.serialize}' did not exist in mapping of reference targets to insts/mems. + | This is indicative of a circuit that has not been run through LowerTypes.""".stripMargin, t) + } + } + + /** Return true if a target should be skipped based on allow and block parameters */ + def skip(a: Target): Boolean = block(a) || !allow(a) + +} + +/** Transform for manipulate all the names in a FIRRTL circuit. + * @tparam A the type of the child transform + */ +abstract class ManipulateNames[A <: ManipulateNames[_] : ClassTag] extends Transform with DependencyAPIMigration { + + /** A function used to manipulate a name in a FIRRTL circuit */ + def manipulate: (String, Namespace) => Option[String] + + override def prerequisites: Seq[TransformDependency] = Seq(Dependency(firrtl.passes.LowerTypes)) + override def optionalPrerequisites: Seq[TransformDependency] = Seq.empty + override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters + override def invalidates(a: Transform) = a match { + case _: analyses.GetNamespace => true + case _ => false + } + + /** Compute a new name for some target and record the rename if the new name differs. If the top module or the circuit + * is renamed, both will be renamed. + * @param name the string to rename + * @param r a data structure containing information necessary for renaming + * @param target the target associated with the name + * @return a new name (or the old name if no renaming is necessary) + */ + private def doRename(name: String, r: RenameDataStructure, target: CompleteTarget): String = { + /* Compute the new name and, if the name is a new name, a new target. */ + val (namex: String, ax: Option[CompleteTarget]) = target match { + /* Do not rename if this is designated as a skip */ + case a if r.skip(a) => + (name, None) + /* Circuit renaming */ + case a@ CircuitTarget(b) => manipulate(b, r.namespaces(a)) match { + case Some(str) => (str, Some(a.copy(circuit = str))) + case None => (b, None) + } + /* Module renaming for non-top modules */ + case a@ ModuleTarget(_, b) => manipulate(b, r.namespaces(a.circuitTarget)) match { + case Some(str) => (str, Some(a.copy(module = str))) + case None => (b, None) + } + /* Instance renaming */ + case a@ InstanceTarget(_, _, Nil, b, c) => manipulate(b, r.namespaces(a.moduleTarget)) match { + case Some(str) => (str, Some(a.copy(instance = str))) + case None => (b, None) + } + /* Rename either a module component or a memory */ + case a@ ReferenceTarget(_, _, _, b, Nil) => manipulate(b, r.namespaces(a.moduleTarget)) match { + case Some(str) => (str, Some(a.copy(ref = str))) + case None => (b, None) + } + /* Rename an instance port or a memory reader/writer/readwriter */ + case a@ ReferenceTarget(_, _, _, b, (token@ TargetToken.Field(c)) :: Nil) => + val ref = r.instanceMap(a.moduleTarget.ref(b)) match { + case Right(inst) => inst.ofModuleTarget + case Left(mem) => mem + } + manipulate(c, r.namespaces(ref)) match { + case Some(str) => (str, Some(a.copy(component = Seq(token.copy(str))))) + case None => (c, None) + } + } + /* Record the optional rename. If the circuit was renamed, also rename the top module. If the top module was + * renamed, also rename the circuit. */ + ax.foreach( + axx => target match { + case c: CircuitTarget => + r.renames.rename(target, r.renames(axx)) + r.renames.rename(c.module(c.circuit), CircuitTarget(namex).module(namex)) + /* Note: this code path is not exercised by the implementation of the [[run]] and [[onModule]] methods. Those + * only use [[doRename]] on the circuit and [[maybeRename]] on the top module. + */ + case m: ModuleTarget if m.module == m.circuit => + r.renames.rename(target, r.renames(axx)) + r.renames.rename(m.circuitTarget, axx.circuitTarget) + case _ => + r.renames.rename(target, r.renames(axx)) + } + ) + namex + } + + /** Change a name based on known renames. Do not record any new renames. + * @param name the string to rename + * @param r a data structure containing information necessary for renaming + * @param target the target associated with the name + * @return a new name (or the old name if no renaming is necessary) + */ + private def maybeRename(name: String, r: RenameDataStructure, t: CompleteTarget): String = + r.renames.underlying.get(t) match { + case Some(ax) if ax.size == 1 => + ax match { + case Seq(foo: CircuitTarget) => foo.name + case Seq(foo: ModuleTarget) => foo.module + case Seq(foo: InstanceTarget) => foo.instance + case Seq(foo: ReferenceTarget) => foo.tokens.last match { + case TargetToken.Ref(value) => value + case TargetToken.Field(value) => value + case _ => Utils.throwInternalError( + s"""|Reference target '${t.serialize}'must end in 'Ref' or 'Field' + | This is indicative of a circuit that has not been run through LowerTypes.""", + Some(new MatchError(foo.serialize))) + } + } + case s@ Some(ax) => Utils.throwInternalError( + s"""Found multiple renames '${t}' -> [${ax.map(_.serialize).mkString(",")}]. This should be impossible.""", + Some(new MatchError(s))) + case None => name + } + + /** Rename an expression + * + * This logic exploits the known structure of the output of [[LowerTypes]] such that the only possible expressions in + * a module are: (1) references to module components, (2) subfields of references are instance components, and (3) + * subfields of subfields or references are memory ports. + */ + private def onExpression(e: ir.Expression, r: RenameDataStructure, t: ModuleTarget): ir.Expression = e match { + /* A reference to something inside this module */ + case w: WRef => w.copy(name = maybeRename(w.name, r, Target.asTarget(t)(w))) + /* This is either the subfield of an instance or a subfield of a memory reader/writer/readwriter */ + case w@ WSubField(expr, ref, _, _) => expr match { + /* This is an instance */ + case we@ WRef(inst, _, _, _) => + val tx = Target.asTarget(t)(we) + val (rTarget: ReferenceTarget, iTarget: InstanceTarget) = r.instanceMap(tx) match { + case Right(a) => (a.ofModuleTarget.ref(ref), a) + case a@ Left(ref) => throw new FirrtlUserException( + s"""|Unexpected '${ref.serialize}' in instanceMap for key '${tx.serialize}' on expression '${w.serialize}'. + | This is indicative of a circuit that has not been run through LowerTypes.""", new MatchError(a)) + } + w.copy(we.copy(name=maybeRename(inst, r, iTarget)), name=maybeRename(ref, r, rTarget)) + /* This is a reader/writer/readwriter */ + case ws@ WSubField(expr, port, _, _) => expr match { + /* This is the memory. */ + case wr@ WRef(mem, _, _, _) => + w.copy( + expr=ws.copy( + expr=wr.copy(name=maybeRename(mem, r, t.ref(mem))), + name=maybeRename(port, r, t.ref(mem).field(port)))) + } + } + case e => e.map(onExpression(_: ir.Expression, r, t)) + } + + /** Rename a statement + * + * Instances will update the rename data structure. Memories are treated specially to rename their readers, writers, + * and readwriters. + */ + private def onStatement(s: ir.Statement, r: RenameDataStructure, t: ModuleTarget): ir.Statement = s match { + case decl: ir.IsDeclaration => decl match { + case decl@ WDefInstance(_, inst, mod, _) => + val modx = maybeRename(mod, r, t.circuitTarget.module(mod)) + val instx = doRename(inst, r, t.instOf(inst, mod)) + r.instanceMap(t.ref(inst)) = Right(t.instOf(inst, mod)) + decl.copy(name = instx, module = modx) + case decl: ir.DefMemory => + val namex = doRename(decl.name, r, t.ref(decl.name)) + val tx = t.ref(decl.name) + r.namespaces(tx) = Namespace(decl.readers ++ decl.writers ++ decl.readwriters) + r.instanceMap(tx) = Left(tx) + decl + .copy( + name = namex, + readers = decl.readers.map(_r => doRename(_r, r, tx.field(_r))), + writers = decl.writers.map(_w => doRename(_w, r, tx.field(_w))), + readwriters = decl.readwriters.map(_rw => doRename(_rw, r, tx.field(_rw))) + ) + .map(onExpression(_: ir.Expression, r, t)) + case decl => + decl + .map(doRename(_: String, r, t.ref(decl.name))) + .map(onExpression(_: ir.Expression, r, t)) + } + case s => + s + .map(onStatement(_: ir.Statement, r, t)) + .map(onExpression(_: ir.Expression, r, t)) + } + + /** Rename a port */ + private def onPort(p: ir.Port, r: RenameDataStructure, t: ModuleTarget): ir.Port = { + p.map(doRename(_: String, r, t.ref(p.name))) + } + + /** Rename a [[DefModule]] and it's internals (ports and statements) to fix keyword collisions and update instance + * references to respect previous renames + * @param renames a [[RenameMap]] + * @param circuit the enclosing [[CircuitName]] + * @return a [[DefModule]] without keyword conflicts + */ + private def onModule(m: ir.DefModule, r: RenameDataStructure, t: CircuitTarget): ir.DefModule = m match { + case _: ir.ExtModule => m + case ir.Module(_, main, _, _) => + val moduleTarget = t.module(m.name) + r.namespaces(moduleTarget) = Namespace(m) + + /* If top module, use [[maybeRename]]: circuit renaming already recorded a top-module rename if one should happen. + * Otherwise, use [[doRename]]: compute a new name and record it. + */ + val onName: String => String = t.circuit match { + case `main` => maybeRename(_, r, moduleTarget) + case _ => doRename(_, r, moduleTarget) + } + + m + .map(onName) + .map(onPort(_: ir.Port, r, moduleTarget)) + .map(onStatement(_: ir.Statement, r, moduleTarget)) + } + + /** Manipulate all names in a circuit + * + * @param c an input circuit + * @param renames a rename map that will be updated as names are manipulated + * @param block a function that returns true if a [[firrtl.annotations.Target Target]] should not be renamed + * @param allow a function that returns true if a [[firrtl.annotations.Target Target]] should be renamed + * @return the circuit with manipulated names + */ + def run( + c: ir.Circuit, + renames: RenameMap, + block: Target => Boolean, + allow: Target => Boolean) + : ir.Circuit = { + val t = CircuitTarget(c.main) + + /* If the circuit is a skip, return the original circuit. Otherwise, walk all the modules and rename them. Rename the + * circuit if the main module was renamed. + */ + (block(t), !allow(t)) match { + case (true, _) => + logger.info(s"Circuit '${t.serialize}' is excluded by the 'block' parameter. No renaming will occur.") + c + case (false, true) => + logger.info(s"Circuit '${t.serialize}' is not included by the 'allow' parameter. No renaming will occur.") + c + case _ => + val r = new RenameDataStructure(c, renames, block, allow) + + /* Record a rename for the circuit if the top module will be renamed. This allows all the module renames to be + * aware of the circuit rename when generating their own renames. E.g., this allows renames to be generated + * that can be resolved in a single step: + * ~foo -> FOO + * ~foo|bar -> ~FOO|BAR + * Instead of renames which require multiple steps: + * ~foo -> FOO + * ~foo|bar -> ~foo|BAR + */ + val mainx = r.skip(t.module(c.main)) match { + case true => c.main + case false => + val tx = CircuitTarget(doRename(c.main, r, t)) + logger.info(s"Main module will be renamed. Renaming circuit: '${t.serialize}' -> ['${tx.serialize}']") + renames.record(t, tx) + tx.circuit + } + + /* Rename all modules from leafs to root in one pass while updating a shared rename map. Going from leafs to + * roots ensures that the rename map is safe for parents to blindly consult. Store this in mapping of old module + * target to new module to allow the modules to be put in the old order. + */ + val modulesx: Map[ModuleTarget, ir.DefModule] = new InstanceGraph(c).moduleOrder.reverse + .map(m => t.module(m.name) -> onModule(m, r, t)) + .toMap + + /* Replace the old modules making sure that they are still in the same order */ + c.copy(modules = c.modules.map(m => modulesx(t.module(m.name))), + main = mainx) + } + } + + /** Return a circuit state with all sensitive names manipulated */ + def execute(state: CircuitState): CircuitState = { + + val block = state.annotations.collect { + case ManipulateNamesBlocklistAnnotation(targetSeq, _: Dependency[A]) => targetSeq + }.flatten.flatten.toSet + + val allow = state.annotations.collect { + case ManipulateNamesAllowlistAnnotation(targetSeq, _: Dependency[A]) => targetSeq + } match { + case Nil => (a: Target) => true + case a => a.flatten.flatten.toSet + } + + val renames = RenameMap() + state.copy(circuit = run(state.circuit, renames, block, allow), renames = Some(renames)) + } + +} diff --git a/src/main/scala/firrtl/transforms/RemoveKeywordCollisions.scala b/src/main/scala/firrtl/transforms/RemoveKeywordCollisions.scala index c5f20363..840a3d99 100644 --- a/src/main/scala/firrtl/transforms/RemoveKeywordCollisions.scala +++ b/src/main/scala/firrtl/transforms/RemoveKeywordCollisions.scala @@ -4,230 +4,28 @@ package firrtl.transforms import firrtl._ -import firrtl.analyses.InstanceGraph -import firrtl.annotations.{Named, CircuitName, ModuleName, ComponentName} -import firrtl.ir -import firrtl.passes.{Uniquify, PassException} import firrtl.Utils.v_keywords -import firrtl.Mappers._ import firrtl.options.Dependency - -import scala.collection.mutable +import firrtl.passes.Uniquify /** Transform that removes collisions with reserved keywords * @param keywords a set of reserved words - * @define implicitRename @param renames the [[RenameMap]] to query when renaming - * @define implicitNamespace @param ns an encolosing [[Namespace]] with which new names must not conflict - * @define implicitScope @param scope the enclosing scope of this name. If [[None]], then this is a [[Circuit]] name */ -class RemoveKeywordCollisions(keywords: Set[String]) extends Transform with DependencyAPIMigration { - private type ModuleType = mutable.HashMap[String, ir.Type] +class RemoveKeywordCollisions(keywords: Set[String]) extends ManipulateNames { + private val inlineDelim = "_" /** Generate a new name, by appending underscores, that will not conflict with the existing namespace * @param n a name * @param ns a [[Namespace]] - * @return a conflict-free name + * @return Some name if a rename occurred, None otherwise * @note prefix uniqueness is not respected */ - private def safeName(n: String, ns: Namespace): String = - Uniquify.findValidPrefix(n + inlineDelim, Seq(""), ns.cloneUnderlying ++ keywords) - - /** Modify a name to not conflict with a Verilog keywords while respecting existing renames and a namespace - * @param n the name to rename - * @param renames the [[RenameMap]] to query when renaming - * $implicitRename - * $implicitNamespace - * $implicitScope - * @return a name without keyword conflicts - */ - private def onName(n: String)(implicit renames: RenameMap, ns: Namespace, scope: Option[Named]): String = { - - // Convert a [[String]] into [[Named]] based on the provided scope. - def wrap(name: String, scope: Option[Named]): Named = scope match { - case None => CircuitName(name) - case Some(cir: CircuitName) => ModuleName(name, cir) - case Some(mod: ModuleName) => ComponentName(name, mod) - case Some(com: ComponentName) => ComponentName(s"${com.name}.$name", com.module) - } - - val named = wrap(n, scope) - - // If this has already been renamed use that name. If it conflicts with a keyword, determine a new, safe name and - // update the renames. Otherwise, leave it alone. - val namedx: Seq[Named] = renames.get(named) match { - case Some(x) => x - case None if keywords(n) => - val sn = wrap(safeName(n, ns), scope) - renames.rename(named, sn) - Seq(sn) - case _ => Seq(wrap(n, scope)) - } - - namedx match { - case Seq(ComponentName(n, _)) => n - case Seq(ModuleName(n, _)) => n - case Seq(CircuitName(n)) => n - case x => throw new PassException( - s"Verilog renaming shouldn't result in multiple renames, but found '$named -> $namedx'") - } - } - - /** Rename the fields of a [[Type]] to match the ports of an instance - * @param t the type to rename - * $implicitRename - * $implicitNamespace - * $implicitScope - * @return a [[Type]] with updated names - * @note This is not intended for fixing arbitrary types, only [[BundleType]] in instance [[WRef]]s - */ - private def onType(t: ir.Type) - (implicit renames: RenameMap, - ns: Namespace, - scope: Option[ModuleName]): ir.Type = t match { - case b: ir.BundleType => b.copy(fields = b.fields.map(f => f.copy(name = onName(f.name)))) - case _ => t - } - - /** Rename an [[Expression]] to respect existing renames and avoid keyword collisions - * @param e the [[Expression]] to rename - * $implicitRename - * $implicitNamespace - * $implicitScope - * @return an [[Expression]] without keyword conflicts - */ - private def onExpression(e: ir.Expression) - (implicit renames: RenameMap, - ns: Namespace, - scope: Option[ModuleName], - iToM: mutable.Map[ComponentName, ModuleName], - modType: ModuleType): ir.Expression = e match { - case wsf@ WSubField(wr@ WRef(name, _, InstanceKind, _), port, _, _) => - val subInst = ComponentName(name, scope.get) - val subModule = iToM(subInst) - val subPort = ComponentName(port, subModule) - - val wrx = wr.copy( - name = renames.get(subInst).orElse(Some(Seq(subInst))).get.head.name, - tpe = modType(subModule.name)) - - wsf.copy( - expr = wrx, - name = renames.get(subPort).orElse(Some(Seq(subPort))).get.head.name) - case wr: WRef => wr.copy(name=onName(wr.name)) - case ex => ex.map(onExpression) - } - - /** Rename a [[Statement]] to respect existing renames and avoid keyword collisions - * $implicitRename - * $implicitNamespace - * $implicitScope - * @return a [[Statement]] without keyword conflicts - */ - private def onStatement(s: ir.Statement) - (implicit renames: RenameMap, - ns: Namespace, - scope: Option[ModuleName], - iToM: mutable.Map[ComponentName, ModuleName], - modType: ModuleType): ir.Statement = s match { - case wdi: WDefInstance => - val subModule = ModuleName(wdi.module, scope.get.circuit) - val modulex = renames.get(subModule).orElse(Some(Seq(subModule))).get.head.name - val wdix = wdi.copy(module = modulex, - name = onName(wdi.name), - tpe = onType(wdi.tpe)(renames, ns, Some(ModuleName(modulex, scope.get.circuit)))) - iToM(ComponentName(wdi.name, scope.get)) = ModuleName(wdix.module, scope.get.circuit) - wdix - case _ => s - .map(onStatement) - .map(onExpression) - .map(onName) - } - - /** Rename a [[Port]] to avoid keyword collisions - * $implicitRename - * $implicitNamespace - * $implicitScope - * @return a [[Port]] without keyword conflicts - */ - private def onPort(p: ir.Port)(implicit renames: RenameMap, ns: Namespace, scope: Option[ModuleName]): ir.Port = - p.copy(name = onName(p.name)) - - /** Rename a [[DefModule]] and it's internals (ports and statements) to fix keyword collisions and update instance - * references to respect previous renames - * @param renames a [[RenameMap]] - * @param circuit the enclosing [[CircuitName]] - * @return a [[DefModule]] without keyword conflicts - */ - private def onModule(renames: RenameMap, - circuit: CircuitName, - modType: ModuleType) - (m: ir.DefModule): ir.DefModule = { - implicit val moduleNamespace: Namespace = Namespace(m) - implicit val scope: Option[ModuleName] = Some(ModuleName(m.name, circuit)) - implicit val r: RenameMap = renames - implicit val mType: ModuleType = modType - - // Store local renames of refs to instances to their renamed modules. This is needed when renaming port connections - // on subfields where only the local instance name is available. - implicit val iToM: mutable.Map[ComponentName, ModuleName] = mutable.Map.empty - - val mx = m - .map(onPort) - .map(onStatement) - .map(onName(_: String)(renames, moduleNamespace, Some(circuit))) - - // Must happen after renaming the name and ports of the module itself - mType += (mx.name -> onType(Utils.module_type(mx))) - mx - } - - /** Fix any Verilog keyword collisions in a [[firrtl.ir Circuit]] - * @param c a [[firrtl.ir Circuit]] with possible name collisions - * @param renames a [[RenameMap]] to update. If you don't want to propagate renames, this can be ignored. - * @return a [[firrtl.ir Circuit]] without keyword conflicts - */ - def run(c: ir.Circuit, renames: RenameMap = RenameMap()): ir.Circuit = { - implicit val circuitNamespace: Namespace = Namespace(c) - implicit val scope: Option[CircuitName] = Some(CircuitName(c.main)) - val modType: ModuleType = new ModuleType() - - // Rename all modules from leafs to root in one pass while updating a shared rename map. Going from leafs to roots - // ensures that the rename map is safe for parents to blindly consult. - val modulesx: Map[ModuleName, Seq[ir.DefModule]] = new InstanceGraph(c).moduleOrder.reverse - .map(onModule(renames, scope.get, modType)) - .groupBy(m => ModuleName(m.name, scope.get)) - - // Reorder the renamed modules into the original circuit order. - val modulesxx: Seq[ir.DefModule] = c.modules.flatMap{ orig => - val named = ModuleName(orig.name, scope.get) - modulesx(renames.get(named).orElse(Some(Seq(named))).get.head) - } - - // Rename the circuit if the top module was renamed - val mainx = renames.get(ModuleName(c.main, CircuitName(c.main))) match { - case Some(Seq(ModuleName(m, _))) => - renames.rename(CircuitName(c.main), CircuitName(m)) - m - case x@ Some(_) => throw new PassException( - s"Verilog renaming shouldn't result in multiple renames, but found '${c.main} -> $x'") - case None => - c.main - } - - // Apply all updates - c.copy(modules = modulesxx, main = mainx) + override def manipulate = (n: String, ns: Namespace) => keywords.contains(n) match { + case true => Some(Uniquify.findValidPrefix(n + inlineDelim, Seq(""), ns.cloneUnderlying ++ keywords)) + case false => None } - /** Fix any Verilog keyword name collisions in a [[CircuitState]] while propagating renames - * @param state the [[CircuitState]] with possible name collisions - * @return a [[CircuitState]] without name collisions - */ - def execute(state: CircuitState): CircuitState = { - val renames = RenameMap() - renames.setCircuit(state.circuit.main) - state.copy(circuit = run(state.circuit, renames), renames = Some(renames)) - } } /** Transform that removes collisions with Verilog keywords */ |
