[GHC] #13143: NOINLINE and worker/wrapper

GHC ghc-devs at haskell.org
Mon Jan 23 21:37:42 UTC 2017


#13143: NOINLINE and worker/wrapper
-------------------------------------+-------------------------------------
        Reporter:  simonpj           |                Owner:
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:
       Component:  Compiler          |              Version:  8.0.1
      Resolution:                    |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  None/Unknown      |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by simonpj):

 I made several errors.  First, here's a better test
 {{{
 {-# NOINLINE f #-}
 f :: Int -> a
 f x = error (show x)

 g :: Bool -> Bool -> Int -> Int
 g True  True  p = f p
 g False True  p = p + 1
 g b     False p = g b True p
 }}}
 I've made `g` recursive to guarantee a w/w split.  And I've given it a
 type signature to avoid the overloading.

 With HEAD we get
 {{{
 $wg_s2kz [InlPrag=[0], Occ=LoopBreaker]
   :: Bool -> Bool -> GHC.Prim.Int# -> GHC.Prim.Int#
 $wg_s2kz =
   \ (w_s2kp :: Bool) (w_s2kq :: Bool) (ww_s2ku :: GHC.Prim.Int#) ->
     case w_s2kp of {
       False ->
         case w_s2kq of {
           False -> $wg_s2kz GHC.Types.False GHC.Types.True ww_s2ku;
           True -> GHC.Prim.+# ww_s2ku 1#
         };
       True ->
         case w_s2kq of {
           False -> $wg_s2kz GHC.Types.True GHC.Types.True ww_s2ku;
           True -> case f @ Int (GHC.Types.I# ww_s2ku) of wild_00 { }
         }
     }
 }}}
 Note the re-boxing of the argument `(I# ww_s2ku)`, in the call to `f`.

 Second, here is a patch that does the job
 {{{
 diff --git a/compiler/coreSyn/CoreUnfold.hs
 b/compiler/coreSyn/CoreUnfold.hs
 index f7e4265..efae22c 100644
 --- a/compiler/coreSyn/CoreUnfold.hs
 +++ b/compiler/coreSyn/CoreUnfold.hs
 @@ -54,7 +54,7 @@ import DataCon
  import Literal
  import PrimOp
  import IdInfo
 -import BasicTypes       ( Arity )
 +import BasicTypes       ( Arity, InlineSpec(..), inlinePragmaSpec )
  import Type
  import PrelNames
  import TysPrim          ( realWorldStatePrimTy )
 @@ -997,6 +997,9 @@ certainlyWillInline dflags fn_info
          --    See Note [certainlyWillInline: INLINABLE]
      do_cunf expr (UnfIfGoodArgs { ug_size = size, ug_args = args })
        | not (null args)  -- See Note [certainlyWillInline: be careful of
 thunks]
 +      , case inlinePragmaSpec (inlinePragInfo fn_info) of
 +          NoInline -> False -- NOINLINE; do not say certainlyWillInline!
 +          _        -> True  -- INLINE, INLINABLE, or nothing
        , let arity = length args
        , size - (10 * (arity + 1)) <= ufUseThreshold dflags
        = Just (fn_unf { uf_src      = InlineStable
 diff --git a/compiler/stranal/WorkWrap.hs b/compiler/stranal/WorkWrap.hs
 index d50bb22..9a0ccc5 100644
 --- a/compiler/stranal/WorkWrap.hs
 +++ b/compiler/stranal/WorkWrap.hs
 @@ -283,12 +283,6 @@ tryWW   :: DynFlags
                                          -- if two, then a worker and a
                                          -- wrapper.
  tryWW dflags fam_envs is_rec fn_id rhs
 -  | isNeverActive inline_act
 -        -- No point in worker/wrappering if the thing is never inlined!
 -        -- Because the no-inline prag will prevent the wrapper ever
 -        -- being inlined at a call site.
 -  = return [ (new_fn_id, rhs) ]
 -
    | Just stable_unf <- certainlyWillInline dflags fn_info
    = return [ (fn_id `setIdUnfolding` stable_unf, rhs) ]
          -- See Note [Don't w/w INLINE things]
 @@ -305,7 +299,6 @@ tryWW dflags fam_envs is_rec fn_id rhs

    where
      fn_info      = idInfo fn_id
 -    inline_act   = inlinePragmaActivation (inlinePragInfo fn_info)
      (wrap_dmds, res_info) = splitStrictSig (strictnessInfo fn_info)

      new_fn_id = zapIdUsedOnceInfo (zapIdUsageEnvInfo fn_id)
 }}}
 There are two components

 * Remove the `isNeverActtive` branch in `tryWW`, as described above

 * Make `certainlyWillInline` return `False` for any NOINLINE thing.   I
 hadn't realised that this would be necessary but it's obviously true that
 a NOINLINE thing shouldn't reply `True` to `certainlyWillInline`.

 Now we get no re-boxing in the example.

 Would you like to try that?

 Simon

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


More information about the ghc-tickets mailing list