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

Quadratic naming with recursive functions #1606

Closed
jackkoenig opened this issue Oct 8, 2020 · 4 comments · Fixed by #1614
Closed

Quadratic naming with recursive functions #1606

jackkoenig opened this issue Oct 8, 2020 · 4 comments · Fixed by #1614
Labels

Comments

@jackkoenig
Copy link
Contributor

When using an intermediate Wire (or Reg, any Data) in a recursive function, the new naming behavior results in a quadratic naming blowup:

def func(bools: Seq[Bool]): Bool = {
  if (bools.isEmpty) true.B
  else {
    val w = Wire(Bool())
    w := bools.head && func(bools.tail)
    w
  }
}
// Call it in a Module
val x = func(io.in)

This gives:

    wire x : UInt<1>
    wire x_x_w : UInt<1>
    wire x_x_x_x_w_w : UInt<1>
    wire x_x_x_x_w_x_x_x_x_w_w_w : UInt<1>
    node _x_x_x_x_w_x_x_x_x_w_w_x_x_x_x_w_x_x_x_x_w_w_w_T = and(io.in[3], UInt<1>("h1"))
    x_x_x_x_w_x_x_x_x_w_w_w <= _x_x_x_x_w_x_x_x_x_w_w_x_x_x_x_w_x_x_x_x_w_w_w_T
    node _x_x_x_x_w_x_x_x_x_w_w_T = and(io.in[2], x_x_x_x_w_x_x_x_x_w_w_w)
    x_x_x_x_w_w <= _x_x_x_x_w_x_x_x_x_w_w_T
    node _x_x_x_x_w_T = and(io.in[1], x_x_x_x_w_w)
    x_x_w <= _x_x_x_x_w_T
    node _x_x_T = and(io.in[0], x_x_w)
    x <= _x_x_T

Executable example: https://scastie.scala-lang.org/O0xhmu4eRgqdXmUb2CfqbA

While this is very funny, it's also an important bug to fix. Internally I'm seeing names well over 2000 characters long and since the Verilog spec allows tools to only support names up to 1024 characters, we're already hitting problems in various tools.

Type of issue: bug report

Impact: no functional change

Development Phase: request

@jackkoenig jackkoenig added the bug label Oct 8, 2020
@aswaterman
Copy link
Member

lol.

