diff options
| author | Jack Koenig | 2021-09-17 21:01:26 -0700 |
|---|---|---|
| committer | Jack Koenig | 2021-09-17 21:01:26 -0700 |
| commit | 5c8c19345e6711279594cf1f9ddab33623c8eba7 (patch) | |
| tree | d9d6ced3934aa4a8be3dec19ddcefe50a7a93d5a /plugin | |
| parent | e63b9667d89768e0ec6dc8a9153335cb48a213a7 (diff) | |
| parent | 958904cb2f2f65d02b2ab3ec6d9ec2e06d04e482 (diff) | |
Merge branch 'master' into 3.5-release
Diffstat (limited to 'plugin')
| -rw-r--r-- | plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala | 138 | ||||
| -rw-r--r-- | plugin/src/main/scala/chisel3/internal/plugin/ChiselComponent.scala (renamed from plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala) | 34 | ||||
| -rw-r--r-- | plugin/src/main/scala/chisel3/internal/plugin/ChiselPlugin.scala | 39 |
3 files changed, 193 insertions, 18 deletions
diff --git a/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala b/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala new file mode 100644 index 00000000..5fe63991 --- /dev/null +++ b/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.internal.plugin + +import scala.collection.mutable +import scala.tools.nsc +import scala.tools.nsc.{Global, Phase} +import scala.tools.nsc.plugins.PluginComponent +import scala.tools.nsc.symtab.Flags +import scala.tools.nsc.transform.TypingTransformers + +// TODO This component could also implement val elements in Bundles +private[plugin] class BundleComponent(val global: Global, arguments: ChiselPluginArguments) + extends PluginComponent + with TypingTransformers { + import global._ + + val phaseName: String = "chiselbundlephase" + val runsAfter: List[String] = "typer" :: Nil + def newPhase(prev: Phase): Phase = new BundlePhase(prev) + + private class BundlePhase(prev: Phase) extends StdPhase(prev) { + override def name: String = phaseName + def apply(unit: CompilationUnit): Unit = { + // This plugin doesn't work on Scala 2.11 nor Scala 3. Rather than complicate the sbt build flow, + // instead we just check the version and if its an early Scala version, the plugin does nothing + val scalaVersion = scala.util.Properties.versionNumberString.split('.') + val scalaVersionOk = scalaVersion(0).toInt == 2 && scalaVersion(1).toInt >= 12 + if (scalaVersionOk && arguments.useBundlePlugin) { + unit.body = new MyTypingTransformer(unit).transform(unit.body) + } else { + val reason = if (!scalaVersionOk) { + s"invalid Scala version '${scala.util.Properties.versionNumberString}'" + } else { + s"not enabled via '${arguments.useBundlePluginFullOpt}'" + } + // Enable this with scalacOption '-Ylog:chiselbundlephase' + global.log(s"Skipping BundleComponent on account of $reason.") + } + } + } + + private class MyTypingTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { + + def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe + + val bundleTpe = inferType(tq"chisel3.Bundle") + val dataTpe = inferType(tq"chisel3.Data") + + // Not cached because it should only be run once per class (thus once per Type) + def isBundle(sym: Symbol): Boolean = sym.tpe <:< bundleTpe + + val isDataCache = new mutable.HashMap[Type, Boolean] + // Cached because this is run on every argument to every Bundle + def isData(sym: Symbol): Boolean = isDataCache.getOrElseUpdate(sym.tpe, sym.tpe <:< dataTpe) + + def cloneTypeFull(tree: Tree): Tree = + localTyper.typed(q"chisel3.experimental.DataMirror.internal.chiselTypeClone[${tree.tpe}]($tree)") + + def isNullaryMethodNamed(name: String, defdef: DefDef): Boolean = + defdef.name.decodedName.toString == name && defdef.tparams.isEmpty && defdef.vparamss.isEmpty + + def getConstructorAndParams(body: List[Tree]): (Option[DefDef], Seq[Symbol]) = { + val paramAccessors = mutable.ListBuffer[Symbol]() + var primaryConstructor: Option[DefDef] = None + body.foreach { + case acc: ValDef if acc.symbol.isParamAccessor => + paramAccessors += acc.symbol + case con: DefDef if con.symbol.isPrimaryConstructor => + primaryConstructor = Some(con) + case d: DefDef if isNullaryMethodNamed("_cloneTypeImpl", d) => + val msg = "Users cannot override _cloneTypeImpl. Let the compiler plugin generate it. If you must, override cloneType instead." + global.globalError(d.pos, msg) + case d: DefDef if isNullaryMethodNamed("_usingPlugin", d) => + val msg = "Users cannot override _usingPlugin, it is for the compiler plugin's use only." + global.globalError(d.pos, msg) + case _ => + } + (primaryConstructor, paramAccessors.toList) + } + + + override def transform(tree: Tree): Tree = tree match { + + case bundle: ClassDef if isBundle(bundle.symbol) && !bundle.mods.hasFlag(Flag.ABSTRACT) => + + // ==================== Generate _cloneTypeImpl ==================== + val (con, params) = getConstructorAndParams(bundle.impl.body) + if (con.isEmpty) { + global.reporter.warning(bundle.pos, "Unable to determine primary constructor!") + return super.transform(tree) + } + val constructor = con.get + + val thiz = gen.mkAttributedThis(bundle.symbol) + + // The params have spaces after them (Scalac implementation detail) + val paramLookup: String => Symbol = params.map(sym => sym.name.toString.trim -> sym).toMap + + // Create a this.<ref> for each field matching order of constructor arguments + // List of Lists because we can have multiple parameter lists + val conArgs: List[List[Tree]] = + constructor.vparamss.map(_.map { vp => + val p = paramLookup(vp.name.toString) + // Make this.<ref> + val select = gen.mkAttributedSelect(thiz, p) + // Clone any Data parameters to avoid field aliasing, need full clone to include direction + if (isData(vp.symbol)) cloneTypeFull(select) else select + }) + + val tparamList = bundle.tparams.map{ t => Ident(t.symbol) } + val ttpe = if(tparamList.nonEmpty) AppliedTypeTree(Ident(bundle.symbol), tparamList) else Ident(bundle.symbol) + val newUntyped = New(ttpe, conArgs) + val neww = localTyper.typed(newUntyped) + + // Create the symbol for the method and have it be associated with the Bundle class + val cloneTypeSym = bundle.symbol.newMethod(TermName("_cloneTypeImpl"), bundle.symbol.pos.focus, Flag.OVERRIDE | Flag.PROTECTED) + // Handwritten cloneTypes don't have the Method flag set, unclear if it matters + cloneTypeSym.resetFlag(Flags.METHOD) + // Need to set the type to chisel3.Bundle for the override to work + cloneTypeSym.setInfo(NullaryMethodType(bundleTpe)) + + val cloneTypeImpl = localTyper.typed(DefDef(cloneTypeSym, neww)) + + // ==================== Generate _usingPlugin ==================== + // Unclear why quasiquotes work here but didn't for cloneTypeSym, maybe they could. + val usingPlugin = localTyper.typed(q"override protected def _usingPlugin: Boolean = true") + + val withMethods = deriveClassDef(bundle) { t => + deriveTemplate(t)(_ :+ cloneTypeImpl :+ usingPlugin) + } + + super.transform(localTyper.typed(withMethods)) + + case _ => super.transform(tree) + } + } +} diff --git a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala b/plugin/src/main/scala/chisel3/internal/plugin/ChiselComponent.scala index 59be7588..af22e6a7 100644 --- a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala +++ b/plugin/src/main/scala/chisel3/internal/plugin/ChiselComponent.scala @@ -1,23 +1,14 @@ -// See LICENSE for license details. +// SPDX-License-Identifier: Apache-2.0 package chisel3.internal.plugin -import scala.tools.nsc -import nsc.{Global, Phase} -import nsc.plugins.Plugin -import nsc.plugins.PluginComponent +import scala.collection.mutable import scala.reflect.internal.Flags +import scala.tools.nsc +import scala.tools.nsc.{Global, Phase} +import scala.tools.nsc.plugins.PluginComponent import scala.tools.nsc.transform.TypingTransformers -import scala.collection.mutable - -// The plugin to be run by the Scala compiler during compilation of Chisel code -class ChiselPlugin(val global: Global) extends Plugin { - val name = "chiselplugin" - val description = "Plugin for Chisel 3 Hardware Description Language" - val components: List[PluginComponent] = List[PluginComponent](new ChiselComponent(global)) -} - // The component of the chisel plugin. Not sure exactly what the difference is between // a Plugin and a PluginComponent. class ChiselComponent(val global: Global) extends PluginComponent with TypingTransformers { @@ -28,9 +19,10 @@ class ChiselComponent(val global: Global) extends PluginComponent with TypingTra class ChiselComponentPhase(prev: Phase) extends StdPhase(prev) { override def name: String = phaseName def apply(unit: CompilationUnit): Unit = { - // This plugin doesn't work on Scala 2.11. Rather than complicate the sbt build flow, + // This plugin doesn't work on Scala 2.11 nor Scala 3. Rather than complicate the sbt build flow, // instead we just check the version and if its an early Scala version, the plugin does nothing - if(scala.util.Properties.versionNumberString.split('.')(1).toInt >= 12) { + val scalaVersion = scala.util.Properties.versionNumberString.split('.') + if (scalaVersion(0).toInt == 2 && scalaVersion(1).toInt >= 12) { unit.body = new MyTypingTransformer(unit).transform(unit.body) } } @@ -90,6 +82,7 @@ class ChiselComponent(val global: Global) extends PluginComponent with TypingTra 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") + private val shouldMatchInstance : Type => Boolean = shouldMatchGen(tq"chisel3.experimental.hierarchy.Instance[_]") // Given a type tree, infer the type and return it private def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe @@ -176,7 +169,7 @@ class ChiselComponent(val global: Global) extends PluginComponent with TypingTra // 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 a Data and in a Bundle, just get the name but not a prefix if (shouldMatchData(tpe) && inBundle(dd)) { val str = stringFromTermName(name) val newRHS = transform(rhs) // chisel3.internal.plugin.autoNameRecursively @@ -190,12 +183,17 @@ class ChiselComponent(val global: Global) extends PluginComponent with TypingTra val prefixed = q"chisel3.experimental.prefix.apply[$tpt](name=$str)(f=$newRHS)" 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 + // If an instance, just get a name but no prefix } else if (shouldMatchModule(tpe)) { val str = stringFromTermName(name) val newRHS = transform(rhs) val named = q"chisel3.internal.plugin.autoNameRecursively($str)($newRHS)" treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) + } else if (shouldMatchInstance(tpe)) { + val str = stringFromTermName(name) + val newRHS = transform(rhs) + val named = q"chisel3.internal.plugin.autoNameRecursively($str)($newRHS)" + treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) } else { // Otherwise, continue super.transform(tree) diff --git a/plugin/src/main/scala/chisel3/internal/plugin/ChiselPlugin.scala b/plugin/src/main/scala/chisel3/internal/plugin/ChiselPlugin.scala new file mode 100644 index 00000000..23082329 --- /dev/null +++ b/plugin/src/main/scala/chisel3/internal/plugin/ChiselPlugin.scala @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.internal.plugin + +import scala.tools.nsc +import nsc.Global +import nsc.plugins.{Plugin, PluginComponent} +import scala.reflect.internal.util.NoPosition + +private[plugin] case class ChiselPluginArguments(var useBundlePlugin: Boolean = true) { + def useBundlePluginOpt = "useBundlePlugin" + def useBundlePluginFullOpt = s"-P:chiselplugin:$useBundlePluginOpt" +} + +// The plugin to be run by the Scala compiler during compilation of Chisel code +class ChiselPlugin(val global: Global) extends Plugin { + val name = "chiselplugin" + val description = "Plugin for Chisel 3 Hardware Description Language" + private val arguments = ChiselPluginArguments() + val components: List[PluginComponent] = List[PluginComponent]( + new ChiselComponent(global), + new BundleComponent(global, arguments) + ) + + override def init(options: List[String], error: String => Unit): Boolean = { + for (option <- options) { + if (option == arguments.useBundlePluginOpt) { + val msg = s"'${arguments.useBundlePluginFullOpt}' is now default behavior, you can stop using the scalacOption." + global.reporter.warning(NoPosition, msg) + } else { + error(s"Option not understood: '$option'") + } + } + true + } + + +} + |
