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

Keep recursive and non-recursive let in Clash Core #1980

Merged
merged 1 commit into from
Nov 8, 2021

Conversation

alex-mckenna
Copy link
Contributor

@alex-mckenna alex-mckenna commented Nov 1, 2021

In GHC core, the difference between recursive and non-recursive binders is preserved. In Clash, this difference is not preserved in let expressions / the bindings map, which leads to sometimes needing to calculate the connected components of the bindings.

However, since Letrec is used a lot (i.e. in normalization) it is painful to simply differentiate everywhere immediately. As a compromise, this is kept as a pattern synonym so existing code should work as it did before. In the future we can migrate these cases until only Let NonRec{} and Let Rec{} are used in code. Until this time, the assumption is made that NonRec is always non-recursive, but Rec holds recursive and potentially recursive bindings.

The parts of the compiler where it is easier to change the definitions now have been changed.

Still TODO:

  • Write a changelog entry (see changelog/README.md)
  • Check copyright notices are up to date in edited files

Copy link
Member

@christiaanb christiaanb left a comment

Choose a reason for hiding this comment

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

According to https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/pattern_synonyms.html you shouldn't need the PatternSynonyms extension when you use them, only when you define them. Is that not true?

clash-ghc/src-ghc/Clash/GHC/GenerateBindings.hs Outdated Show resolved Hide resolved
clash-lib/src/Clash/Core/Subst.hs Outdated Show resolved Hide resolved
clash-lib/src/Clash/Core/Subst.hs Outdated Show resolved Hide resolved
clash-lib/src/Clash/Core/Subst.hs Outdated Show resolved Hide resolved
data Bind a
= NonRec Id a
| Rec [(Id, a)]
deriving (Eq, Show, Generic, NFData, Hashable, Binary, Functor)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
deriving (Eq, Show, Generic, NFData, Hashable, Binary, Functor)
deriving (Show, Generic, NFData, Hashable, Binary, Functor)
-- | Structural equivalence, not alpha equivalence
deriving instance Eq a => Eq (Bind a)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've left a comment by the deriving clause but otherwise left this. I think it would be a good idea to deal with structural / alpha equality for Bind in #1990, because I also have a similar thing with alpha-equivalence for patterns in #1976 that I want to move to that PR

Copy link
Member

Choose a reason for hiding this comment

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

I don't want to add the Structural and Alpha newtypes suggested in #1987 to #1990 though, I want to do that in a separate PR. #1990 significantly reduces compile times which I don't want to delay merging just because there's a further improvement on the horizon, here's master vs #1990

christiaan@DESKTOP-O5QKI8I:~/clash-compiler$ time cabal run clash-testsuite -- -p VHDL.clash --hide-successes -j6
Up to date

All 378 tests passed (555.68s)

real    9m17.383s
user    54m37.413s
sys     2m58.446s

christiaan@DESKTOP-O5QKI8I:~/clash-compiler$ time cabal run clash-testsuite -- -p VHDL.clash --hide-successes -j6
Up to date

All 378 tests passed (410.44s)

real    6m51.604s
user    38m45.764s
sys     2m21.107s

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, but you add the eqTerm function, and I want to add Bind to that and remove the TODO (since I also want to dig out the alpha / structural equality on Pat and add it to that PR anyway we may as well do both)

clash-lib/src/Clash/Normalize/Transformations/XOptimize.hs Outdated Show resolved Hide resolved
clash-lib/src/Clash/Normalize/Util.hs Outdated Show resolved Hide resolved
clash-lib/src/Clash/Rewrite/Util.hs Outdated Show resolved Hide resolved
clash-lib/src/Clash/Core/Pretty.hs Show resolved Hide resolved
clash-lib/src/Clash/Core/Pretty.hs Show resolved Hide resolved
data Bind a
= NonRec Id a
| Rec [(Id, a)]
deriving (Eq, Show, Generic, NFData, Hashable, Binary, Functor)
Copy link
Member

Choose a reason for hiding this comment

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

I don't want to add the Structural and Alpha newtypes suggested in #1987 to #1990 though, I want to do that in a separate PR. #1990 significantly reduces compile times which I don't want to delay merging just because there's a further improvement on the horizon, here's master vs #1990

christiaan@DESKTOP-O5QKI8I:~/clash-compiler$ time cabal run clash-testsuite -- -p VHDL.clash --hide-successes -j6
Up to date

All 378 tests passed (555.68s)

real    9m17.383s
user    54m37.413s
sys     2m58.446s

christiaan@DESKTOP-O5QKI8I:~/clash-compiler$ time cabal run clash-testsuite -- -p VHDL.clash --hide-successes -j6
Up to date

All 378 tests passed (410.44s)

real    6m51.604s
user    38m45.764s
sys     2m21.107s

clash-lib/src/Clash/Rewrite/Util.hs Outdated Show resolved Hide resolved
In GHC core, the difference between recursive and non-recursive
binders is preserved. In Clash, this difference is not preserved
in let expressions / the bindings map, which leads to sometimes
needing to calculate the connected components of the bindings.

However, since Letrec is used a lot (i.e. in normalization) it is
painful to simply differentiate everywhere now. As a compromise,
this is kept as a pattern synyonm so exsting code should work as
it did before. In the future we can migrate these cases until only
`Let NonRec{}` and `Let Rec{}` are used in code. Until this time,
the assumption is made that `NonRec` is _always_ non-recursive, but
`Rec` holds recursive and _potentially_ recursive bindings.

The parts of the compiler where it is easier to change the definitions
now have been changed.
@alex-mckenna alex-mckenna merged commit cd5a9b6 into master Nov 8, 2021
@alex-mckenna alex-mckenna deleted the core-better-letrec branch November 8, 2021 11:32
alex-mckenna pushed a commit that referenced this pull request Apr 6, 2022
In #1980, Clash started to keep the distinction between recursive
and non-recursive let expressions in Core (like GHC). However, for
convenience the old Letrec constructor is still used in some places
to avoid the need to fix every location at once.

The `inlineCleanup` transformation was one such place, however the
fix is relatively simple: when there is only one binder at the end
of cleanup and the original let expression was non-recursive, this
binding must also be non-recursive.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants