-- Inlining,
preInlineUnconditionally, postInlineUnconditionally,
- activeInline, activeRule, inlineMode,
+ activeInline, activeRule,
+ simplEnvForGHCi, simplEnvForRules, updModeForInlineRules,
-- The continuation type
SimplCont(..), DupFlag(..), ArgInfo(..),
contIsDupable, contResultType, contIsTrivial, contArgs, dropArgs,
- countValArgs, countArgs, splitInlineCont,
- mkBoringStop, mkLazyArgStop, contIsRhsOrArg,
- interestingCallContext, interestingArgContext,
+ countValArgs, countArgs,
+ mkBoringStop, mkRhsStop, mkLazyArgStop, contIsRhsOrArg,
+ interestingCallContext,
interestingArg, mkArgInfo,
import Outputable
import FastString
-import List( nub )
+import Data.List
\end{code}
mkBoringStop :: SimplCont
mkBoringStop = Stop BoringCtxt
+mkRhsStop :: SimplCont -- See Note [RHS of lets] in CoreUnfold
+mkRhsStop = Stop (ArgCtxt False)
+
mkLazyArgStop :: CallCtxt -> SimplCont
mkLazyArgStop cci = Stop cci
dropArgs 0 cont = cont
dropArgs n (ApplyTo _ _ _ cont) = dropArgs (n-1) cont
dropArgs n other = pprPanic "dropArgs" (ppr n <+> ppr other)
-
---------------------
-splitInlineCont :: SimplCont -> Maybe (SimplCont, SimplCont)
--- Returns Nothing if the continuation should dissolve an InlineMe Note
--- Return Just (c1,c2) otherwise,
--- where c1 is the continuation to put inside the InlineMe
--- and c2 outside
-
--- Example: (__inline_me__ (/\a. e)) ty
--- Here we want to do the beta-redex without dissolving the InlineMe
--- See test simpl017 (and Trac #1627) for a good example of why this is important
-
-splitInlineCont (ApplyTo dup (Type ty) se c)
- | Just (c1, c2) <- splitInlineCont c = Just (ApplyTo dup (Type ty) se c1, c2)
-splitInlineCont cont@(Stop {}) = Just (mkBoringStop, cont)
-splitInlineCont cont@(StrictBind {}) = Just (mkBoringStop, cont)
-splitInlineCont _ = Nothing
- -- NB: we dissolve an InlineMe in any strict context,
- -- not just function aplication.
- -- E.g. foldr k z (__inline_me (case x of p -> build ...))
- -- Here we want to get rid of the __inline_me__ so we
- -- can float the case, and see foldr/build
- --
- -- However *not* in a strict RHS, else we get
- -- let f = __inline_me__ (\x. e) in ...f...
- -- Now if f is guaranteed to be called, hence a strict binding
- -- we don't thereby want to dissolve the __inline_me__; for
- -- example, 'f' might be a wrapper, so we'd inline the worker
\end{code}
where
interesting (Select _ bndr _ _ _)
| isDeadBinder bndr = CaseCtxt
- | otherwise = ArgCtxt False 2 -- If the binder is used, this
+ | otherwise = ArgCtxt False -- If the binder is used, this
-- is like a strict let
+ -- See Note [RHS of lets] in CoreUnfold
interesting (ApplyTo _ arg _ cont)
| isTypeArg arg = interesting cont
-------------------
mkArgInfo :: Id
+ -> [CoreRule] -- Rules for function
-> Int -- Number of value args
-> SimplCont -- Context of the call
-> ArgInfo
-mkArgInfo fun n_val_args call_cont
+mkArgInfo fun rules n_val_args call_cont
| n_val_args < idArity fun -- Note [Unsaturated functions]
= ArgInfo { ai_rules = False
, ai_strs = vanilla_stricts
, ai_discs = vanilla_discounts }
| otherwise
- = ArgInfo { ai_rules = interestingArgContext fun call_cont
+ = ArgInfo { ai_rules = interestingArgContext rules call_cont
, ai_strs = add_type_str (idType fun) arg_stricts
, ai_discs = arg_discounts }
where
vanilla_discounts, arg_discounts :: [Int]
vanilla_discounts = repeat 0
arg_discounts = case idUnfolding fun of
- CoreUnfolding _ _ _ _ _ (UnfoldIfGoodArgs _ discounts _ _)
+ CoreUnfolding {uf_guidance = UnfoldIfGoodArgs {ug_args = discounts}}
-> discounts ++ vanilla_discounts
_ -> vanilla_discounts
on its first argument -- it must be saturated for these to kick in
-}
-interestingArgContext :: Id -> SimplCont -> Bool
+interestingArgContext :: [CoreRule] -> SimplCont -> Bool
-- If the argument has form (f x y), where x,y are boring,
-- and f is marked INLINE, then we don't want to inline f.
-- But if the context of the argument is
-- where h has rules, then we do want to inline f; hence the
-- call_cont argument to interestingArgContext
--
--- The interesting_arg_ctxt flag makes this happen; if it's
+-- The ai-rules flag makes this happen; if it's
-- set, the inliner gets just enough keener to inline f
-- regardless of how boring f's arguments are, if it's marked INLINE
--
-- The alternative would be to *always* inline an INLINE function,
-- regardless of how boring its context is; but that seems overkill
-- For example, it'd mean that wrapper functions were always inlined
-interestingArgContext fn call_cont
- = idHasRules fn || go call_cont
+interestingArgContext rules call_cont
+ = notNull rules || enclosing_fn_has_rules
where
+ enclosing_fn_has_rules = go call_cont
+
go (Select {}) = False
go (ApplyTo {}) = False
go (StrictArg _ cci _ _) = interesting cci
go (CoerceIt _ c) = go c
go (Stop cci) = interesting cci
- interesting (ArgCtxt rules _) = rules
- interesting _ = False
+ interesting (ArgCtxt rules) = rules
+ interesting _ = False
\end{code}
%* *
%************************************************************************
-Inlining is controlled partly by the SimplifierMode switch. This has two
-settings:
+\begin{code}
+simplEnvForGHCi :: SimplEnv
+simplEnvForGHCi = mkSimplEnv allOffSwitchChecker $
+ SimplGently { sm_rules = False, sm_inline = False }
+ -- Do not do any inlining, in case we expose some unboxed
+ -- tuple stuff that confuses the bytecode interpreter
+
+simplEnvForRules :: SimplEnv
+simplEnvForRules = mkSimplEnv allOffSwitchChecker $
+ SimplGently { sm_rules = True, sm_inline = False }
+
+updModeForInlineRules :: SimplifierMode -> SimplifierMode
+updModeForInlineRules mode
+ = case mode of
+ SimplGently {} -> mode -- Don't modify mode if we already gentle
+ SimplPhase {} -> SimplGently { sm_rules = True, sm_inline = True }
+ -- Simplify as much as possible, subject to the usual "gentle" rules
+\end{code}
+Inlining is controlled partly by the SimplifierMode switch. This has two
+settings
+
SimplGently (a) Simplifying before specialiser/full laziness
- (b) Simplifiying inside INLINE pragma
+ (b) Simplifiying inside InlineRules
(c) Simplifying the LHS of a rule
(d) Simplifying a GHCi expression or Template
Haskell splice
SimplPhase n _ Used at all other times
-The key thing about SimplGently is that it does no call-site inlining.
+Note [Gentle mode]
+~~~~~~~~~~~~~~~~~~
+Gentle mode has a separate boolean flag to control
+ a) inlining (sm_inline flag)
+ b) rules (sm_rules flag)
+A key invariant about Gentle mode is that it is treated as the EARLIEST
+phase. Something is inlined if the sm_inline flag is on AND the thing
+is inlinable in the earliest phase. This is important. Example
+
+ {-# INLINE [~1] g #-}
+ g = ...
+
+ {-# INLINE f #-}
+ f x = g (g x)
+
+If we were to inline g into f's inlining, then an importing module would
+never be able to do
+ f e --> g (g e) ---> RULE fires
+because the InlineRule for f has had g inlined into it.
+
+On the other hand, it is bad not to do ANY inlining into an
+InlineRule, because then recursive knots in instance declarations
+don't get unravelled.
+
+However, *sometimes* SimplGently must do no call-site inlining at all.
Before full laziness we must be careful not to inline wrappers,
because doing so inhibits floating
e.g. ...(case f x of ...)...
anything, because the byte-code interpreter might get confused about
unboxed tuples and suchlike.
-INLINE pragmas
-~~~~~~~~~~~~~~
-SimplGently is also used as the mode to simplify inside an InlineMe note.
+Note [RULEs enabled in SimplGently]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RULES are enabled when doing "gentle" simplification. Two reasons:
-\begin{code}
-inlineMode :: SimplifierMode
-inlineMode = SimplGently
-\end{code}
+ * We really want the class-op cancellation to happen:
+ op (df d1 d2) --> $cop3 d1 d2
+ because this breaks the mutual recursion between 'op' and 'df'
-It really is important to switch off inlinings inside such
-expressions. Consider the following example
+ * I wanted the RULE
+ lift String ===> ...
+ to work in Template Haskell when simplifying
+ splices, so we get simpler code for literal strings
+
+Note [Simplifying gently inside InlineRules]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We don't do much simplification inside InlineRules (which come from
+INLINE pragmas). It really is important to switch off inlinings
+inside such expressions. Consider the following example
let f = \pq -> BIG
in
in ...g...g...g...g...g...
Now, if that's the ONLY occurrence of f, it will be inlined inside g,
-and thence copied multiple times when g is inlined.
-
+and thence copied multiple times when g is inlined.
-This function may be inlinined in other modules, so we
-don't want to remove (by inlining) calls to functions that have
-specialisations, or that may have transformation rules in an importing
-scope.
+This function may be inlinined in other modules, so we don't want to
+remove (by inlining) calls to functions that have specialisations, or
+that may have transformation rules in an importing scope.
E.g. {-# INLINE f #-}
- f x = ...g...
+ f x = ...g...
and suppose that g is strict *and* has specialisations. If we inline
g's wrapper, we deny f the chance of getting the specialised version
ArgOf continuations. It shouldn't do any harm not to dissolve the
inline-me note under these circumstances.
-Note that the result is that we do very little simplification
-inside an InlineMe.
+Although we do very little simplification inside an InlineRule,
+the RHS is simplified as normal. For example:
all xs = foldr (&&) True xs
any p = all . map p {-# INLINE any #-}
-Problem: any won't get deforested, and so if it's exported and the
-importer doesn't use the inlining, (eg passes it as an arg) then we
-won't get deforestation at all. We havn't solved this problem yet!
+The RHS of 'any' will get optimised and deforested; but the InlineRule
+will still mention the original RHS.
preInlineUnconditionally
Conclusion: inline top level things gaily until Phase 0 (the last
phase), at which point don't.
+Note [pre/postInlineUnconditionally in gentle mode]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Even in gentle mode we want to do preInlineUnconditionally. The
+reason is that too little clean-up happens if you don't inline
+use-once things. Also a bit of inlining is *good* for full laziness;
+it can expose constant sub-expressions. Example in
+spectral/mandel/Mandel.hs, where the mandelset function gets a useful
+let-float if you inline windowToViewport
+
+However, as usual for Gentle mode, do not inline things that are
+inactive in the intial stages. See Note [Gentle mode].
+
\begin{code}
preInlineUnconditionally :: SimplEnv -> TopLevelFlag -> InId -> InExpr -> Bool
preInlineUnconditionally env top_lvl bndr rhs
where
phase = getMode env
active = case phase of
- SimplGently -> isAlwaysActive act
+ SimplGently {} -> isEarlyActive act
+ -- See Note [pre/postInlineUnconditionally in gentle mode]
SimplPhase n _ -> isActive n act
act = idInlineActivation bndr
\begin{code}
postInlineUnconditionally
:: SimplEnv -> TopLevelFlag
- -> InId -- The binder (an OutId would be fine too)
+ -> OutId -- The binder (an InId would be fine too)
-> OccInfo -- From the InId
-> OutExpr
-> Unfolding
| isLoopBreaker occ_info = False -- If it's a loop-breaker of any kind, don't inline
-- because it might be referred to "earlier"
| isExportedId bndr = False
+ | isInlineRule unfolding = False -- Note [InlineRule and postInlineUnconditionally]
| exprIsTrivial rhs = True
| otherwise
= case occ_info of
where
active = case getMode env of
- SimplGently -> isAlwaysActive act
+ SimplGently {} -> isEarlyActive act
+ -- See Note [pre/postInlineUnconditionally in gentle mode]
SimplPhase n _ -> isActive n act
act = idInlineActivation bndr
activeInline :: SimplEnv -> OutId -> Bool
activeInline env id
+ | isNonRuleLoopBreaker (idOccInfo id) -- Things with an INLINE pragma may have
+ -- an unfolding *and* be a loop breaker
+ = False -- (maybe the knot is not yet untied)
+ | otherwise
= case getMode env of
- SimplGently -> False
- -- No inlining at all when doing gentle stuff,
- -- except for local things that occur once (pre/postInlineUnconditionally)
- -- The reason is that too little clean-up happens if you
- -- don't inline use-once things. Also a bit of inlining is *good* for
- -- full laziness; it can expose constant sub-expressions.
- -- Example in spectral/mandel/Mandel.hs, where the mandelset
- -- function gets a useful let-float if you inline windowToViewport
+ SimplGently { sm_inline = inlining_on }
+ -> inlining_on && isEarlyActive act
+ -- See Note [Gentle mode]
-- NB: we used to have a second exception, for data con wrappers.
-- On the grounds that we use gentle mode for rule LHSs, and
= Nothing -- Rewriting is off
| otherwise
= case getMode env of
- SimplGently -> Just isAlwaysActive
- -- Used to be Nothing (no rules in gentle mode)
- -- Main motivation for changing is that I wanted
- -- lift String ===> ...
- -- to work in Template Haskell when simplifying
- -- splices, so we get simpler code for literal strings
- SimplPhase n _ -> Just (isActive n)
+ SimplGently { sm_rules = rules_on }
+ | rules_on -> Just isEarlyActive -- Note [RULEs enabled in SimplGently]
+ | otherwise -> Nothing
+ SimplPhase n _ -> Just (isActive n)
\end{code}
+Note [InlineRule and postInlineUnconditionally]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Do not do postInlineUnconditionally if the Id has an InlineRule, otherwise
+we lose the unfolding. Example
+
+ -- f has InlineRule with rhs (e |> co)
+ -- where 'e' is big
+ f = e |> co
+
+Then there's a danger we'll optimise to
+
+ f' = e
+ f = f' |> co
+
+and now postInlineUnconditionally, losing the InlineRule on f. Now f'
+won't inline because 'e' is too big.
+
%************************************************************************
%* *
mkLam _b [] body
= return body
-mkLam _env bndrs body
+mkLam env bndrs body
= do { dflags <- getDOptsSmpl
; mkLam' dflags bndrs body }
where
; return etad_lam }
| dopt Opt_DoLambdaEtaExpansion dflags,
- any isRuntimeVar bndrs
+ not (inGentleMode env), -- In gentle mode don't eta-expansion
+ any isRuntimeVar bndrs -- because it can clutter up the code
+ -- with casts etc that may not be removed
= do { let body' = tryEtaExpansion dflags body
; return (mkLams bndrs body') }