[GHC] #14152: Float exit paths out of recursive functions

GHC ghc-devs at haskell.org
Thu Aug 24 16:37:47 UTC 2017


#14152: Float exit paths out of recursive functions
-------------------------------------+-------------------------------------
           Reporter:  nomeata        |             Owner:  (none)
               Type:  task           |            Status:  new
           Priority:  normal         |         Milestone:
          Component:  Compiler       |           Version:  8.2.1
           Keywords:                 |  Operating System:  Unknown/Multiple
       Architecture:                 |   Type of failure:  None/Unknown
  Unknown/Multiple                   |
          Test Case:                 |        Blocked By:
           Blocking:                 |   Related Tickets:  #14137 #10918
Differential Rev(s):                 |         Wiki Page:
-------------------------------------+-------------------------------------
 This is a spin off of a discussion from #14137.

 == The problem ==

 We generally avoid inlining or floating a binding into a recursive
 function, because we do not want to duplicat work/allocations.

 But sometimes the binding is only used inside the recursive function on
 the “exit path”. In which case it would be good to inline. Example:

 {{{#!hs
   let x = f x0
   in let go 10 = h x
          go i = go (i+1)
      in go (0::Int) + 100
 }}}

 It would be beneficial to inline `x`.

 The problem is that it is not very obvious that this occurence of `x` is
 ok for inlining. In particular, it is not syntactically visible.

 == Proposed solution ==

 If we apply loopification (#14068), this would turn into

 {{{#!hs
   let x = f x0
   in let go n =
        joinrec jgo 10 = h x
                jgo i = call jgo (i+1)
        in call jgo n
      in go (0::Int) + 100
 }}}

 I’d like to call this ''first joinrec normal form'', defined as “every
 recursive function where all recursive calls are tail-recursive is a
 recursive join point”.

 This ticket proposes to transform this even further and ''float out all
 case alternatives that do not mention `jgo` as non-recursive join-
 points'', as so:

 {{{#!hs
   let x = f x0
   in let go n =
        join jexit = h x
        joinrec jgo 10 = call jexit
                jgo i = call jgo (i+1)
        in call jgo n
      in go (0::Int) + 100
 }}}

 I’d like to call this ''second `joinrec` normal form'', defined as “in
 first `joinrec` normal form, and all subexpressions of a recursive join
 point `j` that are in tail-call position and do not mention `j` are join
 calls”.

 If the floated expression has free variables that are bound inside the
 `joinrec`, they turn into parameters of the newly created joinpoint.

 At this point, GHC can tell that `go` is called at most once, and will
 therefore happily inline `x` into the right hand side of `jexit.

 == Alternative solutions ==

 Ticket #10918 uses Call Arity results to learn that `x` is one-Shot, and
 inline it even in the original code. This works, but the problem is that
 float-out will undo this. See [ticket:10918#comment:5].

 == Limitation ===

 It only works for recursive functions that are join points, or can be
 turned into join points by loopification (#14068). It does not work
 forexample for

 {{{#!hs
 let x = f x0
 let go 0 = h x
     go n = (go (n-1) + 1
 in go 10
 }}}

 although it would be equally desirable to float  `h x` out of `go` so that
 `x` can be inlined.

 == Preservation ==

 A remaining tricky point is that we need to stop one of these carefully-
 constructed non-recursive join points being inlined into a recursive join
 point, even if it is invoked at just one place. That should not be hard.
 And in a final run of the simplifer (or in CorePrep) we could switch off
 that restriction and let it inline. (Ticket #14137 is about inlining
 ''more'' join points into recursive join points, so it is the antithesis
 to the present ticket.)

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


More information about the ghc-tickets mailing list