[GHC] #12808: For non-strict code including primitive (Addr#) code, Loop Invariant Code Flow not lifted outside the loop...

GHC ghc-devs at haskell.org
Fri Dec 9 23:43:51 UTC 2016


#12808: For non-strict code including primitive (Addr#) code, Loop Invariant Code
Flow not lifted outside the loop...
-------------------------------------+-------------------------------------
        Reporter:  GordonBGood       |                Owner:
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:  8.2.1
       Component:  Compiler          |              Version:  8.0.1
      Resolution:                    |             Keywords:  JoinPoints
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:                    |
-------------------------------------+-------------------------------------
Changes (by simonpj):

 * keywords:   => JoinPoints
 * cc: maurerl@… (added)


Comment:

 Interesting.  Looking at comment:12, note the difference between the SLOW
 version:
 {{{
 let nxtc c = if c > top
              then return ()
              else do { ...; nxtc (c+p) }
 in do { nxtc ss; nxtp (p + 1) }
 }}}
 and the FAST version
 {{{
 let nxtc c = if c > top
              then nxtp (p + 1) else do
              else do { ...; nxtc (c+p) }
 in nxtc ss
 }}}
 In SLOW, `nxtc` is represented by a heap-allocated closure, whereas in
 FAST `nxtc` is a join point, and hence not allocated at all (you can see
 that from the `let-no-escape` in the STG).  See our paper
 [https://www.microsoft.com/en-us/research/publication/compiling-without-
 continuations/ Compiling without continuations], and [wiki:SequentCore].

 Notice that in SLOW, the call to `nxtc s` is ''followed by'' a call to
 `nxtp (p+1)`.  But in FAST we move that call right into `nxtc` itself, in
 the return branch of the `if`.  That's what makes `nxtc` into a join
 point.

 (None of this has anything to do with non-strictness, incidentally.)

 This is a rather non-trivial transformation. You clearly think it's a
 pretty obvious optimisation, but it doesn't look obvious to me.

 Happily, though, our new Core-with-join-point (described in the paper)
 should catch this nicely.  If we start with SLOW, after a bit we'll get
 this
 {{{
 let nxtc c s = if c > top
                then (# s,c #)
                else case ... of (# s',p #) -> nxtc (c+p) s' }
 in case nxtc ss s of
      (# s', r #) -> nxtp (p + 1) s' }
 }}}
 Now if we do float-in, to move the `let nxtc` in to the scruintee of the
 case, it becomes a join point, and join-point analysis should find it.
 After that, the transformations in the paper will turn it into FAST.

 The example in comment:10 looks similar:
 {{{
   case doit adr# sp# of sd# -> cullp (p + 2) sd# in cullp 1 s4# }}}}}
 }}}
 Here again, `doit` will become a join point if we float it in.  The
 example in the Descrption is too big for me to analyse.

 I've cc'd Luke Maurer who is implementing Core with join points; this
 looks like another good example.  (cf #12781)

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


More information about the ghc-tickets mailing list