What's the desired behavior? It shouldn't grow linearly, either, or the same sort of issues will crop up eventually, especially with longer base names. If you rewrite this example iteratively (https://scastie.scala-lang.org/b2r2IB3cQFWALeO1SpFFKQ) you get logarithmic growth in the form of a postfixed integer, which seems desirable.

Yet, we presumably do want multiple non-recursive function calls to build up these longish names. Maybe we could heuristically filter out the recursive (and mutually recursive) cases by not adding a prefix that has already been added before. Maybe it is as simple as building up a list of prefixes that have been used (("x", "w") in this case) and not adding a prefix that has already been used.

@jackkoenig
Copy link
Contributor Author

For fixing just the quadratic issue, I think there's a straightforward solution. Basically this occurs because when doing a connection, eg. w := bools.head && func(bools.tail), it's not the "local" name w that is used as a prefix, rather it's the full already prefixed name (eg. x_x_w) which gets combined with the prefix for the current place in the call stack ("x" :: "x" :: Nil) if that makes sense. I think the fix is just to make the prefix from the connection just be "w".

As for the linear growth issue, obviously we don't want that either, but I am treating it as a separate issue. It's admittedly not the best user experience but I was just thinking about adding a stripPrefix or popPrefix call to deal with the linear growth. I'm not sure how to distinguish otherwise unless we also want the plugin to try to identify recursion. That's probably doable, but the issue will still crop up in mutual recursion.

@jackkoenig
Copy link
Contributor Author

jackkoenig commented Oct 8, 2020

We could try to filter out prefixes that we've already seen so that's an option, but that would likely affect places where it may be desirable to repeat a prefix once or twice based on a particular call stack (in non-recursive places).

@aswaterman
Copy link
Member

Or, more simply, truncate the prefix to (N - suffix-length) chars, where N is the longest name we want to support. You're right, separate issue, though.

jackkoenig added a commit that referenced this issue Oct 12, 2020
Fixes #1606

Previously, the Data itself would be put on the prefix stack and its
full name would be used as the prefix for subsequent names. This meant
that prefixes would grow quadratically as the prefix is present both on
the Data pushed to the stack, and on the stack itself. This is fixed by
just using the "local" name of the Data being pushed on the stack.

A related issue is defering the name resolution. This led to unintuitive
behavior when the name of a Data used as a prefix is overriden later
(usually when the Data is a return value). The overriding name would
show up in prefixes twice instead of once. It is much more intuitive to
grab the name at the moment of the connection creating the prefix rather
than defering to later.
jackkoenig added a commit that referenced this issue Oct 12, 2020
Fixes #1606

Previously, the Data itself would be put on the prefix stack and its
full name would be used as the prefix for subsequent names. This meant
that prefixes would grow quadratically as the prefix is present both on
the Data pushed to the stack, and on the stack itself. This is fixed by
just using the "local" name of the Data being pushed on the stack.

A related issue is deferring the name resolution. This led to unintuitive
behavior when the name of a Data used as a prefix is overridden later
(usually when the Data is a return value). The overriding name would
show up in prefixes twice instead of once. It is much more intuitive to
grab the name at the moment of the connection creating the prefix rather
than deferring to later.
@mergify mergify bot closed this as completed in #1614 Oct 12, 2020
mergify bot added a commit that referenced this issue Oct 12, 2020
Fixes #1606

Previously, the Data itself would be put on the prefix stack and its
full name would be used as the prefix for subsequent names. This meant
that prefixes would grow quadratically as the prefix is present both on
the Data pushed to the stack, and on the stack itself. This is fixed by
just using the "local" name of the Data being pushed on the stack.

A related issue is deferring the name resolution. This led to unintuitive
behavior when the name of a Data used as a prefix is overridden later
(usually when the Data is a return value). The overriding name would
show up in prefixes twice instead of once. It is much more intuitive to
grab the name at the moment of the connection creating the prefix rather
than deferring to later.

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
mergify bot pushed a commit that referenced this issue Oct 12, 2020
Fixes #1606

Previously, the Data itself would be put on the prefix stack and its
full name would be used as the prefix for subsequent names. This meant
that prefixes would grow quadratically as the prefix is present both on
the Data pushed to the stack, and on the stack itself. This is fixed by
just using the "local" name of the Data being pushed on the stack.

A related issue is deferring the name resolution. This led to unintuitive
behavior when the name of a Data used as a prefix is overridden later
(usually when the Data is a return value). The overriding name would
show up in prefixes twice instead of once. It is much more intuitive to
grab the name at the moment of the connection creating the prefix rather
than deferring to later.

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
(cherry picked from commit 1b15dca)
mergify bot added a commit that referenced this issue Oct 13, 2020
* When prefixing with a data, eagly get local name (#1614)

Fixes #1606

Previously, the Data itself would be put on the prefix stack and its
full name would be used as the prefix for subsequent names. This meant
that prefixes would grow quadratically as the prefix is present both on
the Data pushed to the stack, and on the stack itself. This is fixed by
just using the "local" name of the Data being pushed on the stack.

A related issue is deferring the name resolution. This led to unintuitive
behavior when the name of a Data used as a prefix is overridden later
(usually when the Data is a return value). The overriding name would
show up in prefixes twice instead of once. It is much more intuitive to
grab the name at the moment of the connection creating the prefix rather
than deferring to later.

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
(cherry picked from commit 1b15dca)

* Waive MiMa failures on package private methods

Co-authored-by: Jack Koenig <[email protected]>
jackkoenig pushed a commit that referenced this issue Feb 28, 2023
Co-authored-by: Scala Steward <[email protected]>
Co-authored-by: Scala Steward <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants