[GHC] #9279: Local wrapper function remains in final program; result = extra closure allocation

GHC ghc-devs at haskell.org
Tue Sep 11 11:21:38 UTC 2018


#9279: Local wrapper function remains in final program; result = extra closure
allocation
-------------------------------------+-------------------------------------
        Reporter:  simonmar          |                Owner:  simonpj
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:
       Component:  Compiler          |              Version:  7.8.2
      Resolution:                    |             Keywords:  LateLamLift
Operating System:  Unknown/Multiple  |         Architecture:
 Type of failure:  Runtime           |  Unknown/Multiple
  performance bug                    |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by simonpj):

 Concerning comment at 4 I have not looked in detail at the example in the
 Description, but
 I'm guessing that we have a situation like this
 {{{
 f x xs = let g :: Int -> Int
              g y = y+x
          in map g xs
 }}}
 We see that `g` is strict, so we w/w it thus:
 {{{
 f x xs = let $wg :: Int# -> Int#
              $wg y# = case x of I# x# -> y# + x#

              g :: Int -> Int
              {-# Stable unfolding = \y -> <as below> #-}
              g y = case y of I# y# ->
                    case $wg y# of r# ->
                    I# r#
          in map g xs
 }}}
 But alas, `g` never gets to inline, so it is all in vain.
 Worse, we have lost out, because `map` calls `g` which calls
 `$wg` and that's slower than what we started with.

 What we want is this:

 * If the only call to `$wg` is from `g`, then inline it
   back in.

 Currently there are two calls to `gw`, one in the RHS of `g`
 and one in the stable unfolding of `g`.  If we simply nuked
 the stable unfolding to `g` (which was added by w/w), then
 there'd only be one call to `gw` and we'd inine it happily.

 On the other hand, if the body of f was `(map g xs, g 7)`,
 then the `g 7` would by now have turned into a call of `$wg`,
 so whether we inlined `$wg` would depend on how big `$wg` is,
 which is absolutely fine.

 Arguably should not do this nuking stuff until after `TidyCore`, which
 generates bindings to put in the interface file.  We want to leave
 `f`'s RHS undisturbed until then, in case `f` itself is inlined
 in other modules.  (An alternative view: it'd be ok to dump the
 w/w split in before `TidyCore` because we'll rediscover the strictness
 (and perhaps better strictness) in any module that inlines `f`.)

 My conclusion:

 * for local functions (i.e. bound by a `let`, or at top level but not
 exported)
 * that have been w/w'd
 * at a fairly late stage in the pipeline
 * kill off the stable-unfolding introduced by w/w
 * and simplify

 I think it'd be interesting to try this.

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


More information about the ghc-tickets mailing list