diff options
| author | Schuyler Eldridge | 2020-06-02 21:51:15 -0400 |
|---|---|---|
| committer | GitHub | 2020-06-03 01:51:15 +0000 |
| commit | b4192bba22c1d95daf354e900886a6690bb55e09 (patch) | |
| tree | c2a6163f94b6f7229b9a7f4ebff8d9ab60434a93 /src | |
| parent | 97a8d8249abae4a62c06c6af20e7d7da344ce689 (diff) | |
Revert: Generalize keyword collision to name manipulation, Add {Lower,Upper}CaseNames Transforms (#1651)
* Revert "Add test of {Lower, Upper}CaseNames"
This reverts commit 93c078b8469bc55cd2d33147c33e2b5421fda9d9.
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
* Revert "Add --change-name-case <lower|upper> option"
This reverts commit d3ab7e2db66fe3a63778f427dad6c08f64695ba5.
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
* Revert "Add features.{LowerCaseNames, UpperCaseNames} transforms"
This reverts commit c8dcdacf313f19a4d0238be694478a325432edd4.
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
* Revert "Refactor RemoveKeywordCollisions->ManipulateNames"
This reverts commit c534c5abae7b80a725ec9925569b3383b3c24a34.
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Diffstat (limited to 'src')
5 files changed, 206 insertions, 320 deletions
diff --git a/src/main/scala/firrtl/features/LetterCaseTransform.scala b/src/main/scala/firrtl/features/LetterCaseTransform.scala deleted file mode 100644 index 09bf6643..00000000 --- a/src/main/scala/firrtl/features/LetterCaseTransform.scala +++ /dev/null @@ -1,38 +0,0 @@ -// See LICENSE for license details. - -package firrtl.features - -import firrtl.{analyses, Namespace, passes, Transform} -import firrtl.options.Dependency -import firrtl.stage.Forms -import firrtl.transforms.ManipulateNames - -/** Parent of transforms that do change the letter case of names in a FIRRTL circuit */ -abstract class LetterCaseTransform extends ManipulateNames { - override def prerequisites = Seq(Dependency(passes.LowerTypes)) - override def optionalPrerequisites = Seq.empty - override def optionalPrerequisiteOf = Forms.LowEmitters - override def invalidates(a: Transform) = a match { - case _: analyses.GetNamespace => true - case _ => false - } - - protected def newName: String => String - - final def condition = _ => true - - final def manipulate = (a: String, ns: Namespace) => newName(a) match { - case `a` => a - case b => ns.newName(b) - } -} - -/** Convert all FIRRTL names to lowercase */ -final class LowerCaseNames extends LetterCaseTransform { - override protected def newName = (a: String) => a.toLowerCase -} - -/** Convert all FIRRTL names to UPPERCASE */ -final class UpperCaseNames extends LetterCaseTransform { - override protected def newName = (a: String) => a.toUpperCase -} diff --git a/src/main/scala/firrtl/stage/FirrtlAnnotations.scala b/src/main/scala/firrtl/stage/FirrtlAnnotations.scala index 1604e92e..5be84bb9 100644 --- a/src/main/scala/firrtl/stage/FirrtlAnnotations.scala +++ b/src/main/scala/firrtl/stage/FirrtlAnnotations.scala @@ -193,18 +193,7 @@ object RunFirrtlTransformAnnotation extends HasShellOptions { s"Unknown error when instantiating class $txName", e) }), helpText = "Run these transforms during compilation", shortOption = Some("fct"), - helpValueName = Some("<package>.<class>") ), - new ShellOption[String]( - longOption = "change-name-case", - toAnnotationSeq = _ match { - case "lower" => Seq(RunFirrtlTransformAnnotation(new firrtl.features.LowerCaseNames)) - case "upper" => Seq(RunFirrtlTransformAnnotation(new firrtl.features.UpperCaseNames)) - case a => throw new OptionsException(s"Unknown case '$a'. Did you misspell it?") - }, - helpText = "Convert all FIRRTL names to a specific case", - helpValueName = Some("<lower|upper>") - ) - ) + helpValueName = Some("<package>.<class>") ) ) } diff --git a/src/main/scala/firrtl/transforms/ManipulateNames.scala b/src/main/scala/firrtl/transforms/ManipulateNames.scala deleted file mode 100644 index 1d628881..00000000 --- a/src/main/scala/firrtl/transforms/ManipulateNames.scala +++ /dev/null @@ -1,220 +0,0 @@ -// See LICENSE for license details. - -package firrtl.transforms - -import firrtl._ -import firrtl.analyses.InstanceGraph -import firrtl.annotations.{Named, CircuitName, ModuleName, ComponentName} -import firrtl.Mappers._ -import firrtl.passes.PassException - -import scala.collection.mutable - -/** Transform for manipulate all the names in a FIRRTL circuit. */ -abstract class ManipulateNames extends Transform with DependencyAPIMigration { - - /** If true, this is a name that should be renamed, if false, the current name should be kept */ - def condition: String => Boolean - - /** A function used to manipulate a name in a FIRRTL circuit if the abstract condition is met */ - def manipulate: (String, Namespace) => String - - private type ModuleType = mutable.HashMap[String, ir.Type] - - /** 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 condition(n) => - val sn = wrap(manipulate(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) - } - - /** 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)) - } - -} diff --git a/src/main/scala/firrtl/transforms/RemoveKeywordCollisions.scala b/src/main/scala/firrtl/transforms/RemoveKeywordCollisions.scala index ca2fb453..c7ed6688 100644 --- a/src/main/scala/firrtl/transforms/RemoveKeywordCollisions.scala +++ b/src/main/scala/firrtl/transforms/RemoveKeywordCollisions.scala @@ -4,9 +4,15 @@ 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, PreservesAll} -import firrtl.passes.Uniquify + +import scala.collection.mutable /** Transform that removes collisions with reserved keywords * @param keywords a set of reserved words @@ -14,22 +20,214 @@ import firrtl.passes.Uniquify * @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 ManipulateNames { - +class RemoveKeywordCollisions(keywords: Set[String]) extends Transform with DependencyAPIMigration { + private type ModuleType = mutable.HashMap[String, ir.Type] private val inlineDelim = "_" - /** Match any strings in the keywords set */ - override def condition = keywords(_) - /** 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 * @note prefix uniqueness is not respected */ - override def manipulate = (n: String, ns: Namespace) => + 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) + } + + /** 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 */ diff --git a/src/test/scala/firrtlTests/features/LetterCaseTransformSpec.scala b/src/test/scala/firrtlTests/features/LetterCaseTransformSpec.scala deleted file mode 100644 index 93576578..00000000 --- a/src/test/scala/firrtlTests/features/LetterCaseTransformSpec.scala +++ /dev/null @@ -1,43 +0,0 @@ -// See LICENSE for license details. - -package firrtlTests.features - -import firrtl.features.{LowerCaseNames, UpperCaseNames} - -import firrtl.{CircuitState, Parser} -import firrtl.annotations.CircuitTarget - -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers - -class LetterCaseTransformSpec extends AnyFlatSpec with Matchers { - - class CircuitFixture { - private val input = - """|circuit Foo: - | module Foo: - | node Bar = UInt<1>(0) - | node baz = UInt<1>(0) - | node QUX = UInt<1>(0) - | node quuxQuux = UInt<1>(0) - | node QuuzQuuz = UInt<1>(0) - | node corge_corge = UInt<1>(0) - |""".stripMargin - val state = CircuitState(Parser.parse(input), Seq.empty) - } - - behavior of "LowerCaseNames" - - it should "change all names to lowercase" in new CircuitFixture { - val string = (new LowerCaseNames).execute(state).circuit.serialize - List("foo", "bar", "baz", "qux", "quuxquux", "quuzquuz", "corge_corge").foreach(string should include (_)) - } - - behavior of "UpperCaseNames" - - it should "change all names to uppercase" in new CircuitFixture { - val string = (new UpperCaseNames).execute(state).circuit.serialize - List("FOO", "BAR", "BAZ", "QUX", "QUUXQUUX", "QUUZQUUZ", "CORGE_CORGE").foreach(string should include (_)) - } - -} |
