diff options
| author | Jack Koenig | 2020-11-10 18:40:51 -0800 |
|---|---|---|
| committer | GitHub | 2020-11-11 02:40:51 +0000 |
| commit | 1260f7c89f1b95bdb00e56e49edb73dc2eac3a0e (patch) | |
| tree | 8f349d91946fd43b7bf0d8ed9987404c0a49b7a1 /plugin/src | |
| parent | 8187318e7aef42d541ce307f93d9fc946ed4c38d (diff) | |
Refine autonaming to have more intuitive behavior (#1660)
* Refine autonaming to have more intuitive behavior
Last name in an Expression wins, while the first Statement to name wins.
This is done via checking the _id of HasIds during autonaming and only
applying a name if the HasId was created in the scope of autonaming.
There is no change to .autoSeed or .suggestName behavior.
Behavior of chisel3-plugins from before this change is maintained.
* Update docs with naming plugin changes
Diffstat (limited to 'plugin/src')
| -rw-r--r-- | plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala | 85 |
1 files changed, 73 insertions, 12 deletions
diff --git a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala b/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala index 9f90ba16..59be7588 100644 --- a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala +++ b/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala @@ -39,7 +39,7 @@ class ChiselComponent(val global: Global) extends PluginComponent with TypingTra class MyTypingTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { - private def shouldMatchGen(bases: Tree*): ValDef => Boolean = { + private def shouldMatchGen(bases: Tree*): Type => Boolean = { val cache = mutable.HashMap.empty[Type, Boolean] val baseTypes = bases.map(inferType) @@ -72,8 +72,7 @@ class ChiselComponent(val global: Global) extends PluginComponent with TypingTra } // Return function so that it captures the cache - { p: ValDef => - val q = inferType(p.tpt) + { q: Type => cache.getOrElseUpdate(q, { // First check if a match, then check early exit, then recurse if(terminate(q)){ @@ -87,9 +86,10 @@ class ChiselComponent(val global: Global) extends PluginComponent with TypingTra } } - private val shouldMatchData : ValDef => Boolean = shouldMatchGen(tq"chisel3.Data") - private val shouldMatchDataOrMem : ValDef => Boolean = shouldMatchGen(tq"chisel3.Data", tq"chisel3.MemBase[_]") - private val shouldMatchModule : ValDef => Boolean = shouldMatchGen(tq"chisel3.experimental.BaseModule") + + private val shouldMatchData : Type => Boolean = shouldMatchGen(tq"chisel3.Data") + private val shouldMatchDataOrMem : Type => Boolean = shouldMatchGen(tq"chisel3.Data", tq"chisel3.MemBase[_]") + private val shouldMatchModule : Type => Boolean = shouldMatchGen(tq"chisel3.experimental.BaseModule") // Given a type tree, infer the type and return it private def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe @@ -118,6 +118,50 @@ class ChiselComponent(val global: Global) extends PluginComponent with TypingTra okFlags(dd.mods) && !isNull && dd.rhs != EmptyTree } + // TODO Unify with okVal + private def okUnapply(dd: ValDef): Boolean = { + + // These were found through trial and error + def okFlags(mods: Modifiers): Boolean = { + val badFlags = Set( + Flag.PARAM, + Flag.DEFERRED, + Flags.TRIEDCOOKING, + Flags.CASEACCESSOR, + Flags.PARAMACCESSOR + ) + val goodFlags = Set( + Flag.SYNTHETIC, + Flag.ARTIFACT + ) + goodFlags.forall(f => mods.hasFlag(f)) && badFlags.forall(f => !mods.hasFlag(f)) + } + + // Ensure expression isn't null, as you can't call `null.autoName("myname")` + val isNull = dd.rhs match { + case Literal(Constant(null)) => true + case _ => false + } + val tpe = inferType(dd.tpt) + definitions.isTupleType(tpe) && okFlags(dd.mods) && !isNull && dd.rhs != EmptyTree + } + + private def findUnapplyNames(tree: Tree): Option[List[String]] = { + val applyArgs: Option[List[Tree]] = tree match { + case Match(_, List(CaseDef(_, _, Apply(_, args)))) => Some(args) + case _ => None + } + applyArgs.flatMap { args => + var ok = true + val result = mutable.ListBuffer[String]() + args.foreach { + case Ident(TermName(name)) => result += name + // Anything unexpected and we abort + case _ => ok = false + } + if (ok) Some(result.toList) else None + } + } // Whether this val is directly enclosed by a Bundle type private def inBundle(dd: ValDef): Boolean = { @@ -131,30 +175,47 @@ class ChiselComponent(val global: Global) extends PluginComponent with TypingTra override def transform(tree: Tree): Tree = tree match { // Check if a subtree is a candidate case dd @ ValDef(mods, name, tpt, rhs) if okVal(dd) => + val tpe = inferType(tpt) // If a Data and in a Bundle, just get the name but not a prefix - if (shouldMatchData(dd) && inBundle(dd)) { + if (shouldMatchData(tpe) && inBundle(dd)) { val str = stringFromTermName(name) val newRHS = transform(rhs) // chisel3.internal.plugin.autoNameRecursively - val named = q"chisel3.internal.plugin.autoNameRecursively($str, $newRHS)" + val named = q"chisel3.internal.plugin.autoNameRecursively($str)($newRHS)" treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) } // If a Data or a Memory, get the name and a prefix - else if (shouldMatchDataOrMem(dd)) { + else if (shouldMatchDataOrMem(tpe)) { val str = stringFromTermName(name) val newRHS = transform(rhs) val prefixed = q"chisel3.experimental.prefix.apply[$tpt](name=$str)(f=$newRHS)" - val named = q"chisel3.internal.plugin.autoNameRecursively($str, $prefixed)" + val named = q"chisel3.internal.plugin.autoNameRecursively($str)($prefixed)" treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) // If an instance, just get a name but no prefix - } else if (shouldMatchModule(dd)) { + } else if (shouldMatchModule(tpe)) { val str = stringFromTermName(name) val newRHS = transform(rhs) - val named = q"chisel3.internal.plugin.autoNameRecursively($str, $newRHS)" + val named = q"chisel3.internal.plugin.autoNameRecursively($str)($newRHS)" treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) } else { // Otherwise, continue super.transform(tree) } + case dd @ ValDef(mods, name, tpt, rhs @ Match(_, _)) if okUnapply(dd) => + val tpe = inferType(tpt) + val fieldsOfInterest: List[Boolean] = tpe.typeArgs.map(shouldMatchData) + // Only transform if at least one field is of interest + if (fieldsOfInterest.reduce(_ || _)) { + findUnapplyNames(rhs) match { + case Some(names) => + val onames: List[Option[String]] = fieldsOfInterest.zip(names).map { case (ok, name) => if (ok) Some(name) else None } + val named = q"chisel3.internal.plugin.autoNameRecursivelyProduct($onames)($rhs)" + treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) + case None => // It's not clear how this could happen but we don't want to crash + super.transform(tree) + } + } else { + super.transform(tree) + } // Otherwise, continue case _ => super.transform(tree) } |
