[GHC] #11564: Possible overzealous unfolding
GHC
ghc-devs at haskell.org
Tue Feb 9 21:42:50 UTC 2016
#11564: Possible overzealous unfolding
-------------------------------------+-------------------------------------
Reporter: simonmar | Owner: simonpj
Type: bug | Status: new
Priority: high | Milestone:
Component: Compiler | Version: 8.1
Keywords: | Operating System: Unknown/Multiple
Architecture: | Type of failure: None/Unknown
Unknown/Multiple |
Test Case: | Blocked By:
Blocking: | Related Tickets:
Differential Rev(s): | Wiki Page:
-------------------------------------+-------------------------------------
I was investigating why (>>=) in the Haxl monad is being inlined more than
I would expect, and I ran into something I don't fully understand, and
looks dubious.
Start from this standalone example:
{{{
{-# LANGUAGE ScopedTypeVariables, ExistentialQuantification #-}
module Haxl where
import Data.IORef
import Control.Exception
newtype GenHaxl u a = GenHaxl
{ unHaxl :: Int -> IORef () -> IO (Result u a) }
data Result u a
= Done a
| Throw SomeException
| Blocked (Cont u a)
data Cont u a
= forall b. Cont u b :>>= (b -> GenHaxl u a)
| forall b. (b -> a) :<$> (Cont u b)
instance Monad (GenHaxl u) where
return a = GenHaxl $ \_env _ref -> return (Done a)
GenHaxl m >>= k = GenHaxl $ \env ref -> do
e <- m env ref
case e of
Done a -> unHaxl (k a) env ref
Throw e -> return (Throw e)
Blocked cont -> return (Blocked (cont :>>= k))
instance Functor (GenHaxl u)
instance Applicative (GenHaxl u)
}}}
(it could be simplified further, but I've intentionally used the exact
definition of `>>=` that is used in Haxl to be sure I'm not investigating
the wrong thing)
Compile like this:
{{{
ghc -O -c Haxl.hs
}}}
and look at the .hi file:
{{{
ghc --show-iface Haxl.hi
}}}
see this:
{{{
ea159c3b107c307a4e76cd310efcc8bc
$fMonadGenHaxl2 ::
GenHaxl u a
-> (a -> GenHaxl u b)
-> Int
-> IORef ()
-> State# RealWorld
-> (# State# RealWorld, Result u b #)
{- Arity: 5, HasNoCafRefs,
Strictness:
<C(C(C(S(SS)))),1*C1(C1(C1(U(U,1*U))))><L,U><L,U><L,U><S,U>,
Unfolding: InlineRule (5, True, False)
(\ @ u
@ a
@ b
(ds :: GenHaxl u a)
(k :: a -> GenHaxl u b)
(env :: Int)
(ref :: IORef ())
(s :: State# RealWorld)[OneShot] ->
case (ds `cast` (N:GenHaxl[0] <u>_P <a>_R) env ref)
`cast`
(N:IO[0] <Result u a>_R)
s of ds1 { (#,#) ipv ipv1 ->
case ipv1 of wild {
Done a1
-> ((k a1) `cast` (N:GenHaxl[0] <u>_P <b>_R) env ref)
`cast`
(N:IO[0] <Result u b>_R)
ipv
Throw e -> (# ipv, Throw @ u @ b e #)
Blocked cont
-> (# ipv, Blocked @ u @ b (:>>= @ u @ b @ a cont k) #)
} }) -}
}}}
That right there is the definition of `>>=`. Note that it has an
`InlineRule`, which means that it will be unconditionally unfolded pretty
much everywhere. I don't think this is right - there's no benefit to be
had in inlining it unconditionally.
I delved in a bit more, and it seems this unfolding arises during worker-
wrapper. Before WW we have
{{{
a_sVM
[LclId,
Arity=5,
Str=DmdType <C(C(C(S(SS)))),1*C1(C1(C1(U(U,1*U))))><L,U><L,U><L,U><S,U>,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=IF_ARGS [0 60 0 0 0] 94 60}]
a_sVM =
\ @ u_XQR
@ a_aPN
@ b_aPO
ds_dQP [Dmd=<C(C(C(S(SS)))),1*C1(C1(C1(U(U,1*U))))>]
k_aEC
env_aED
ref_aEE
s_aVC [Dmd=<S,U>, OS=OneShot] ->
case (((ds_dQP `cast` ...) env_aED ref_aEE) `cast` ...) s_aVC
of _ [Occ=Dead, Dmd=<L,A>]
{ (# ipv_aVF [Dmd=<S,U>], ipv1_aVG [Dmd=<S,1*U>] #) ->
case ipv1_aVG of _ [Occ=Dead, Dmd=<L,A>] {
Done a_aEG ->
((((k_aEC a_aEG) `cast` ...) env_aED ref_aEE) `cast` ...) ipv_aVF;
Throw e_aEH -> (# ipv_aVF, Haxl.Throw e_aEH #);
Blocked cont_aEI ->
(# ipv_aVF, Haxl.Blocked (Haxl.:>>= cont_aEI k_aEC) #)
}
}
}}}
and after WW we have
{{{
a_sVM
[LclId,
Arity=5,
Str=DmdType <C(C(C(S(SS)))),1*C1(C1(C1(U(U,1*U))))><L,U><L,U><L,U><S,U>,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=5,unsat_ok=True,boring_ok=False)
Tmpl= \ @ u_XQR
@ a_aPN
@ b_aPO
ds_dQP [Occ=Once]
k_aEC [Occ=Once*]
env_aED
ref_aEE
s_aVC [Occ=Once, OS=OneShot] ->
case (((ds_dQP `cast` ...) env_aED ref_aEE) `cast` ...)
s_aVC
of _ [Occ=Dead]
{ (# ipv_aVF [Occ=Once*], ipv1_aVG [Occ=Once!] #) ->
case ipv1_aVG of _ [Occ=Dead] {
Done a_aEG [Occ=Once] ->
((((k_aEC a_aEG) `cast` ...) env_aED ref_aEE) `cast`
...) ipv_aVF;
Throw e_aEH [Occ=Once] -> (# ipv_aVF, Haxl.Throw e_aEH
#);
Blocked cont_aEI [Occ=Once] ->
(# ipv_aVF, Haxl.Blocked (Haxl.:>>= cont_aEI k_aEC)
#)
}
}}]
a_sVM =
\ @ u_XQR
@ a_aPN
@ b_aPO
ds_dQP [Dmd=<C(C(C(S(SS)))),1*C1(C1(C1(U(U,1*U))))>]
k_aEC
env_aED
ref_aEE
s_aVC [Dmd=<S,U>, OS=OneShot] ->
case (((ds_dQP `cast` ...) env_aED ref_aEE) `cast` ...) s_aVC
of _ [Occ=Dead, Dmd=<L,A>]
{ (# ipv_aVF [Dmd=<S,U>], ipv1_aVG [Dmd=<S,1*U>] #) ->
case ipv1_aVG of _ [Occ=Dead, Dmd=<L,A>] {
Done a_aEG ->
((((k_aEC a_aEG) `cast` ...) env_aED ref_aEE) `cast` ...) ipv_aVF;
Throw e_aEH -> (# ipv_aVF, Haxl.Throw e_aEH #);
Blocked cont_aEI ->
(# ipv_aVF, Haxl.Blocked (Haxl.:>>= cont_aEI k_aEC) #)
}
}
}}}
For some unknown reason, this binding has acquired an always-on unfolding.
There's no wrapper, we're just unfolding the whole thing.
Simon, can you shed any light here? I would like to tune unfolding sizes
to reduce code bloat in our codebase, but with this unfolding being
unconditional it doesn't work to use `-funfolding-use-threshold`, I can
only use `NOINLINE` but that's too blunt.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/11564>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list