[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