summaryrefslogtreecommitdiff
path: root/compiler/simplCore/SimplUtils.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/simplCore/SimplUtils.hs')
-rw-r--r--compiler/simplCore/SimplUtils.hs462
1 files changed, 329 insertions, 133 deletions
diff --git a/compiler/simplCore/SimplUtils.hs b/compiler/simplCore/SimplUtils.hs
index e6e660b91f..ca1b9bd23d 100644
--- a/compiler/simplCore/SimplUtils.hs
+++ b/compiler/simplCore/SimplUtils.hs
@@ -17,8 +17,8 @@ module SimplUtils (
simplEnvForGHCi, updModeForStableUnfoldings, updModeForRules,
-- The continuation type
- SimplCont(..), DupFlag(..),
- isSimplified,
+ SimplCont(..), DupFlag(..), StaticEnv,
+ isSimplified, contIsStop,
contIsDupable, contResultType, contHoleType,
contIsTrivial, contArgs,
countArgs,
@@ -30,13 +30,18 @@ module SimplUtils (
addValArgTo, addCastTo, addTyArgTo,
argInfoExpr, argInfoAppArgs, pushSimplifiedArgs,
- abstractFloats
+ abstractFloats,
+
+ -- Utilities
+ isExitJoinId
) where
#include "HsVersions.h"
+import GhcPrelude
+
import SimplEnv
-import CoreMonad ( SimplifierMode(..), Tick(..) )
+import CoreMonad ( SimplMode(..), Tick(..) )
import DynFlags
import CoreSyn
import qualified CoreSubst
@@ -57,6 +62,7 @@ import DataCon ( dataConWorkId, isNullaryRepDataCon )
import VarSet
import BasicTypes
import Util
+import OrdList ( isNilOL )
import MonadUtils
import Outputable
import Pair
@@ -114,7 +120,7 @@ data SimplCont
| ApplyToVal -- (ApplyToVal arg K)[e] = K[ e arg ]
{ sc_dup :: DupFlag -- See Note [DupFlag invariants]
, sc_arg :: InExpr -- The argument,
- , sc_env :: StaticEnv -- and its static env
+ , sc_env :: StaticEnv -- see Note [StaticEnv invariant]
, sc_cont :: SimplCont }
| ApplyToTy -- (ApplyToTy ty K)[e] = K[ e ty ]
@@ -127,7 +133,7 @@ data SimplCont
{ sc_dup :: DupFlag -- See Note [DupFlag invariants]
, sc_bndr :: InId -- case binder
, sc_alts :: [InAlt] -- Alternatives
- , sc_env :: StaticEnv -- and their static environment
+ , sc_env :: StaticEnv -- See Note [StaticEnv invariant]
, sc_cont :: SimplCont }
-- The two strict forms have no DupFlag, because we never duplicate them
@@ -137,7 +143,7 @@ data SimplCont
, sc_bndr :: InId
, sc_bndrs :: [InBndr]
, sc_body :: InExpr
- , sc_env :: StaticEnv
+ , sc_env :: StaticEnv -- See Note [StaticEnv invariant]
, sc_cont :: SimplCont }
| StrictArg -- (StrictArg (f e1 ..en) K)[e] = K[ f e1 .. en e ]
@@ -151,6 +157,8 @@ data SimplCont
(Tickish Id) -- Tick tickish <hole>
SimplCont
+type StaticEnv = SimplEnv -- Just the static part is relevant
+
data DupFlag = NoDup -- Unsimplified, might be big
| Simplified -- Simplified
| OkToDup -- Simplified and small
@@ -164,7 +172,25 @@ perhapsSubstTy dup env ty
| isSimplified dup = ty
| otherwise = substTy env ty
-{-
+{- Note [StaticEnv invariant]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We pair up an InExpr or InAlts with a StaticEnv, which establishes the
+lexical scope for that InExpr. When we simplify that InExpr/InAlts, we
+use
+ - Its captured StaticEnv
+ - Overriding its InScopeSet with the larger one at the
+ simplification point.
+
+Why override the InScopeSet? Example:
+ (let y = ey in f) ex
+By the time we simplify ex, 'y' will be in scope.
+
+However the InScopeSet in the StaticEnv is not irrelevant: it should
+include all the free vars of applying the substitution to the InExpr.
+Reason: contHoleType uses perhapsSubstTy to apply the substitution to
+the expression, and that (rightly) gives ASSERT failures if the InScopeSet
+isn't big enough.
+
Note [DupFlag invariants]
~~~~~~~~~~~~~~~~~~~~~~~~~
In both (ApplyToVal dup _ env k)
@@ -196,7 +222,7 @@ instance Outputable SimplCont where
= (text "StrictArg" <+> ppr (ai_fun ai)) $$ ppr cont
ppr (Select { sc_dup = dup, sc_bndr = bndr, sc_alts = alts, sc_env = se, sc_cont = cont })
= (text "Select" <+> ppr dup <+> ppr bndr) $$
- ifPprDebug (nest 2 $ vcat [ppr (seTvSubst se), ppr alts]) $$ ppr cont
+ whenPprDebug (nest 2 $ vcat [ppr (seTvSubst se), ppr alts]) $$ ppr cont
{- Note [The hole type in ApplyToTy]
@@ -345,6 +371,10 @@ contIsRhs (Stop _ RhsCtxt) = True
contIsRhs _ = False
-------------------
+contIsStop :: SimplCont -> Bool
+contIsStop (Stop {}) = True
+contIsStop _ = False
+
contIsDupable :: SimplCont -> Bool
contIsDupable (Stop {}) = True
contIsDupable (ApplyToTy { sc_cont = k }) = contIsDupable k
@@ -419,23 +449,25 @@ contArgs cont
-------------------
-mkArgInfo :: Id
+mkArgInfo :: SimplEnv
+ -> Id
-> [CoreRule] -- Rules for function
-> Int -- Number of value args
-> SimplCont -- Context of the call
-> ArgInfo
-mkArgInfo fun rules n_val_args call_cont
+mkArgInfo env fun rules n_val_args call_cont
| n_val_args < idArity fun -- Note [Unsaturated functions]
= ArgInfo { ai_fun = fun, ai_args = [], ai_type = fun_ty
- , ai_rules = fun_rules, ai_encl = False
+ , ai_rules = fun_rules
+ , ai_encl = False
, ai_strs = vanilla_stricts
, ai_discs = vanilla_discounts }
| otherwise
= ArgInfo { ai_fun = fun, ai_args = [], ai_type = fun_ty
, ai_rules = fun_rules
- , ai_encl = interestingArgContext rules call_cont
- , ai_strs = add_type_str fun_ty arg_stricts
+ , ai_encl = interestingArgContext rules call_cont
+ , ai_strs = arg_stricts
, ai_discs = arg_discounts }
where
fun_ty = idType fun
@@ -453,7 +485,11 @@ mkArgInfo fun rules n_val_args call_cont
vanilla_stricts = repeat False
arg_stricts
- = case splitStrictSig (idStrictness fun) of
+ | not (sm_inline (seMode env))
+ = vanilla_stricts -- See Note [Do not expose strictness if sm_inline=False]
+ | otherwise
+ = add_type_str fun_ty $
+ case splitStrictSig (idStrictness fun) of
(demands, result_info)
| not (demands `lengthExceeds` n_val_args)
-> -- Enough args, use the strictness given.
@@ -475,26 +511,25 @@ mkArgInfo fun rules n_val_args call_cont
add_type_str :: Type -> [Bool] -> [Bool]
-- If the function arg types are strict, record that in the 'strictness bits'
-- No need to instantiate because unboxed types (which dominate the strict
- -- types) can't instantiate type variables.
- -- add_type_str is done repeatedly (for each call); might be better
- -- once-for-all in the function
+ -- types) can't instantiate type variables.
+ -- add_type_str is done repeatedly (for each call);
+ -- might be better once-for-all in the function
-- But beware primops/datacons with no strictness
- add_type_str
- = go
- where
- go _ [] = []
- go fun_ty strs -- Look through foralls
- | Just (_, fun_ty') <- splitForAllTy_maybe fun_ty -- Includes coercions
- = go fun_ty' strs
- go fun_ty (str:strs) -- Add strict-type info
- | Just (arg_ty, fun_ty') <- splitFunTy_maybe fun_ty
- = (str || Just False == isLiftedType_maybe arg_ty) : go fun_ty' strs
- -- If the type is levity-polymorphic, we can't know whether it's
- -- strict. isLiftedType_maybe will return Just False only when
- -- we're sure the type is unlifted.
- go _ strs
- = strs
+ add_type_str _ [] = []
+ add_type_str fun_ty all_strs@(str:strs)
+ | Just (arg_ty, fun_ty') <- splitFunTy_maybe fun_ty -- Add strict-type info
+ = (str || Just False == isLiftedType_maybe arg_ty)
+ : add_type_str fun_ty' strs
+ -- If the type is levity-polymorphic, we can't know whether it's
+ -- strict. isLiftedType_maybe will return Just False only when
+ -- we're sure the type is unlifted.
+
+ | Just (_, fun_ty') <- splitForAllTy_maybe fun_ty
+ = add_type_str fun_ty' all_strs -- Look through foralls
+
+ | otherwise
+ = all_strs
{- Note [Unsaturated functions]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -504,6 +539,28 @@ Consider (test eyeball/inline4)
where f has arity 2. Then we do not want to inline 'x', because
it'll just be floated out again. Even if f has lots of discounts
on its first argument -- it must be saturated for these to kick in
+
+Note [Do not expose strictness if sm_inline=False]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Trac #15163 showed a case in which we had
+
+ {-# INLINE [1] zip #-}
+ zip = undefined
+
+ {-# RULES "foo" forall as bs. stream (zip as bs) = ..blah... #-}
+
+If we expose zip's bottoming nature when simplifing the LHS of the
+RULE we get
+ {-# RULES "foo" forall as bs.
+ stream (case zip of {}) = ..blah... #-}
+discarding the arguments to zip. Usually this is fine, but on the
+LHS of a rule it's not, because 'as' and 'bs' are now not bound on
+the LHS.
+
+This is a pretty pathalogical example, so I'm not losing sleep over
+it, but the simplest solution was to check sm_inline; if it is False,
+which it is on the LHS of a rule (see updModeForRules), then don't
+make use of the strictness info for the function.
-}
@@ -546,14 +603,31 @@ since we can just eliminate this case instead (x is in WHNF). Similar
applies when x is bound to a lambda expression. Hence
contIsInteresting looks for case expressions with just a single
default case.
+
+Note [No case of case is boring]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If we see
+ case f x of <alts>
+
+we'd usually treat the context as interesting, to encourage 'f' to
+inline. But if case-of-case is off, it's really not so interesting
+after all, because we are unlikely to be able to push the case
+expression into the branches of any case in f's unfolding. So, to
+reduce unnecessary code expansion, we just make the context look boring.
+This made a small compile-time perf improvement in perf/compiler/T6048,
+and it looks plausible to me.
-}
-interestingCallContext :: SimplCont -> CallCtxt
+interestingCallContext :: SimplEnv -> SimplCont -> CallCtxt
-- See Note [Interesting call context]
-interestingCallContext cont
+interestingCallContext env cont
= interesting cont
where
- interesting (Select {}) = CaseCtxt
+ interesting (Select {})
+ | sm_case_case (getMode env) = CaseCtxt
+ | otherwise = BoringCtxt
+ -- See Note [No case of case is boring]
+
interesting (ApplyToVal {}) = ValAppCtxt
-- Can happen if we have (f Int |> co) y
-- If f has an INLINE prag we need to give it some
@@ -694,11 +768,11 @@ interestingArg env e = go env 0 e
{-
************************************************************************
* *
- SimplifierMode
+ SimplMode
* *
************************************************************************
-The SimplifierMode controls several switches; see its definition in
+The SimplMode controls several switches; see its definition in
CoreMonad
sm_rules :: Bool -- Whether RULES are enabled
sm_inline :: Bool -- Whether inlining is enabled
@@ -708,19 +782,20 @@ CoreMonad
simplEnvForGHCi :: DynFlags -> SimplEnv
simplEnvForGHCi dflags
- = mkSimplEnv $ SimplMode { sm_names = ["GHCi"]
- , sm_phase = InitialPhase
- , sm_rules = rules_on
+ = mkSimplEnv $ SimplMode { sm_names = ["GHCi"]
+ , sm_phase = InitialPhase
+ , sm_dflags = dflags
+ , sm_rules = rules_on
, sm_inline = False
, sm_eta_expand = eta_expand_on
- , sm_case_case = True }
+ , sm_case_case = True }
where
rules_on = gopt Opt_EnableRewriteRules dflags
eta_expand_on = gopt Opt_DoLambdaEtaExpansion dflags
-- Do not do any inlining, in case we expose some unboxed
-- tuple stuff that confuses the bytecode interpreter
-updModeForStableUnfoldings :: Activation -> SimplifierMode -> SimplifierMode
+updModeForStableUnfoldings :: Activation -> SimplMode -> SimplMode
-- See Note [Simplifying inside stable unfoldings]
updModeForStableUnfoldings inline_rule_act current_mode
= current_mode { sm_phase = phaseFromActivation inline_rule_act
@@ -733,12 +808,12 @@ updModeForStableUnfoldings inline_rule_act current_mode
phaseFromActivation (ActiveAfter _ n) = Phase n
phaseFromActivation _ = InitialPhase
-updModeForRules :: SimplifierMode -> SimplifierMode
+updModeForRules :: SimplMode -> SimplMode
-- See Note [Simplifying rules]
updModeForRules current_mode
- = current_mode { sm_phase = InitialPhase
- , sm_inline = False
- , sm_rules = False
+ = current_mode { sm_phase = InitialPhase
+ , sm_inline = False -- See Note [Do not expose strictness if sm_inline=False]
+ , sm_rules = False
, sm_eta_expand = False }
{- Note [Simplifying rules]
@@ -840,7 +915,7 @@ f when it is inlined. So our conservative plan (implemented by
updModeForStableUnfoldings) is this:
-------------------------------------------------------------
- When simplifying the RHS of an stable unfolding, set the phase
+ When simplifying the RHS of a stable unfolding, set the phase
to the phase in which the stable unfolding first becomes active
-------------------------------------------------------------
@@ -890,8 +965,8 @@ mark it 'demanded', so when the RHS is simplified, it'll get an ArgOf
continuation.
-}
-activeUnfolding :: SimplEnv -> Id -> Bool
-activeUnfolding env id
+activeUnfolding :: SimplMode -> Id -> Bool
+activeUnfolding mode id
| isCompulsoryUnfolding (realIdUnfolding id)
= True -- Even sm_inline can't override compulsory unfoldings
| otherwise
@@ -902,8 +977,6 @@ activeUnfolding env id
-- (a) they are active
-- (b) sm_inline says so, except that for stable unfoldings
-- (ie pragmas) we inline anyway
- where
- mode = getMode env
getUnfoldingInRuleMatch :: SimplEnv -> InScopeEnv
-- When matching in RULE, we want to "look through" an unfolding
@@ -928,13 +1001,11 @@ getUnfoldingInRuleMatch env
| otherwise = isActive (sm_phase mode) (idInlineActivation id)
----------------------
-activeRule :: SimplEnv -> Activation -> Bool
+activeRule :: SimplMode -> Activation -> Bool
-- Nothing => No rules at all
-activeRule env
+activeRule mode
| not (sm_rules mode) = \_ -> False -- Rewriting is off
| otherwise = isActive (sm_phase mode)
- where
- mode = getMode env
{-
************************************************************************
@@ -1017,7 +1088,7 @@ 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].
+inactive in the initial stages. See Note [Gentle mode].
Note [Stable unfoldings and preInlineUnconditionally]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1041,6 +1112,11 @@ want PreInlineUnconditionally to second-guess it. A live example is
Trac #3736.
c.f. Note [Stable unfoldings and postInlineUnconditionally]
+NB: if the pragama is INLINEABLE, then we don't want to behave int
+this special way -- an INLINEABLE pragam just says to GHC "inline this
+if you like". But if there is a unique occurrence, we want to inline
+the stable unfolding, not the RHS.
+
Note [Top-level bottoming Ids]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Don't inline top-level Ids that are bottoming, even if they are used just
@@ -1054,31 +1130,45 @@ is a term (not a coercion) so we can't necessarily inline the latter in
the former.
-}
-preInlineUnconditionally :: DynFlags -> SimplEnv -> TopLevelFlag -> InId -> InExpr -> Bool
+preInlineUnconditionally
+ :: SimplEnv -> TopLevelFlag -> InId
+ -> InExpr -> StaticEnv -- These two go together
+ -> Maybe SimplEnv -- Returned env has extended substitution
-- Precondition: rhs satisfies the let/app invariant
-- See Note [CoreSyn let/app invariant] in CoreSyn
-- Reason: we don't want to inline single uses, or discard dead bindings,
-- for unlifted, side-effect-ful bindings
-preInlineUnconditionally dflags env top_lvl bndr rhs
- | not active = False
- | isStableUnfolding (idUnfolding bndr) = False -- Note [Stable unfoldings and preInlineUnconditionally]
- | isTopLevel top_lvl && isBottomingId bndr = False -- Note [Top-level bottoming Ids]
- | not (gopt Opt_SimplPreInlining dflags) = False
- | isCoVar bndr = False -- Note [Do not inline CoVars unconditionally]
- | otherwise = case idOccInfo bndr of
- IAmDead -> True -- Happens in ((\x.1) v)
- occ@OneOcc { occ_one_br = True }
- -> try_once (occ_in_lam occ)
- (occ_int_cxt occ)
- _ -> False
+preInlineUnconditionally env top_lvl bndr rhs rhs_env
+ | not pre_inline_unconditionally = Nothing
+ | not active = Nothing
+ | isTopLevel top_lvl && isBottomingId bndr = Nothing -- Note [Top-level bottoming Ids]
+ | isCoVar bndr = Nothing -- Note [Do not inline CoVars unconditionally]
+ | isExitJoinId bndr = Nothing -- Note [Do not inline exit join points]
+ -- in module Exitify
+ | not (one_occ (idOccInfo bndr)) = Nothing
+ | not (isStableUnfolding unf) = Just (extend_subst_with rhs)
+
+ -- Note [Stable unfoldings and preInlineUnconditionally]
+ | isInlinablePragma inline_prag
+ , Just inl <- maybeUnfoldingTemplate unf = Just (extend_subst_with inl)
+ | otherwise = Nothing
where
- mode = getMode env
- active = isActive (sm_phase mode) act
- -- See Note [pre/postInlineUnconditionally in gentle mode]
- act = idInlineActivation bndr
- try_once in_lam int_cxt -- There's one textual occurrence
+ unf = idUnfolding bndr
+ extend_subst_with inl_rhs = extendIdSubst env bndr (mkContEx rhs_env inl_rhs)
+
+ one_occ IAmDead = True -- Happens in ((\x.1) v)
+ one_occ (OneOcc { occ_one_br = True -- One textual occurrence
+ , occ_in_lam = in_lam
+ , occ_int_cxt = int_cxt })
| not in_lam = isNotTopLevel top_lvl || early_phase
| otherwise = int_cxt && canInlineInLam rhs
+ one_occ _ = False
+
+ pre_inline_unconditionally = gopt Opt_SimplPreInlining (seDynFlags env)
+ mode = getMode env
+ active = isActive (sm_phase mode) (inlinePragmaActivation inline_prag)
+ -- See Note [pre/postInlineUnconditionally in gentle mode]
+ inline_prag = idInlinePragma bndr
-- Be very careful before inlining inside a lambda, because (a) we must not
-- invalidate occurrence information, and (b) we want to avoid pushing a
@@ -1163,18 +1253,16 @@ story for now.
-}
postInlineUnconditionally
- :: DynFlags -> SimplEnv -> TopLevelFlag
- -> OutId -- The binder (an InId would be fine too)
- -- (*not* a CoVar)
+ :: SimplEnv -> TopLevelFlag
+ -> OutId -- The binder (*not* a CoVar), including its unfolding
-> OccInfo -- From the InId
-> OutExpr
- -> Unfolding
-> Bool
-- Precondition: rhs satisfies the let/app invariant
-- See Note [CoreSyn let/app invariant] in CoreSyn
-- Reason: we don't want to inline single uses, or discard dead bindings,
-- for unlifted, side-effect-ful bindings
-postInlineUnconditionally dflags env top_lvl bndr occ_info rhs unfolding
+postInlineUnconditionally env top_lvl bndr occ_info rhs
| not active = False
| isWeakLoopBreaker occ_info = False -- If it's a loop-breaker of any kind, don't inline
-- because it might be referred to "earlier"
@@ -1242,7 +1330,9 @@ postInlineUnconditionally dflags env top_lvl bndr occ_info rhs unfolding
-- Alas!
where
- active = isActive (sm_phase (getMode env)) (idInlineActivation bndr)
+ unfolding = idUnfolding bndr
+ dflags = seDynFlags env
+ active = isActive (sm_phase (getMode env)) (idInlineActivation bndr)
-- See Note [pre/postInlineUnconditionally in gentle mode]
{-
@@ -1278,7 +1368,7 @@ ones that are trivial):
Note [Stable unfoldings and postInlineUnconditionally]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Do not do postInlineUnconditionally if the Id has an stable unfolding,
+Do not do postInlineUnconditionally if the Id has a stable unfolding,
otherwise we lose the unfolding. Example
-- f has stable unfolding with rhs (e |> co)
@@ -1414,40 +1504,49 @@ because the latter is not well-kinded.
************************************************************************
-}
-tryEtaExpandRhs :: SimplEnv -> RecFlag -> OutId -> OutExpr
- -> SimplM (Arity, OutExpr)
+tryEtaExpandRhs :: SimplMode -> OutId -> OutExpr
+ -> SimplM (Arity, Bool, OutExpr)
-- See Note [Eta-expanding at let bindings]
-tryEtaExpandRhs env is_rec bndr rhs
- = do { dflags <- getDynFlags
- ; (new_arity, new_rhs) <- try_expand dflags
+-- If tryEtaExpandRhs rhs = (n, is_bot, rhs') then
+-- (a) rhs' has manifest arity
+-- (b) if is_bot is True then rhs' applied to n args is guaranteed bottom
+tryEtaExpandRhs mode bndr rhs
+ | Just join_arity <- isJoinId_maybe bndr
+ = do { let (join_bndrs, join_body) = collectNBinders join_arity rhs
+ ; return (count isId join_bndrs, exprIsBottom join_body, rhs) }
+ -- Note [Do not eta-expand join points]
+ -- But do return the correct arity and bottom-ness, because
+ -- these are used to set the bndr's IdInfo (Trac #15517)
+
+ | otherwise
+ = do { (new_arity, is_bot, new_rhs) <- try_expand
; WARN( new_arity < old_id_arity,
(text "Arity decrease:" <+> (ppr bndr <+> ppr old_id_arity
<+> ppr old_arity <+> ppr new_arity) $$ ppr new_rhs) )
-- Note [Arity decrease] in Simplify
- return (new_arity, new_rhs) }
+ return (new_arity, is_bot, new_rhs) }
where
- try_expand dflags
+ try_expand
| exprIsTrivial rhs
- = return (exprArity rhs, rhs)
-
- | sm_eta_expand (getMode env) -- Provided eta-expansion is on
- , let new_arity1 = findRhsArity dflags bndr rhs old_arity
- new_arity2 = idCallArity bndr
- new_arity = max new_arity1 new_arity2
- , new_arity > old_arity -- And the current manifest arity isn't enough
- = if is_rec == Recursive && isJoinId bndr
- then WARN(True, text "Can't eta-expand recursive join point:" <+>
- ppr bndr)
- return (old_arity, rhs)
- else do { tick (EtaExpansion bndr)
- ; return (new_arity, etaExpand new_arity rhs) }
+ = return (exprArity rhs, False, rhs)
+
+ | sm_eta_expand mode -- Provided eta-expansion is on
+ , new_arity > old_arity -- And the current manifest arity isn't enough
+ = do { tick (EtaExpansion bndr)
+ ; return (new_arity, is_bot, etaExpand new_arity rhs) }
+
| otherwise
- = return (old_arity, rhs)
+ = return (old_arity, is_bot && new_arity == old_arity, rhs)
+ dflags = sm_dflags mode
old_arity = exprArity rhs -- See Note [Do not expand eta-expand PAPs]
old_id_arity = idArity bndr
+ (new_arity1, is_bot) = findRhsArity dflags bndr rhs old_arity
+ new_arity2 = idCallArity bndr
+ new_arity = max new_arity1 new_arity2
+
{-
Note [Eta-expanding at let bindings]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1473,6 +1572,44 @@ because then 'genMap' will inline, and it really shouldn't: at least
as far as the programmer is concerned, it's not applied to two
arguments!
+Note [Do not eta-expand join points]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Similarly to CPR (see Note [Don't CPR join points] in WorkWrap), a join point
+stands well to gain from its outer binding's eta-expansion, and eta-expanding a
+join point is fraught with issues like how to deal with a cast:
+
+ let join $j1 :: IO ()
+ $j1 = ...
+ $j2 :: Int -> IO ()
+ $j2 n = if n > 0 then $j1
+ else ...
+
+ =>
+
+ let join $j1 :: IO ()
+ $j1 = (\eta -> ...)
+ `cast` N:IO :: State# RealWorld -> (# State# RealWorld, ())
+ ~ IO ()
+ $j2 :: Int -> IO ()
+ $j2 n = (\eta -> if n > 0 then $j1
+ else ...)
+ `cast` N:IO :: State# RealWorld -> (# State# RealWorld, ())
+ ~ IO ()
+
+The cast here can't be pushed inside the lambda (since it's not casting to a
+function type), so the lambda has to stay, but it can't because it contains a
+reference to a join point. In fact, $j2 can't be eta-expanded at all. Rather
+than try and detect this situation (and whatever other situations crop up!), we
+don't bother; again, any surrounding eta-expansion will improve these join
+points anyway, since an outer cast can *always* be pushed inside. By the time
+CorePrep comes around, the code is very likely to look more like this:
+
+ let join $j1 :: State# RealWorld -> (# State# RealWorld, ())
+ $j1 = (...) eta
+ $j2 :: Int -> State# RealWorld -> (# State# RealWorld, ())
+ $j2 = if n > 0 then $j1
+ else (...) eta
+
Note [Do not eta-expand PAPs]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We used to have old_arity = manifestArity rhs, which meant that we
@@ -1603,22 +1740,25 @@ new binding is abstracted. Note that
which is obviously bogus.
-}
-abstractFloats :: [OutTyVar] -> SimplEnv -> OutExpr -> SimplM ([OutBind], OutExpr)
-abstractFloats main_tvs body_env body
+abstractFloats :: DynFlags -> TopLevelFlag -> [OutTyVar] -> SimplFloats
+ -> OutExpr -> SimplM ([OutBind], OutExpr)
+abstractFloats dflags top_lvl main_tvs floats body
= ASSERT( notNull body_floats )
+ ASSERT( isNilOL (sfJoinFloats floats) )
do { (subst, float_binds) <- mapAccumLM abstract empty_subst body_floats
; return (float_binds, CoreSubst.substExpr (text "abstract_floats1") subst body) }
where
+ is_top_lvl = isTopLevel top_lvl
main_tv_set = mkVarSet main_tvs
- body_floats = getFloatBinds body_env
- empty_subst = CoreSubst.mkEmptySubst (seInScope body_env)
+ body_floats = letFloatBinds (sfLetFloats floats)
+ empty_subst = CoreSubst.mkEmptySubst (sfInScope floats)
abstract :: CoreSubst.Subst -> OutBind -> SimplM (CoreSubst.Subst, OutBind)
abstract subst (NonRec id rhs)
- = do { (poly_id, poly_app) <- mk_poly tvs_here id
- ; let poly_rhs = mkLams tvs_here rhs'
- subst' = CoreSubst.extendIdSubst subst id poly_app
- ; return (subst', (NonRec poly_id poly_rhs)) }
+ = do { (poly_id1, poly_app) <- mk_poly1 tvs_here id
+ ; let (poly_id2, poly_rhs) = mk_poly2 poly_id1 tvs_here rhs'
+ subst' = CoreSubst.extendIdSubst subst id poly_app
+ ; return (subst', NonRec poly_id2 poly_rhs) }
where
rhs' = CoreSubst.substExpr (text "abstract_floats2") subst rhs
@@ -1629,11 +1769,13 @@ abstractFloats main_tvs body_env body
exprSomeFreeVarsList isTyVar rhs'
abstract subst (Rec prs)
- = do { (poly_ids, poly_apps) <- mapAndUnzipM (mk_poly tvs_here) ids
+ = do { (poly_ids, poly_apps) <- mapAndUnzipM (mk_poly1 tvs_here) ids
; let subst' = CoreSubst.extendSubstList subst (ids `zip` poly_apps)
- poly_rhss = [mkLams tvs_here (CoreSubst.substExpr (text "abstract_floats3") subst' rhs)
- | rhs <- rhss]
- ; return (subst', Rec (poly_ids `zip` poly_rhss)) }
+ poly_pairs = [ mk_poly2 poly_id tvs_here rhs'
+ | (poly_id, rhs) <- poly_ids `zip` rhss
+ , let rhs' = CoreSubst.substExpr (text "abstract_floats")
+ subst' rhs ]
+ ; return (subst', Rec poly_pairs) }
where
(ids,rhss) = unzip prs
-- For a recursive group, it's a bit of a pain to work out the minimal
@@ -1651,7 +1793,8 @@ abstractFloats main_tvs body_env body
-- Here, we must abstract 'x' over 'a'.
tvs_here = toposortTyVars main_tvs
- mk_poly tvs_here var
+ mk_poly1 :: [TyVar] -> Id -> SimplM (Id, CoreExpr)
+ mk_poly1 tvs_here var
= do { uniq <- getUniqueM
; let poly_name = setNameUnique (idName var) uniq -- Keep same name
poly_ty = mkInvForAllTys tvs_here (idType var) -- But new type of course
@@ -1671,6 +1814,21 @@ abstractFloats main_tvs body_env body
-- the occurrences of x' will be just the occurrences originally
-- pinned on x.
+ mk_poly2 :: Id -> [TyVar] -> CoreExpr -> (Id, CoreExpr)
+ mk_poly2 poly_id tvs_here rhs
+ = (poly_id `setIdUnfolding` unf, poly_rhs)
+ where
+ poly_rhs = mkLams tvs_here rhs
+ unf = mkUnfolding dflags InlineRhs is_top_lvl False poly_rhs
+
+ -- We want the unfolding. Consider
+ -- let
+ -- x = /\a. let y = ... in Just y
+ -- in body
+ -- Then we float the y-binding out (via abstractFloats and addPolyBind)
+ -- but 'x' may well then be inlined in 'body' in which case we'd like the
+ -- opportunity to inline 'y' too.
+
{-
Note [Abstract over coercions]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1785,7 +1943,7 @@ prepareAlts scrut case_bndr' alts
mkCase tries these things
* Note [Nerge nested cases]
-* Note [Elimiante identity case]
+* Note [Eliminate identity case]
* Note [Scrutinee constant folding]
Note [Merge Nested Cases]
@@ -1985,13 +2143,18 @@ mkCase1 dflags scrut bndr alts_ty alts = mkCase2 dflags scrut bndr alts_ty alts
mkCase2 dflags scrut bndr alts_ty alts
| -- See Note [Scrutinee Constant Folding]
- case alts of -- Not if there is just a DEFAULT alterantive
+ case alts of -- Not if there is just a DEFAULT alternative
[(DEFAULT,_,_)] -> False
_ -> True
, gopt Opt_CaseFolding dflags
, Just (scrut', tx_con, mk_orig) <- caseRules dflags scrut
= do { bndr' <- newId (fsLit "lwild") (exprType scrut')
- ; alts' <- mapM (tx_alt tx_con mk_orig bndr') alts
+
+ ; alts' <- mapMaybeM (tx_alt tx_con mk_orig bndr') alts
+ -- mapMaybeM: discard unreachable alternatives
+ -- See Note [Unreachable caseRules alternatives]
+ -- in PrelRules
+
; mkCase3 dflags scrut' bndr' alts_ty $
add_default (re_sort alts')
}
@@ -2015,19 +2178,14 @@ mkCase2 dflags scrut bndr alts_ty alts
-- to construct an expression equivalent to the original one, for use
-- in the DEFAULT case
+ tx_alt :: (AltCon -> Maybe AltCon) -> (Id -> CoreExpr) -> Id
+ -> CoreAlt -> SimplM (Maybe CoreAlt)
tx_alt tx_con mk_orig new_bndr (con, bs, rhs)
- | DataAlt dc <- con', not (isNullaryRepDataCon dc)
- = -- For non-nullary data cons we must invent some fake binders
- -- See Note [caseRules for dataToTag] in PrelRules
- do { us <- getUniquesM
- ; let (ex_tvs, arg_ids) = dataConRepInstPat us dc
- (tyConAppArgs (idType new_bndr))
- ; return (con', ex_tvs ++ arg_ids, rhs') }
- | otherwise
- = return (con', [], rhs')
+ = case tx_con con of
+ Nothing -> return Nothing
+ Just con' -> do { bs' <- mk_new_bndrs new_bndr con'
+ ; return (Just (con', bs', rhs')) }
where
- con' = tx_con con
-
rhs' | isDeadBinder bndr = rhs
| otherwise = bindNonRec bndr orig_val rhs
@@ -2036,23 +2194,61 @@ mkCase2 dflags scrut bndr alts_ty alts
LitAlt l -> Lit l
DataAlt dc -> mkConApp2 dc (tyConAppArgs (idType bndr)) bs
+ mk_new_bndrs new_bndr (DataAlt dc)
+ | not (isNullaryRepDataCon dc)
+ = -- For non-nullary data cons we must invent some fake binders
+ -- See Note [caseRules for dataToTag] in PrelRules
+ do { us <- getUniquesM
+ ; let (ex_tvs, arg_ids) = dataConRepInstPat us dc
+ (tyConAppArgs (idType new_bndr))
+ ; return (ex_tvs ++ arg_ids) }
+ mk_new_bndrs _ _ = return []
re_sort :: [CoreAlt] -> [CoreAlt] -- Re-sort the alternatives to
re_sort alts = sortBy cmpAlt alts -- preserve the #case_invariants#
add_default :: [CoreAlt] -> [CoreAlt]
- -- TagToEnum may change a boolean True/False set of alternatives
- -- to LitAlt 0#/1# alterantives. But literal alternatives always
- -- have a DEFAULT (I think). So add it.
+ -- See Note [Literal cases]
add_default ((LitAlt {}, bs, rhs) : alts) = (DEFAULT, bs, rhs) : alts
add_default alts = alts
+{- Note [Literal cases]
+~~~~~~~~~~~~~~~~~~~~~~~
+If we have
+ case tagToEnum (a ># b) of
+ False -> e1
+ True -> e2
+
+then caseRules for TagToEnum will turn it into
+ case tagToEnum (a ># b) of
+ 0# -> e1
+ 1# -> e2
+
+Since the case is exhaustive (all cases are) we can convert it to
+ case tagToEnum (a ># b) of
+ DEFAULT -> e1
+ 1# -> e2
+
+This may generate sligthtly better code (although it should not, since
+all cases are exhaustive) and/or optimise better. I'm not certain that
+it's necessary, but currenty we do make this change. We do it here,
+NOT in the TagToEnum rules (see "Beware" in Note [caseRules for tagToEnum]
+in PrelRules)
+-}
+
--------------------------------------------------
-- Catch-all
--------------------------------------------------
mkCase3 _dflags scrut bndr alts_ty alts
= return (Case scrut bndr alts_ty alts)
+-- See Note [Exitification] and Note [Do not inline exit join points] in Exitify.hs
+-- This lives here (and not in Id) because occurrence info is only valid on
+-- InIds, so it's crucial that isExitJoinId is only called on freshly
+-- occ-analysed code. It's not a generic function you can call anywhere.
+isExitJoinId :: Var -> Bool
+isExitJoinId id = isJoinId id && isOneOcc (idOccInfo id) && occ_in_lam (idOccInfo id)
+
{-
Note [Dead binders]
~~~~~~~~~~~~~~~~~~~~