Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refine autonaming to have more intuitive behavior #1660

Merged
merged 2 commits into from
Nov 11, 2020
Merged

Conversation

jackkoenig
Copy link
Contributor

@jackkoenig jackkoenig commented Nov 6, 2020

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. More precisely, assuming this PR is released with Chisel v3.4.1, if you use Chisel v3.4.1 with the v3.4.0 (or v3.4.0-RC3) plugin, you'll get the v3.4.0 behavior.

The bulk of the work here was making unapply continue to work because it previously worked via overriding names. Now, a signal will only be named by autoNameRecursively or autoNameRecursivelyProduct calls that wrap the Expression within which the Data is created.

Fixes #1603

Contributor Checklist

  • Did you add Scaladoc to every public function/method?
  • Did you add at least one test demonstrating the PR?
  • Did you delete any extraneous printlns/debugging code?
  • Did you specify the type of improvement?
  • Did you add appropriate documentation in docs/src?
  • Did you state the API impact?
  • Did you specify the code generation impact?
  • Did you request a desired merge strategy?
  • Did you add text to be included in the Release Notes for this change?

Type of Improvement

  • bug fix
  • new feature/API

API Impact

This changes the naming behavior to what I've been calling "Outer Expression, First Statement" wins. For example:

  val width: Option[Int] = Some(8)
  val debug = width.map { w =>
    val foo = IO(Output(UInt(w.W))) // name: "debug_foo"
    val delay = RegNext(in) // name: "debug_delay"
    foo := delay
    foo
  } // name: "debug"
  val bar = debug // name: "bar"

Intuitively, the name of this port should be debug. It is useful that things built within the anonymous function should have debug as a prefix in addition to their val name (eg. debug_delay), but because foo is returned, the outer name should win (debug). It is also intuitive that debug wins over bar since debug is the name of the declaration rather than an alias.

Prior to this PR, the port would be named debug_foo and if it were a non-port, it would be named bar. This PR unifies the behavior of ports and non-ports into what is (in my opinion) more intuitive.

Backend Code Generation Impact

Change in naming algorithm

Desired Merge Strategy

  • Squash

Release Notes

Change naming priority to "Outer Expression, First Statement"

Reviewer Checklist (only modified by reviewer)

  • Did you add the appropriate labels?
  • Did you mark the proper milestone (3.2.x, 3.3.x, 3.4.0, 3.5.0) ?
  • Did you review?
  • Did you check whether all relevant Contributor checkboxes have been checked?
  • Did you mark as Please Merge?

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.
@jackkoenig jackkoenig added this to the 3.4.x milestone Nov 6, 2020
@jackkoenig jackkoenig requested a review from azidar November 6, 2020 23:37
@jackkoenig jackkoenig requested a review from a team as a code owner November 6, 2020 23:37
@mwachs5
Copy link
Contributor

mwachs5 commented Nov 7, 2020

👍 for the algorithm/behavior as documented in the comments

@jackkoenig
Copy link
Contributor Author

Marked DO NOT MERGE because I need to write docs and I'm seeing a handful of cases of worse names that I wouldn't expect so I need to dig into it.

@jackkoenig
Copy link
Contributor Author

jackkoenig commented Nov 10, 2020

I'm seeing a handful of cases of worse names that I wouldn't expect

In a randomish sampling, I found this behavior to be correct. In some cases a temporary name would get overridden to a better, non-temporary name; however, it could also result in overriding names in a bad way. For example:

class Example extends MultiIOModule {
  def extractBelow(from: UInt, idx: Int): UInt = {
    val x = from
    x(idx, 0)
  }
  
  val in  = IO(Input(UInt(16.W)))
  val out = IO(Output(UInt(16.W)))
  // If foo is a field of Example, reflective naming hides the bug
  if (true) {
    val foo = Reg(UInt(16.W))
    foo := in
    out := extractBelow(foo, 15)
  }
}

With v3.4.0 gives:

