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

Enabling -O2 breaks Clash.Magic.clashSimulation #2736

Closed
leonschoorl opened this issue Jun 19, 2024 · 7 comments · Fixed by #2737
Closed

Enabling -O2 breaks Clash.Magic.clashSimulation #2736

leonschoorl opened this issue Jun 19, 2024 · 7 comments · Fixed by #2737

Comments

@leonschoorl
Copy link
Member

leonschoorl commented Jun 19, 2024

When running clash on modules compiled by GHC, like in a starter-project setup, enabling -O2 can break Clash.Magic.clashSimulation.
Specifically it's the option -fspec-constr, which is implied by -O2.

Steps to reproduce:

$ stack new my-clash-project clash-lang/simple

Change contents of src/Example/Project.hs to:

{-# OPTIONS_GHC -fspec-constr #-}
-- {-# OPTIONS_GHC -ddump-spec #-}
-- {-# OPTIONS_GHC -ddump-simpl -dverbose-core2core #-}
module Example.Project where

import Clash.Prelude

topEntity :: Int
topEntity
 | clashSimulation = 111
 | otherwise = 222
$ stack run clash -- Example.Project --vhdl

or

$ cabal run clash -- Example.Project --vhdl

Result:

  result <= to_signed(111,64);

We're living in a simulation 😱

This doesn't happen when running clash directly on the source file.

@leonschoorl
Copy link
Member Author

clashSimulation = True
{-# OPAQUE clashSimulation #-}

When enabled SpecConstr turns:

topEntity = case clashSimulation of
 False -> b
 True -> a

into

topEntity = a

@christiaanb does this violate the contract of OPAQUE?

@christiaanb
Copy link
Member

I guess it follows the letter of the contract, but not the spirit...

Note [OPAQUE pragma]
~~~~~~~~~~~~~~~~~~~~
Suppose a function `f` is marked {-# OPAQUE f #-}.  Then every call of `f`
should remain a call of `f` throughout optimisation; it should not be turned
into a call of a name-mangled variant of `f` (e.g by worker/wrapper).

Since clashSimulation and its content are completely elided in the final result, technically, we see no name-mangled version of clashSimulation. i.e. we wouldn't be surprised if something with an OPAQUE annotation got elided as part of dead code elimination. That being said, I think in this particular case, GHC is doing something to the OPAQUE annoted binder that is non-desirable.

@christiaanb
Copy link
Member

You could try compiling Clash.Magic with {-# OPTIONS_GHC -O0 #-} to see whether that helps.

@martijnbastiaan
Copy link
Member

martijnbastiaan commented Jun 20, 2024

Maybe

clashSimulation = unsafePerformIO (fromMaybe True <$> lookupEnv "XXX_ISSUE_2736")
{-# OPAQUE clashSimulation #-}

Should be a readMaybe in there too, but I think the general idea should be clear :)

@christiaanb
Copy link
Member

Together with @leonschoorl we discoved that

import GHC.Magic (noinline)

clashSimulation = noinline True
{-# OPAQUE clashSimulation #-}

is a workaround/proper fix. At least it's not something where we need to wait for a new GHC release with a changed behavior with respect to OPAQUE.

@rowanG077
Copy link
Member

Related perhaps: #2570 ?

@leonschoorl
Copy link
Member Author

@rowanG077 I don't think so, here the problem is happening inside a transformation inside GHC, with #2570 it seems to be inside transformations in clash

mergify bot pushed a commit that referenced this issue Jun 30, 2024
The SpecConstr pass in GHC is able to poke through the OPAQUE of
clashSimulation.
Adding a noinline seems to block it from seeing the True inside.

Fixes #2736

(cherry picked from commit 68fb3da)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants