[GHC] #10844: CallStack should not be inlined
GHC
ghc-devs at haskell.org
Sun Oct 4 21:08:23 UTC 2015
#10844: CallStack should not be inlined
-------------------------------------+-------------------------------------
Reporter: nomeata | Owner: gridaphobe
Type: task | Status: patch
Priority: normal | Milestone:
Component: Compiler | Version: 7.10.2
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s): Phab:D1259
-------------------------------------+-------------------------------------
Comment (by gridaphobe):
Ok, here's a version of Joachim's test case that doesn't involve
CallStacks.
{{{#!haskell
==> T10844a.hs <==
module T10844a where
data Foo = Foo Int String
showFoo (Foo i s) = unwords [ "Foo", show i, show s ]
foo n = showFoo $ Foo 0 "foo"
{-# INLINE foo #-}
==> T10844.hs <==
module T10844 where
import T10844a
main = print (foo 0)
}}}
With optimizations it produces the following core
{{{
% ghc --make -fforce-recomp -ddump-simpl T10844.hs -O
[1 of 2] Compiling T10844a ( T10844a.hs, T10844a.o )
==================== Tidy Core ====================
Result size of Tidy Core = {terms: 61, types: 45, coercions: 0}
T10844a.showFoo3 :: [Char]
[GblId,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,
WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 40 0}]
T10844a.showFoo3 = GHC.CString.unpackCString# "Foo"#
T10844a.showFoo2 :: Char
[GblId,
Caf=NoCafRefs,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 20}]
T10844a.showFoo2 = GHC.Types.C# ' '
T10844a.showFoo1 :: [Char]
[GblId,
Caf=NoCafRefs,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 30}]
T10844a.showFoo1 =
GHC.Types.: @ Char GHC.Show.shows6 (GHC.Types.[] @ Char)
T10844a.$wshowFoo [InlPrag=[0]] :: Int -> String -> String
[GblId,
Arity=2,
Str=DmdType <L,1*U(U)><L,1*U>,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [20 0] 220 0}]
T10844a.$wshowFoo =
\ (ww_sRs :: Int) (ww1_sRt :: String) ->
++
@ Char
T10844a.showFoo3
(GHC.Types.:
@ Char
T10844a.showFoo2
(case ww_sRs of _ [Occ=Dead] { GHC.Types.I# ww3_aQt ->
case GHC.Show.$wshowSignedInt 0 ww3_aQt (GHC.Types.[] @ Char)
of _ [Occ=Dead] { (# ww5_aQx, ww6_aQy #) ->
++
@ Char
(GHC.Types.: @ Char ww5_aQx ww6_aQy)
(GHC.Types.:
@ Char
T10844a.showFoo2
(++
@ Char
(GHC.Types.:
@ Char
GHC.Show.shows6
(GHC.Show.showLitString ww1_sRt T10844a.showFoo1))
(GHC.Types.[] @ Char)))
}
}))
showFoo [InlPrag=INLINE[0]] :: Foo -> String
[GblId,
Arity=1,
Str=DmdType <S,1*U(1*U(U),1*U)>,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
Tmpl= \ (w_sRp [Occ=Once!] :: Foo) ->
case w_sRp
of _ [Occ=Dead] { Foo ww1_sRs [Occ=Once] ww2_sRt
[Occ=Once] ->
T10844a.$wshowFoo ww1_sRs ww2_sRt
}}]
showFoo =
\ (w_sRp :: Foo) ->
case w_sRp of _ [Occ=Dead] { Foo ww1_sRs ww2_sRt ->
T10844a.$wshowFoo ww1_sRs ww2_sRt
}
lvl_rEJ :: Int
[GblId, Caf=NoCafRefs, Str=DmdType]
lvl_rEJ = GHC.Types.I# 0
lvl1_rRU :: [Char]
[GblId, Str=DmdType]
lvl1_rRU = GHC.CString.unpackCString# "foo"#
lvl2_rRV :: String
[GblId, Str=DmdType]
lvl2_rRV = T10844a.$wshowFoo lvl_rEJ lvl1_rRU
foo [InlPrag=INLINE (sat-args=1)] :: forall t_aOn. t_aOn -> String
[GblId,
Arity=1,
Str=DmdType <L,A>,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=False,boring_ok=False)
Tmpl= \ (@ t_aOp) _ [Occ=Dead] ->
$ @ Foo
@ String
showFoo
(T10844a.Foo
(GHC.Types.I# 0)
(GHC.Base.build
@ Char
(\ (@ b_aPw) -> GHC.CString.unpackFoldrCString# @
b_aPw "foo"#)))}]
foo = \ (@ t_aOp) _ [Occ=Dead] -> lvl2_rRV
[2 of 2] Compiling T10844 ( T10844.hs, T10844.o )
==================== Tidy Core ====================
Result size of Tidy Core = {terms: 31, types: 22, coercions: 3}
T10844.main7 :: Int
[GblId,
Caf=NoCafRefs,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 20}]
T10844.main7 = GHC.Types.I# 0
T10844.main6 :: [Char]
[GblId,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,
WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 40 0}]
T10844.main6 = GHC.CString.unpackCString# "foo"#
T10844.main5 :: String
[GblId,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,
WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 30 0}]
T10844.main5 = T10844a.$wshowFoo T10844.main7 T10844.main6
...
}}}
The problem is that the unfolding for `foo` contains the string literal
`"foo"` instead of the floated `lvl1_rRU`, so we end up inlining the
string literal in `T10844`, which seems rather pointless.
In fact, I'd argue that since the `Foo 0 "foo"` doesn't contain any free
variables, the whole thing should be floated to the top-level. GHC
actually goes further and floats the entire RHS (to `lvl2_rRV`), but it
doesn't update `foo`s unfolding.
The thing that I'm unsure about is that this example relies on the
`INLINE` pragma. As I understand it, `INLINE` tells GHC to unconditionally
inline the entire definition, which is precisely what it's doing here.
(`INLINEABLE` has a similar effect in this example.) The more general fix,
to make the unfolding reflect the result of the FloatOut pass, would
subtly change the semantics of `INLINE`. It would no longer mean "inline
the entire RHS" but rather "inline the pieces of the RHS that we think are
sensible to inline". Is that a change we want to make? It would certainly
decrease binary sizes, but it seems like that could also cause a
performance loss due to increased cache misses.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/10844#comment:9>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list