[GHC] #14620: Polymorphic functions not easily recognized as join points

GHC ghc-devs at haskell.org
Tue Dec 26 22:55:23 UTC 2017


#14620: Polymorphic functions not easily recognized as join points
-------------------------------------+-------------------------------------
           Reporter:  dfeuer         |             Owner:  (none)
               Type:  bug            |            Status:  new
           Priority:  normal         |         Milestone:  8.6.1
          Component:  Compiler       |           Version:  8.2.2
           Keywords:                 |  Operating System:  Unknown/Multiple
       Architecture:                 |   Type of failure:  Runtime
  Unknown/Multiple                   |  performance bug
          Test Case:                 |        Blocked By:
           Blocking:                 |   Related Tickets:  #14610
Differential Rev(s):                 |         Wiki Page:
-------------------------------------+-------------------------------------
 This grew out of ticket:14610#comment:3. If I write

 {{{#!hs
 foo :: forall a. (Int -> Bool) -> Int -> a -> a
 foo p = go
   where
     go :: Int -> a -> a
     go !n a
       | p n = a
       | otherwise = go (n + 1) a
 }}}

 then I get

 {{{
 foo
   = \ (@ a_aYZ)
       (p_aWO :: Int -> Bool)
       (eta_B2 :: Int)
       (eta1_B1 :: a_aYZ) ->
       case eta_B2 of { GHC.Types.I# ww1_s1bZ ->
       joinrec {
         $wgo_s1c1 [InlPrag=NOUSERINLINE[0], Occ=LoopBreaker]
           :: GHC.Prim.Int# -> a_aYZ -> a_aYZ
         [LclId[JoinId(2)], Arity=2, Str=<L,U><S,1*U>, Unf=OtherCon []]
         $wgo_s1c1 (ww2_X1cu :: GHC.Prim.Int#) (w_s1bW :: a_aYZ)
           = case p_aWO (GHC.Types.I# ww2_X1cu) of {
               False -> jump $wgo_s1c1 (GHC.Prim.+# ww2_X1cu 1#) w_s1bW;
               True -> w_s1bW
             }; } in
       jump $wgo_s1c1 ww1_s1bZ eta1_B1
       }
 }}}

 But if I make `go` polymorphic,

 {{{#!hs
 foo :: (Int -> Bool) -> Int -> a -> a
 foo p = go
   where
     go :: Int -> b -> b
     go !n a
       | p n = a
       | otherwise = go (n + 1) a
 }}}

 I get a wrapper and this worker:

 {{{#!hs
 T14610.$wfoo
   = \ (@ a_s1cm)
       (w_s1cn :: Int -> Bool)
       (ww_s1cs :: GHC.Prim.Int#)
       (w1_s1cp :: a_s1cm) ->
       letrec {
         $wgo_s1cl [InlPrag=NOUSERINLINE[0], Occ=LoopBreaker]
           :: forall b. GHC.Prim.Int# -> b -> b
         [LclId, Arity=2, Str=<L,U><S,1*U>, Unf=OtherCon []]
         $wgo_s1cl
           = \ (@ b_s1ce) (ww1_s1cj :: GHC.Prim.Int#) (w2_s1cg :: b_s1ce)
 ->
               case w_s1cn (GHC.Types.I# ww1_s1cj) of {
                 False -> $wgo_s1cl @ b_s1ce (GHC.Prim.+# ww1_s1cj 1#)
 w2_s1cg;
                 True -> w2_s1cg
               }; } in
       $wgo_s1cl @ a_s1cm ww_s1cs w1_s1cp
 }}}

 This distinction remains as `let` vs. `let-no-escape` in STG. As Joachim
 Breitner's comments seem to suggest, we could probably recognize this by
 applying the static argument transformation to the type argument of `go`.
 But we don't currently have any machinery for doing that, I don't think.
 Furthermore, that would fail with polymorphic recursion even if the only
 type changes are from `newtype`. That said, the SAT approach would
 presumably help when the worker has a non-essential type signature for
 clarity.

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


More information about the ghc-tickets mailing list