[GHC] #14079: Failure to do CPR in the presence of a local letrec

GHC ghc-devs at haskell.org
Thu Aug 3 14:12:37 UTC 2017


#14079: Failure to do CPR in the presence of a local letrec
-------------------------------------+-------------------------------------
        Reporter:  nomeata           |                Owner:  (none)
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:
       Component:  Compiler          |              Version:  8.3
      Resolution:                    |             Keywords:  JoinPoints
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  None/Unknown      |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by nomeata):

 Not sure to what extend there is a bug, but I believe it explains the
 regressions when we introduce loopification.

 Let’s start with this code.
 {{{
 je :: (Int, Int) -> Int -> (Int, Int)
 je !x y | y > 0 = x
         | otherwise = je x (makeBig y + 1)
 }}}
 Without loopification, this stays a top-level recursive bindings. Even if
 it is small, it is never inlined, so w/w happens, and we get a nice
 worker, with both tuples unboxed:
 {{{
 $wje :: Int -> Int -> Int# -> (# Int, Int #)
 }}}
 This avoid allocation of tuples, which is great.

 Now, let’s do loopification by hand (the extra `n` is just to avoid
 floating `je` to the top-level, because we do not support top-level join
 points:
 {{{
 e :: (Int, Int) -> Int -> Int -> (Int, Int)
 e x y n = je x y
  where je !x y | y > 0 = x
                | otherwise = je x (y + n)
 }}}
 Now `e` is small and, by changing from recursive to non-recursive, now
 inlineable. Therefore w/w refuses to work on `e` and we get no worker for
 `e`. We do get a worker for the local join point `je`, but because it is a
 join-point, no CPR happens, and its type is
 {{{
 $wje :: Int -> Int -> Int# -> (Int, Int)
 }}}

 As you point out that other changes to `e` (such as making it look big, or
 marking it `NOINLINE`) avoid this and give it a nice wrapper. But that is
 a red herring: The code out there _is_ small and _isn’t_ marked
 `NOINLINE`.

 Anyways, so we are stuck with an inlineable `e` without a worker. The next
 question is hence: What happens with it? If we indeed inline `e`, and
 inline it into a nice context (say, into `case _ of (x,y) -> _`, then
 case-of-case (and case-of-joinrec) will move this `case` deep into the
 `letrec`. But (and at this point I am running out of concrete examples. I
 guess I have to look closer at nofib), what if that does not happen?

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


More information about the ghc-tickets mailing list