[GHC] #10918: Float once-used let binding into a recursive function

GHC ghc-devs at haskell.org
Wed Oct 7 15:24:27 UTC 2015


#10918: Float once-used let binding into a recursive function
-------------------------------------+-------------------------------------
        Reporter:  nomeata           |                Owner:
            Type:  task              |               Status:  new
        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):
-------------------------------------+-------------------------------------

Comment (by nomeata):

 One effect, which might or might not be the reason for the paraffin
 regression (but certainly obscures the view) is that an inlined expression
 seems to float out further than a let-bound expression. More explicitly,
 consider this code:

 {{{#!hs
 foo :: [[Bool]] -> [Bool]
 --foo input = [ not y | (x:xs) <- input, y <- (x:xs) ]
 foo [] = []
 foo (y:ys) =
     case y of
         [] -> foo ys
         (x:xs) ->
             let z = foo ys in
             let go [] = z
                 go (y':ys') = not y' : go ys'
             in  not x : go xs
 }}}

 With my change, this will be turned into

 {{{#!hs
 foo :: [[Bool]] -> [Bool]
 --foo input = [ not y | (x:xs) <- input, y <- (x:xs) ]
 foo [] = []
 foo (y:ys) =
     case y of
         [] -> foo ys
         (x:xs) ->
             let go [] = foo ys
                 go (y':ys') = not y' : go ys'
             in  not x : go xs
 }}}

 If the compiler would stop here, I’d be happy.

 But instead, something interesting happens. In the pristine case, the
 binding to `z` is not affected by the level set:
 {{{
 (let {
    <z,<1,3>>
    <z,<1,3>> = T10918a.foo ys } in
 }}}
 whereas the plain `foo ys` expression got wrapped in a binding and is
 assigned a level further out:
 {{{
 [] ->
   let {
     <lvl,F<1,1>>
     <lvl,F<1,1>> = T10918a.foo ys } in
   lvl;
 }}}

 The `F<1,1>` is – as expected – the binding site of `ys`.

 But now the first invocation of `foo ys` is in scope of the `lvl`, CSE
 happens, the variable is no longer used at most once, it cannot be inlined
 back and we end up with
 {{{#!hs
 foo :: [[Bool]] -> [Bool]
 --foo input = [ not y | (x:xs) <- input, y <- (x:xs) ]
 foo [] = []
 foo (y:ys) =
     let lvl = foo ys
     case y of
         [] -> lvl
         (x:xs) ->
             let go [] = lvl
                 go (y':ys') = not y' : go ys'
             in  not x : go xs
 }}}
 which is not what I want to see.

 So the issue here seems to be that under some circumstances, an `e` in
 `C[e]` can be floated out further than `let z = e in C[z]`.

 (All this does not look too server in this case, but it happens a few
 times in paraffin, so I hope that this is actually related to the
 allocation increase.)

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


More information about the ghc-tickets mailing list