summaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
authorJack Koenig2021-09-17 21:01:26 -0700
committerJack Koenig2021-09-17 21:01:26 -0700
commit5c8c19345e6711279594cf1f9ddab33623c8eba7 (patch)
treed9d6ced3934aa4a8be3dec19ddcefe50a7a93d5a /plugin
parente63b9667d89768e0ec6dc8a9153335cb48a213a7 (diff)
parent958904cb2f2f65d02b2ab3ec6d9ec2e06d04e482 (diff)
Merge branch 'master' into 3.5-release
Diffstat (limited to 'plugin')
-rw-r--r--plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala138
-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.scala39
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
+ }
+
+
+}
+