circuit Example :
  module Example :
    input clock : Clock
    input reset : UInt<1>
    input in : UInt<16>
    output out : UInt<16>
    reg out_x : UInt<16>, clock with :
      reset => (UInt<1>("h0"), out_x)
    out_x <= in
    node _out_T = bits(out_x, 15, 0)
    out <= _out_T

(Scastie: https://scastie.scala-lang.org/T6mdGw3LQOSNc2yEyUMy5w)

See how the val x in the utility function (with prefix out from the :=) overrides the name? In this case, it's clearly not desirable. That being said, in other cases, this change turns some good names into temporaries. For example, a slight modification on the above:

class Example extends MultiIOModule {
  def extractBelow(from: UInt, idx: Int): UInt = {
    val x = from
    x(idx, 0)
  }
  
  val in  = IO(Input(UInt(16.W)))
  val out = IO(Output(UInt(16.W)))
  // If foo is a field of Example, reflective naming hides the bug
  if (true) {
    val foo = Reg(UInt(16.W))
    foo := in
    out := extractBelow(foo +& 1.U, 15) // <---- difference here
  }
}

With v3.4.0 gives:

circuit Example :
  module Example :
    input clock : Clock
    input reset : UInt<1>
    input in : UInt<16>
    output out : UInt<16>
    reg foo : UInt<16>, clock with :
      reset => (UInt<1>("h0"), foo)
    foo <= in
    node out_x = add(foo, UInt<1>("h1"))
    node _out_T = bits(out_x, 15, 0)
    out <= _out_T

(Scastie: https://scastie.scala-lang.org/3FoJCFOmRUK5iYteYkkvjg)

In this case, it's nice that the sum gets the name out_x. With the change in this PR, instead you get:

circuit Example :
  module Example :
    input clock : Clock
    input reset : UInt<1>
    input in : UInt<16>
    output out : UInt<16>
    reg foo : UInt<16>, clock with :
      reset => (UInt<1>("h0"), foo)
    foo <= in
    node _out_T = add(foo, UInt<1>("h1"))
    node _out_T_1 = bits(_out_T, 15, 0)
    out <= _out_T_1

The loss of out_x is a bit unfortunate, but one could argue that this is more predicable since the Expression at the callsite does not have a name. If we prioritize predictability over "best possible names" then I think this is actually good.

@jackkoenig
Copy link
Contributor Author

@azidar I've done an initial updating of the docs, but I have not yet added a description of "Outer Expression, First Statement". I would appreciate review of the code and tests while I finish up the docs.

private [chisel3] def autoSeed(seed: String): this.type = {
private[chisel3] def autoSeed(seed: String): this.type = forceAutoSeed(seed)
// Bypass the overridden behavior of autoSeed in [[Data]], apply autoSeed even to ports
private[chisel3] def forceAutoSeed(seed: String): this.type = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the plan to remove autoseed in the future?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly, a good question for the dev meeting

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(_ || _)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this never empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If none of the fields are <: Data then it will be empty, eg.

val (a, b) = ("hi", 3)

@jackkoenig jackkoenig added the Please Merge Accepted PRs that are ready to be merged. Useful when waiting on CI. label Nov 11, 2020
@mergify mergify bot merged commit 1260f7c into master Nov 11, 2020
mergify bot pushed a commit that referenced this pull request Nov 11, 2020
* 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

(cherry picked from commit 1260f7c)
@mergify mergify bot added the Backported This PR has been backported label Nov 11, 2020
mergify bot added a commit that referenced this pull request Nov 11, 2020
* 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

(cherry picked from commit 1260f7c)

* Waive binary incompatibility false positive in 2.11

Co-authored-by: Jack Koenig <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
@jackkoenig jackkoenig deleted the refine-autonaming branch December 1, 2020 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Backported This PR has been backported Please Merge Accepted PRs that are ready to be merged. Useful when waiting on CI.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Change in behavior between 3.3.2 and 3.4.0-RC3 for naming
3 participants