Opened 4 years ago

Last modified 3 years ago

#10401 new bug

state hack-related regression

Reported by: rwbarton Owned by:
Priority: normal Milestone:
Component: Compiler Version: 7.10.1
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Runtime performance bug Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:


Consider the following program exp.hs:

import Control.Monad
import Debug.Trace

expensive :: String -> String
expensive x = trace "$$$" x
{-# NOINLINE expensive #-}

main :: IO ()
main = do
  str <- fmap expensive getLine
  replicateM_ 3 $ print str

When run as echo hi | ./exp, it might print either




depending on the compiler version and optimization settings.

In 7.8.4, building with -O2 produces the second (bad) output, while building with -O2 -fno-state-hack produces the first output, not surprisingly.

However in 7.10.1 and in HEAD both -O2 and -O2 -fno-state-hack produce the second output.

It seems this difference in behavior between 7.8.4 and 7.10.1 may be due to the following difference in the unfolding for IO's fmap. In 7.8.4

  $fFunctorIO2 :: (a -> b)
                  -> GHC.Types.IO a
                  -> GHC.Prim.State# GHC.Prim.RealWorld
                  -> (# GHC.Prim.State# GHC.Prim.RealWorld, b #)
    {- Arity: 3, HasNoCafRefs,
       Strictness: <L,1*C1(U)><C(S),1*C1(U(U,U))><L,U>,
       Unfolding: (\ @ a
                     @ b
                     f :: a -> b
                     x :: GHC.Types.IO a
                     s :: GHC.Prim.State# GHC.Prim.RealWorld ->
                   case x `cast` (GHC.Types.NTCo:IO[0] <a>_R)
                          s of ds { (#,#) ipv ipv1 ->
                   (# ipv, f ipv1 #) }) -}

while in 7.10.1

  $fFunctorIO2 ::
    (a -> b) -> IO a -> State# RealWorld -> (# State# RealWorld, b #)
  {- Arity: 3, HasNoCafRefs,
     Strictness: <L,1*C1(U)><C(S),1*C1(U(U,U))><L,U>,
     Unfolding: InlineRule (3, True, False)
                (\ @ a @ b f :: a -> b x :: IO a s :: State# RealWorld[OneShot] ->
--                                                                     ^^^^^^^
                 case x `cast` (NTCo:IO[0] <a>_R) s of ds { (#,#) ipv ipv1 ->
                 (# ipv, f ipv1 #) }) -}

I didn't completely confirm this, though, nor determine how that difference arose in the first place (I assume the libraries were built with -O2 for both versions?)

Change History (3)

comment:1 Changed 4 years ago by nomeata

The difference you marked is a result of changeset:c001bde73e38904ed161b0b61b240f99a3b6f48d/ghc. But I believe it is not the cause of the problem, as the state hack would, if I understand it correctly, assume State# RealWorld to be one-shot independent of whether that annotation is present in the unfolding or not.

comment:2 Changed 4 years ago by rwbarton

But the ticket is mostly about the behavior of 7.10.1 with -O2 -fno-state-hack. Does that change things? (Probably should have been clearer about this.)

In 7.8 we have -fno-state-hack as an escape hatch for when the state hack heuristic goes wrong. But we can't expect people to rebuild all their dependencies including base with -fno-state-hack, and disabling optimizations completely is not very satisfactory either.

comment:3 Changed 3 years ago by simonpj

See #1168, the master ticket.

Note: See TracTickets for help on using tickets.