-- Inlining,
preInlineUnconditionally, postInlineUnconditionally,
activeInline, activeRule,
+ simplEnvForGHCi, simplEnvForRules, updModeForInlineRules,
-- The continuation type
SimplCont(..), DupFlag(..), ArgInfo(..),
contIsDupable, contResultType, contIsTrivial, contArgs, dropArgs,
- countValArgs, countArgs,
- mkBoringStop, mkLazyArgStop, contIsRhsOrArg,
+ pushArgs, countValArgs, countArgs, addArgTo,
+ mkBoringStop, mkRhsStop, mkLazyArgStop, contIsRhsOrArg,
interestingCallContext,
interestingArg, mkArgInfo,
| ApplyTo -- C arg
DupFlag
- InExpr SimplEnv -- The argument and its static env
+ InExpr StaticEnv -- The argument and its static env
SimplCont
| Select -- case C of alts
DupFlag
- InId [InAlt] SimplEnv -- The case binder, alts, and subst-env
+ InId [InAlt] StaticEnv -- The case binder, alts, and subst-env
SimplCont
-- The two strict forms have no DupFlag, because we never duplicate them
| StrictBind -- (\x* \xs. e) C
InId [InBndr] -- let x* = [] in e
- InExpr SimplEnv -- is a special case
+ InExpr StaticEnv -- is a special case
SimplCont
- | StrictArg -- e C
- OutExpr -- e; *always* of form (Var v `App1` e1 .. `App` en)
- CallCtxt -- Whether *this* argument position is interesting
- ArgInfo -- Whether the function at the head of e has rules, etc
- SimplCont -- plus strictness flags for *further* args
+ | StrictArg -- f e1 ..en C
+ ArgInfo -- Specifies f, e1..en, Whether f has rules, etc
+ -- plus strictness flags for *further* args
+ CallCtxt -- Whether *this* argument position is interesting
+ SimplCont
data ArgInfo
= ArgInfo {
- ai_rules :: Bool, -- Function has rules (recursively)
- -- => be keener to inline in all args
- ai_strs :: [Bool], -- Strictness of arguments
+ ai_fun :: Id, -- The function
+ ai_args :: [OutExpr], -- ...applied to these args (which are in *reverse* order)
+ ai_rules :: [CoreRule], -- Rules for this function
+
+ ai_encl :: Bool, -- Flag saying whether this function
+ -- or an enclosing one has rules (recursively)
+ -- True => be keener to inline in all args
+
+ ai_strs :: [Bool], -- Strictness of remaining arguments
-- Usually infinite, but if it is finite it guarantees
-- that the function diverges after being given
-- that number of args
- ai_discs :: [Int] -- Discounts for arguments; non-zero => be keener to inline
+ ai_discs :: [Int] -- Discounts for remaining arguments; non-zero => be keener to inline
-- Always infinite
}
+addArgTo :: ArgInfo -> OutExpr -> ArgInfo
+addArgTo ai arg = ai { ai_args = arg : ai_args ai }
+
instance Outputable SimplCont where
ppr (Stop interesting) = ptext (sLit "Stop") <> brackets (ppr interesting)
ppr (ApplyTo dup arg _ cont) = ((ptext (sLit "ApplyTo") <+> ppr dup <+> pprParendExpr arg)
{- $$ nest 2 (pprSimplEnv se) -}) $$ ppr cont
ppr (StrictBind b _ _ _ cont) = (ptext (sLit "StrictBind") <+> ppr b) $$ ppr cont
- ppr (StrictArg f _ _ cont) = (ptext (sLit "StrictArg") <+> ppr f) $$ ppr cont
+ ppr (StrictArg ai _ cont) = (ptext (sLit "StrictArg") <+> ppr (ai_fun ai)) $$ ppr cont
ppr (Select dup bndr alts _ cont) = (ptext (sLit "Select") <+> ppr dup <+> ppr bndr) $$
(nest 4 (ppr alts)) $$ ppr cont
ppr (CoerceIt co cont) = (ptext (sLit "CoerceIt") <+> ppr co) $$ ppr cont
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
go (Stop {}) ty = ty
go (CoerceIt co cont) _ = go cont (snd (coercionKind co))
go (StrictBind _ bs body se cont) _ = go cont (subst_ty se (exprType (mkLams bs body)))
- go (StrictArg fn _ _ cont) _ = go cont (funResultTy (exprType fn))
+ go (StrictArg ai _ cont) _ = go cont (funResultTy (argInfoResultTy ai))
go (Select _ _ alts se cont) _ = go cont (subst_ty se (coreAltsType alts))
go (ApplyTo _ arg se cont) ty = go cont (apply_to_arg ty arg se)
apply_to_arg ty (Type ty_arg) se = applyTy ty (subst_ty se ty_arg)
apply_to_arg ty _ _ = funResultTy ty
+argInfoResultTy :: ArgInfo -> OutType
+argInfoResultTy (ArgInfo { ai_fun = fun, ai_args = args })
+ = foldr (\arg fn_ty -> applyTypeToArg fn_ty arg) (idType fun) args
+
-------------------
countValArgs :: SimplCont -> Int
countValArgs (ApplyTo _ (Type _) _ cont) = countValArgs cont
go args (ApplyTo _ arg se cont) = go (substExpr se arg : args) cont
go args cont = (reverse args, cont)
+pushArgs :: SimplEnv -> [CoreExpr] -> SimplCont -> SimplCont
+pushArgs _env [] cont = cont
+pushArgs env (arg:args) cont = ApplyTo NoDup arg env (pushArgs env args cont)
+
dropArgs :: Int -> SimplCont -> SimplCont
dropArgs 0 cont = cont
dropArgs n (ApplyTo _ _ _ cont) = dropArgs (n-1) cont
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
-- motivation to inline. See Note [Cast then apply]
-- in CoreUnfold
- interesting (StrictArg _ cci _ _) = cci
- interesting (StrictBind {}) = BoringCtxt
- interesting (Stop cci) = cci
- interesting (CoerceIt _ cont) = interesting cont
+ interesting (StrictArg _ cci _) = cci
+ interesting (StrictBind {}) = BoringCtxt
+ interesting (Stop cci) = cci
+ interesting (CoerceIt _ cont) = interesting cont
-- If this call is the arg of a strict function, the context
-- is a bit interesting. If we inline here, we may get useful
-- evaluation information to avoid repeated evals: e.g.
mkArgInfo fun rules n_val_args call_cont
| n_val_args < idArity fun -- Note [Unsaturated functions]
- = ArgInfo { ai_rules = False
+ = ArgInfo { ai_fun = fun, ai_args = [], ai_rules = rules
+ , ai_encl = False
, ai_strs = vanilla_stricts
, ai_discs = vanilla_discounts }
| otherwise
- = ArgInfo { ai_rules = interestingArgContext rules call_cont
+ = ArgInfo { ai_fun = fun, ai_args = [], ai_rules = rules
+ , ai_encl = interestingArgContext rules call_cont
, ai_strs = add_type_str (idType fun) arg_stricts
, ai_discs = arg_discounts }
where
where
enclosing_fn_has_rules = go call_cont
- go (Select {}) = False
- go (ApplyTo {}) = False
- go (StrictArg _ cci _ _) = interesting cci
- go (StrictBind {}) = False -- ??
- go (CoerceIt _ c) = go c
- go (Stop cci) = interesting cci
+ go (Select {}) = False
+ go (ApplyTo {}) = False
+ go (StrictArg _ cci _) = interesting cci
+ go (StrictBind {}) = False -- ??
+ 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
-~~~~~~~~~~~~~~
-We don't simplify inside InlineRules (which come from INLINE pragmas).
-It really is important to switch off inlinings inside such
-expressions. Consider the following example
+Note [RULEs enabled in SimplGently]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RULES are enabled when doing "gentle" simplification. Two reasons:
+
+ * 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'
+
+ * 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 -> isEarlyActive act
+ SimplGently {} -> isEarlyActive act
+ -- See Note [pre/postInlineUnconditionally in gentle mode]
SimplPhase n _ -> isActive n act
act = idInlineActivation bndr
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]