Skip to content

WorkWrap: `certainlyWillInline` bindings have non-WW'd unfoldings

In #19727 (closed) we have a situation that is quite similar to

small :: Int -> Int
small x = go 0 x
  where
    go z 0 = z * x
    go z y = go (z+y) (y-1)

Here, small satisfies certainlyWillInline. Thus, we give it a stable unfolding in WW. But in that stable unfolding, the local go function should better have been WW'd! Yet, we see

small :: Int -> Int    
[GblId,
 Arity=1,                                                  
 Str=<1P(SL)>,                    
 Cpr=1,
 Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
         WorkFree=True, Expandable=True,                                                                                   
         Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
         Tmpl= \ (x_auy :: Int) ->
                 joinrec {                                 
                   go_sKU [Occ=LoopBreakerT[2]] :: Int -> Int -> Int
                   [LclId[JoinId(2)], Arity=2, Str=<L><L>, Unf=OtherCon []]
                   go_sKU (z_auA [Occ=Once2!] :: Int) (ds_dKl [Occ=Once1!] :: Int)
                     = case ds_dKl of { GHC.Types.I# ds1_dKm [Occ=Once1!] ->
                       case ds1_dKm of ds2_X3 {                                                                            
                         __DEFAULT ->
                           jump go_sKU
                             (case z_auA of { GHC.Types.I# x1_aKu [Occ=Once1] ->
                              GHC.Types.I# (GHC.Prim.+# x1_aKu ds2_X3)
                              })
                             (GHC.Types.I# (GHC.Prim.-# ds2_X3 1#));
                         0# -> GHC.Num.$fNumInt_$c* z_auA x_auy
                       }  
                       }; } in                                                                                             
                 jump go_sKU Lib.small1 x_auy}]
small   
  = \ (x_auy :: Int) ->
      case x_auy of { GHC.Types.I# ww_sL3 ->
      join {
        exit_X4 [Dmd=SCS(L)] :: GHC.Prim.Int# -> Int
        [LclId[JoinId(1)], Arity=1, Str=<L>]
        exit_X4 (ww1_sKZ [OS=OneShot] :: GHC.Prim.Int#)
          = GHC.Types.I# (GHC.Prim.*# ww1_sKZ ww_sL3) } in
      joinrec {
        $wgo_sL6 [InlPrag=[2], Occ=LoopBreaker, Dmd=SCS(C1(L))]
          :: GHC.Prim.Int# -> GHC.Prim.Int# -> Int
        [LclId[JoinId(2)], Arity=2, Str=<L><1L>, Unf=OtherCon []]
        $wgo_sL6 (ww1_sKZ :: GHC.Prim.Int#) (ww2_X5 :: GHC.Prim.Int#)
          = case ww2_X5 of ds_X3 {
              __DEFAULT ->
                jump $wgo_sL6 (GHC.Prim.+# ww1_sKZ ds_X3) (GHC.Prim.-# ds_X3 1#);
              0# -> jump exit_X4 ww1_sKZ
            }; } in
      jump $wgo_sL6 0# ww_sL3
      }

That's because the call to certainlyWillInline, which is called from splitFun, operates on the unfolding pre-WW. We should update the unfolding here, in wwBind:

wwBind  :: WwOpts
        -> CoreBind
        -> UniqSM [CoreBind]    -- returns a WwBinding intermediate form;
                                -- the caller will convert to Expr/Binding,
                                -- as appropriate.

wwBind ww_opts (NonRec binder rhs) = do
    new_rhs   <- wwExpr ww_opts rhs
    new_pairs <- tryWW ww_opts NonRecursive binder new_rhs
    return [NonRec b e | (b,e) <- new_pairs]

Note the new_rhs passed to tryWW (which calls splitFun), but the lack of updating on binder.

Edited by Simon Peyton Jones
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information