[GHC] #8589: Bad choice of loop breaker with INLINABLE/INLINE

GHC ghc-devs at haskell.org
Mon Dec 2 12:27:59 UTC 2013


#8589: Bad choice of loop breaker with INLINABLE/INLINE
------------------------------+--------------------------------------------
       Reporter:              |             Owner:
  NickSmallbone               |            Status:  new
           Type:  bug         |         Milestone:
       Priority:  normal      |           Version:  7.6.3
      Component:  Compiler    |  Operating System:  Unknown/Multiple
       Keywords:              |   Type of failure:  Runtime performance bug
   Architecture:              |         Test Case:
  Unknown/Multiple            |          Blocking:
     Difficulty:  Unknown     |
     Blocked By:              |
Related Tickets:              |
------------------------------+--------------------------------------------
 Take the following program, which defines a pair of lists recursively:

 {{{
 module Test(xs, ys) where

 pair :: ([Bool], [Bool])
 pair = (False:xs, True:ys)
   where
     (xs, ys) = pair

 (xs, ys) = pair
 }}}

 GHC is clever enough to disentangle {{{xs}}} from {{{ys}}} and with
 {{{-ddump-simpl -O}}} I get:

 {{{
 Rec {
 xs [Occ=LoopBreaker] :: [Bool]
 [GblId, Caf=NoCafRefs, Str=DmdType]
 xs = : False xs
 end Rec }

 Rec {
 ys [Occ=LoopBreaker] :: [Bool]
 [GblId, Caf=NoCafRefs, Str=DmdType]
 ys = : True ys
 end Rec }
 }}}

 However, if I mark {{{pair}}} as {{{INLINABLE}}} or {{{INLINE}}} (it
 doesn't matter which), I get much worse code where {{{xs}}} and {{{ys}}}
 go through {{{pair}}}:

 {{{
 Rec {
 pair [InlPrag=INLINABLE[ALWAYS], Occ=LoopBreaker]
   :: ([Bool], [Bool])
 [GblId,
  Str=DmdType m,
  Unf=Unf{Src=InlineStable, TopLvl=True, Arity=0, Value=True,
          ConLike=True, WorkFree=False, Expandable=True,
          Guidance=IF_ARGS [] 50 30
          Tmpl= (: False
                   (case pair of _ { (xs1_Xf6 [Occ=Once], _) -> xs1_Xf6 }),
                 : True (case pair of _ { (_, ys1_Xf6 [Occ=Once]) ->
 ys1_Xf6 }))}]
 pair = (a1_rgo, a_rgn)

 ys_ys :: [Bool]
 [GblId,
  Str=DmdType,
  Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False,
          ConLike=False, WorkFree=True, Expandable=True,
          Guidance=IF_ARGS [] 10 0}]
 ys_ys = case pair of _ { (xs1_XeW, ys1_Xf7) -> ys1_Xf7 }

 a_rgn :: [Bool]
 [GblId, Str=DmdType]
 a_rgn = : True ys_ys

 xs_xs :: [Bool]
 [GblId,
  Str=DmdType,
  Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False,
          ConLike=False, WorkFree=True, Expandable=True,
          Guidance=IF_ARGS [] 10 0}]
 xs_xs = case pair of _ { (xs1_XeW, ys1_XeS) -> xs1_XeW }

 a1_rgo :: [Bool]
 [GblId, Str=DmdType]
 a1_rgo = : False xs_xs
 end Rec }

 ys :: [Bool]
 [GblId,
  Str=DmdType,
  Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False,
          ConLike=False, WorkFree=True, Expandable=True,
          Guidance=ALWAYS_IF(unsat_ok=True,boring_ok=True)}]
 ys = ys_ys

 xs :: [Bool]
 [GblId,
  Str=DmdType,
  Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False,
          ConLike=False, WorkFree=True, Expandable=True,
          Guidance=ALWAYS_IF(unsat_ok=True,boring_ok=True)}]
 xs = xs_xs
 }}}

 It seems that GHC chooses {{{pair}}} as the loop breaker, which stops the
 simplifier in its tracks.

 It might seem a bit silly to mark {{{pair}}} as {{{INLINE}}}, since it's
 not mutually recursive. The function I really had was polymorphic with a
 typeclass constraint, and I wrote {{{INLINABLE}}} to get it specialised at
 its call site. (Also, {{{pair}}} is mutually recursive in the Core, so you
 would expect GHC to avoid using it as a loop breaker if I mark it
 {{{INLINE}}}.)

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8589>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list