module WorkWrap ( wwTopBinds, mkWrapper ) where
import CoreSyn
-import CoreUnfold ( certainlyWillInline, mkInlineRule, mkWwInlineRule )
+import CoreUnfold ( certainlyWillInline, mkInlineUnfolding, mkWwInlineRule )
import CoreUtils ( exprType, exprIsHNF )
import CoreArity ( exprArity )
import Var
import Id
import Type ( Type )
import IdInfo
-import Demand ( Demand(..), StrictSig(..), DmdType(..), DmdResult(..),
- Demands(..), mkTopDmdType, isBotRes, returnsCPR, topSig, isAbsent
- )
+import Demand
import UniqSupply
-import BasicTypes ( RecFlag(..), isNonRec, isNeverActive,
- Activation(..), InlinePragma(..),
- inlinePragmaActivation, inlinePragmaRuleMatchInfo )
+import BasicTypes
import VarEnv ( isEmptyVarEnv )
import Maybes ( orElse )
import WwLib
The only reason this is monadised is for the unique supply.
-Note [Don't w/w inline things (a)]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
+Note [Don't w/w INLINE things]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It's very important to refrain from w/w-ing an INLINE function (ie one
with an InlineRule) because the wrapper will then overwrite the
InlineRule unfolding.
in a recursive group. It might not be the loop breaker. (We could
test for loop-breaker-hood, but I'm not sure that ever matters.)
-Note [Don't w/w inline things (b)]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-In general, we refrain from w/w-ing *small* functions, because they'll
-inline anyway. But we must take care: it may look small now, but get
-to be big later after other inling has happened. So we take the
-precaution of adding an INLINE pragma to any such functions.
+Note [Don't w/w INLINABLE things]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If we have
+ {-# INLINABLE f #-}
+ f x y = ....
+then in principle we might get a more efficient loop by w/w'ing f.
+But that would make a new unfolding which would overwrite the old
+one. So we leave INLINABLE things alone too.
+
+This is a slight infelicity really, because it means that adding
+an INLINABLE pragma could make a program a bit less efficient,
+because you lose the worker/wrapper stuff. But I don't see a way
+to avoid that.
+
+Note [Don't w/w inline small non-loop-breaker things]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+In general, we refrain from w/w-ing *small* functions, which are not
+loop breakers, because they'll inline anyway. But we must take care:
+it may look small now, but get to be big later after other inlining
+has happened. So we take the precaution of adding an INLINE pragma to
+any such functions.
I made this change when I observed a big function at the end of
-compilation with a useful strictness signature but no w-w. When
-I measured it on nofib, it didn't make much difference; just a few
-percent improved allocation on one benchmark (bspt/Euclid.space).
-But nothing got worse.
+compilation with a useful strictness signature but no w-w. (It was
+small during demand analysis, we refrained from w/w, and then got big
+when something was inlined in its rhs.) When I measured it on nofib,
+it didn't make much difference; just a few percent improved allocation
+on one benchmark (bspt/Euclid.space). But nothing got worse.
+
+There is an infelicity though. We may get something like
+ f = g val
+==>
+ g x = case gw x of r -> I# r
+
+ f {- InlineStable, Template = g val -}
+ f = case gw x of r -> I# r
+
+The code for f duplicates that for g, without any real benefit. It
+won't really be executed, because calls to f will go via the inlining.
Note [Wrapper activation]
~~~~~~~~~~~~~~~~~~~~~~~~~
---------------------
checkSize :: Id -> CoreExpr
-> UniqSM [(Id,CoreExpr)] -> UniqSM [(Id,CoreExpr)]
- -- See Note [Don't w/w inline things (a) and (b)]
checkSize fn_id rhs thing_inside
- | isStableUnfolding unfolding -- For DFuns and INLINE things, leave their
- = return [ (fn_id, rhs) ] -- unfolding unchanged; but still attach
- -- strictness info to the Id
+ | isStableUnfolding (realIdUnfolding fn_id)
+ = return [ (fn_id, rhs) ]
+ -- See Note [Don't w/w INLINABLE things]
+ -- and Note [Don't w/w INLINABLABLE things]
+ -- NB: use realIdUnfolding because we want to see the unfolding
+ -- even if it's a loop breaker!
- | certainlyWillInline unfolding
+ | certainlyWillInline (idUnfolding fn_id)
= return [ (fn_id `setIdUnfolding` inline_rule, rhs) ]
- -- Note [Don't w/w inline things (b)]
+ -- Note [Don't w/w inline small non-loop-breaker things]
+ -- NB: use idUnfolding because we don't want to apply
+ -- this criterion to a loop breaker!
| otherwise = thing_inside
where
- unfolding = idUnfolding fn_id
- inline_rule = mkInlineRule rhs Nothing
+ inline_rule = mkInlineUnfolding Nothing rhs
---------------------
splitFun :: Id -> IdInfo -> [Demand] -> DmdResult -> Expr Var
-- arity is consistent with the demand type goes through
wrap_rhs = wrap_fn work_id
- wrap_prag = InlinePragma { inl_inline = True
+ wrap_prag = InlinePragma { inl_inline = Inline
, inl_sat = Nothing
, inl_act = ActiveAfter 0
, inl_rule = rule_match_info }
-- in case x of
-- I# y -> let x = I# y in x }
-- See comments above. Is it not beautifully short?
+-- Moreover, it works just as well when there are
+-- several binders, and if the binders are lifted
+-- E.g. x = e
+-- --> x = let x = e in
+-- case x of (a,b) -> let x = (a,b) in x
splitThunk :: Var -> Expr Var -> UniqSM [(Var, Expr Var)]
splitThunk fn_id rhs = do