import TyCon ( isProductTyCon, isRecursiveTyCon )
import Id ( Id, idType, idInlinePragma,
isDataConId, isGlobalId, idArity,
-#ifdef DEBUG
+#ifdef OLD_STRICTNESS
idDemandInfo, idStrictness, idCprInfo,
#endif
idNewStrictness, idNewStrictness_maybe,
setIdNewStrictness, idNewDemandInfo,
+ idNewDemandInfo_maybe,
setIdNewDemandInfo, idName
)
-#ifdef DEBUG
+#ifdef OLD_STRICTNESS
import IdInfo ( newStrictnessFromOld, newDemand )
#endif
import Var ( Var )
import Type ( isUnLiftedType )
import CoreLint ( showPass, endPass )
import Util ( mapAndUnzip, mapAccumL, mapAccumR, lengthIs )
-import BasicTypes ( Arity, TopLevelFlag(..), isTopLevel, isNeverActive )
+import BasicTypes ( Arity, TopLevelFlag(..), isTopLevel, isNeverActive,
+ RecFlag(..), isRec )
import Maybes ( orElse, expectJust )
import Outputable
\end{code}
= do {
showPass dflags "Demand analysis" ;
let { binds_plus_dmds = do_prog binds } ;
+
endPass dflags "Demand analysis"
Opt_D_dump_stranal binds_plus_dmds ;
-#ifdef DEBUG
- -- Only if DEBUG is on, because only then is the old
+#ifdef OLD_STRICTNESS
+ -- Only if OLD_STRICTNESS is on, because only then is the old
-- strictness analyser run
- let dmd_changes = get_changes binds_plus_dmds ;
+ let { dmd_changes = get_changes binds_plus_dmds } ;
printDump (text "Changes in demands" $$ dmd_changes) ;
#endif
return binds_plus_dmds
-> (SigEnv, CoreBind)
dmdAnalTopBind sigs (NonRec id rhs)
= let
- ( _, _, (_, rhs1)) = dmdAnalRhs TopLevel sigs (id, rhs)
- (sigs2, _, (id2, rhs2)) = dmdAnalRhs TopLevel sigs (id, rhs1)
+ ( _, _, (_, rhs1)) = dmdAnalRhs TopLevel NonRecursive sigs (id, rhs)
+ (sigs2, _, (id2, rhs2)) = dmdAnalRhs TopLevel NonRecursive sigs (id, rhs1)
-- Do two passes to improve CPR information
- -- See the comments with mkSigTy.ignore_cpr_info below
+ -- See comments with ignore_cpr_info in mk_sig_ty
+ -- and with extendSigsWithLam
in
(sigs2, NonRec id2 rhs2)
= let
(sigs', _, pairs') = dmdFix TopLevel sigs pairs
-- We get two iterations automatically
+ -- c.f. the NonRec case above
in
(sigs', Rec pairs')
\end{code}
| Call body_dmd <- dmd -- A call demand: good!
= let
- (body_ty, body') = dmdAnal sigs body_dmd body
+ sigs' = extendSigsWithLam sigs var
+ (body_ty, body') = dmdAnal sigs' body_dmd body
(lam_ty, var') = annotateLamIdBndr body_ty var
in
(lam_ty, Lam var' body')
(alt_ty, alt') = dmdAnalAlt sigs_alt dmd alt
(alt_ty1, case_bndr') = annotateBndr alt_ty case_bndr
(_, bndrs', _) = alt'
- case_bndr_sig = StrictSig (mkDmdType emptyVarEnv [] RetCPR)
+ case_bndr_sig = cprSig
-- Inside the alternative, the case binder has the CPR property.
-- Meaning that a case on it will successfully cancel.
-- Example:
dmdAnal sigs dmd (Let (NonRec id rhs) body)
= let
- (sigs', lazy_fv, (id1, rhs')) = dmdAnalRhs NotTopLevel sigs (id, rhs)
+ (sigs', lazy_fv, (id1, rhs')) = dmdAnalRhs NotTopLevel NonRecursive sigs (id, rhs)
(body_ty, body') = dmdAnal sigs' dmd body
(body_ty1, id2) = annotateBndr body_ty id1
body_ty2 = addLazyFVs body_ty1 lazy_fv
= loop 1 initial_sigs orig_pairs
where
bndrs = map fst orig_pairs
- initial_sigs = extendSigEnvList sigs [(id, (initial_sig id, top_lvl)) | id <- bndrs]
+ initial_sigs = extendSigEnvList sigs [(id, (initialSig id, top_lvl)) | id <- bndrs]
loop :: Int
-> SigEnv -- Already contains the current sigs
-> [(Id,CoreExpr)]
-> (SigEnv, DmdEnv, [(Id,CoreExpr)])
loop n sigs pairs
- | all (same_sig sigs sigs') bndrs
+ | found_fixpoint
= (sigs', lazy_fv, pairs')
-- Note: use pairs', not pairs. pairs' is the result of
-- processing the RHSs with sigs (= sigs'), whereas pairs
-- is the result of processing the RHSs with the *previous*
-- iteration of sigs.
- | n >= 10 = pprTrace "dmdFix loop" (ppr n <+> (vcat
+
+ | n >= 10 = pprTrace "dmdFix loop" (ppr n <+> (vcat
[ text "Sigs:" <+> ppr [(id,lookup sigs id, lookup sigs' id) | (id,_) <- pairs],
text "env:" <+> ppr (ufmToList sigs),
text "binds:" <+> pprCoreBinding (Rec pairs)]))
- (emptySigEnv, emptyDmdEnv, orig_pairs) -- Safe output
+ (emptySigEnv, lazy_fv, orig_pairs) -- Safe output
+ -- The lazy_fv part is really important! orig_pairs has no strictness
+ -- info, including nothing about free vars. But if we have
+ -- letrec f = ....y..... in ...f...
+ -- where 'y' is free in f, we must record that y is mentioned,
+ -- otherwise y will get recorded as absent altogether
+
| otherwise = loop (n+1) sigs' pairs'
where
+ found_fixpoint = all (same_sig sigs sigs') bndrs
-- Use the new signature to do the next pair
-- The occurrence analyser has arranged them in a good order
-- so this can significantly reduce the number of iterations needed
((sigs', lazy_fv'), pair')
-- )
where
- (sigs', lazy_fv1, pair') = dmdAnalRhs top_lvl sigs (id,rhs)
+ (sigs', lazy_fv1, pair') = dmdAnalRhs top_lvl Recursive sigs (id,rhs)
lazy_fv' = plusUFM_C both lazy_fv lazy_fv1
-- old_sig = lookup sigs id
-- new_sig = lookup sigs' id
+ same_sig sigs sigs' var = lookup sigs var == lookup sigs' var
+ lookup sigs var = case lookupVarEnv sigs var of
+ Just (sig,_) -> sig
+
-- Get an initial strictness signature from the Id
-- itself. That way we make use of earlier iterations
-- of the fixpoint algorithm. (Cunning plan.)
-- Note that the cunning plan extends to the DmdEnv too,
-- since it is part of the strictness signature
- initial_sig id = idNewStrictness_maybe id `orElse` botSig
+initialSig id = idNewStrictness_maybe id `orElse` botSig
- same_sig sigs sigs' var = lookup sigs var == lookup sigs' var
- lookup sigs var = case lookupVarEnv sigs var of
- Just (sig,_) -> sig
-
-dmdAnalRhs :: TopLevelFlag
+dmdAnalRhs :: TopLevelFlag -> RecFlag
-> SigEnv -> (Id, CoreExpr)
-> (SigEnv, DmdEnv, (Id, CoreExpr))
-- Process the RHS of the binding, add the strictness signature
-- to the Id, and augment the environment with the signature as well.
-dmdAnalRhs top_lvl sigs (id, rhs)
+dmdAnalRhs top_lvl rec_flag sigs (id, rhs)
= (sigs', lazy_fv, (id', rhs'))
where
arity = idArity id -- The idArity should be up to date
-- The simplifier was run just beforehand
(rhs_dmd_ty, rhs') = dmdAnal sigs (vanillaCall arity) rhs
(lazy_fv, sig_ty) = WARN( arity /= dmdTypeDepth rhs_dmd_ty, ppr id )
- mkSigTy id rhs rhs_dmd_ty
+ mkSigTy top_lvl rec_flag id rhs rhs_dmd_ty
id' = id `setIdNewStrictness` sig_ty
sigs' = extendSigEnv top_lvl sigs id sig_ty
\end{code}
-- NB: not used for never-inline things; hence False
mkTopSigTy rhs dmd_ty = snd (mk_sig_ty False False rhs dmd_ty)
-mkSigTy :: Id -> CoreExpr -> DmdType -> (DmdEnv, StrictSig)
-mkSigTy id rhs dmd_ty = mk_sig_ty (isNeverActive (idInlinePragma id))
- (isStrictDmd (idNewDemandInfo id))
- rhs dmd_ty
+mkSigTy :: TopLevelFlag -> RecFlag -> Id -> CoreExpr -> DmdType -> (DmdEnv, StrictSig)
+mkSigTy top_lvl rec_flag id rhs dmd_ty
+ = mk_sig_ty never_inline thunk_cpr_ok rhs dmd_ty
+ where
+ never_inline = isNeverActive (idInlinePragma id)
+ maybe_id_dmd = idNewDemandInfo_maybe id
+ -- Is Nothing the first time round
+
+ thunk_cpr_ok
+ | isTopLevel top_lvl = False -- Top level things don't get
+ -- their demandInfo set at all
+ | isRec rec_flag = False -- Ditto recursive things
+ | Just dmd <- maybe_id_dmd = isStrictDmd dmd
+ | otherwise = True -- Optimistic, first time round
+ -- See notes below
+\end{code}
+
+The thunk_cpr_ok stuff [CPR-AND-STRICTNESS]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If the rhs is a thunk, we usually forget the CPR info, because
+it is presumably shared (else it would have been inlined, and
+so we'd lose sharing if w/w'd it into a function.
+
+However, if the strictness analyser has figured out (in a previous
+iteration) that it's strict, then we DON'T need to forget the CPR info.
+Instead we can retain the CPR info and do the thunk-splitting transform
+(see WorkWrap.splitThunk).
+
+This made a big difference to PrelBase.modInt, which had something like
+ modInt = \ x -> let r = ... -> I# v in
+ ...body strict in r...
+r's RHS isn't a value yet; but modInt returns r in various branches, so
+if r doesn't have the CPR property then neither does modInt
+Another case I found in practice (in Complex.magnitude), looks like this:
+ let k = if ... then I# a else I# b
+ in ... body strict in k ....
+(For this example, it doesn't matter whether k is returned as part of
+the overall result; but it does matter that k's RHS has the CPR property.)
+Left to itself, the simplifier will make a join point thus:
+ let $j k = ...body strict in k...
+ if ... then $j (I# a) else $j (I# b)
+With thunk-splitting, we get instead
+ let $j x = let k = I#x in ...body strict in k...
+ in if ... then $j a else $j b
+This is much better; there's a good chance the I# won't get allocated.
+
+The difficulty with this is that we need the strictness type to
+look at the body... but we now need the body to calculate the demand
+on the variable, so we can decide whether its strictness type should
+have a CPR in it or not. Simple solution:
+ a) use strictness info from the previous iteration
+ b) make sure we do at least 2 iterations, by doing a second
+ round for top-level non-recs. Top level recs will get at
+ least 2 iterations except for totally-bottom functions
+ which aren't very interesting anyway.
+
+NB: strictly_demanded is never true of a top-level Id, or of a recursive Id.
+
+The Nothing case in thunk_cpr_ok [CPR-AND-STRICTNESS]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Demand info now has a 'Nothing' state, just like strictness info.
+The analysis works from 'dangerous' towards a 'safe' state; so we
+start with botSig for 'Nothing' strictness infos, and we start with
+"yes, it's demanded" for 'Nothing' in the demand info. The
+fixpoint iteration will sort it all out.
+
+We can't start with 'not-demanded' because then consider
+ f x = let
+ t = ... I# x
+ in
+ if ... then t else I# y else f x'
+
+In the first iteration we'd have no demand info for x, so assume
+not-demanded; then we'd get TopRes for f's CPR info. Next iteration
+we'd see that t was demanded, and so give it the CPR property, but
+by now f has TopRes, so it will stay TopRes.
+ever_in
+Instead, with the Nothing setting the first time round, we say
+'yes t is demanded' the first time.
+
+However, this does mean that for non-recursive bindings we must
+iterate twice to be sure of not getting over-optimistic CPR info,
+in the case where t turns out to be not-demanded. This is handled
+by dmdAnalTopBind.
-mk_sig_ty never_inline strictly_demanded rhs (DmdType fv dmds res)
+
+\begin{code}
+mk_sig_ty never_inline thunk_cpr_ok rhs (DmdType fv dmds res)
| never_inline && not (isBotRes res)
-- HACK ALERT
-- Don't strictness-analyse NOINLINE things. Why not? Because
res' = case res of
RetCPR | ignore_cpr_info -> TopRes
other -> res
- ignore_cpr_info = is_thunk && not strictly_demanded
- is_thunk = not (exprIsValue rhs)
- -- If the rhs is a thunk, we forget the CPR info, because
- -- it is presumably shared (else it would have been inlined, and
- -- so we'd lose sharing if w/w'd it into a function.
- --
- -- Also, if the strictness analyser has figured out (in a previous iteration)
- -- that it's strict, the let-to-case transformation will happen, so again
- -- it's good.
- -- This made a big difference to PrelBase.modInt, which had something like
- -- modInt = \ x -> let r = ... -> I# v in
- -- ...body strict in r...
- -- r's RHS isn't a value yet; but modInt returns r in various branches, so
- -- if r doesn't have the CPR property then neither does modInt
- -- Another case I found in practice (in Complex.magnitude), looks like this:
- -- let k = if ... then I# a else I# b
- -- in ... body strict in k ....
- -- (For this example, it doesn't matter whether k is returned as part of
- -- the overall result.) Left to itself, the simplifier will make a join
- -- point thus:
- -- let $j k = ...body strict in k...
- -- if ... then $j (I# a) else $j (I# b)
- --
- --
- -- The difficulty with this is that we need the strictness type to
- -- look at the body... but we now need the body to calculate the demand
- -- on the variable, so we can decide whether its strictness type should
- -- have a CPR in it or not. Simple solution:
- -- a) use strictness info from the previous iteration
- -- b) make sure we do at least 2 iterations, by doing a second
- -- round for top-level non-recs. Top level recs will get at
- -- least 2 iterations except for totally-bottom functions
- -- which aren't very interesting anyway.
- --
- -- NB: strictly_demanded is never true of a top-level Id, or of a recursive Id.
+ ignore_cpr_info = not (exprIsValue rhs || thunk_cpr_ok)
\end{code}
The unpack strategy determines whether we'll *really* unpack the argument,
extendSigEnvList = extendVarEnvList
+extendSigsWithLam :: SigEnv -> Id -> SigEnv
+-- Extend the SigEnv when we meet a lambda binder
+-- If the binder is marked demanded with a product demand, then give it a CPR
+-- signature, because in the likely event that this is a lambda on a fn defn
+-- [we only use this when the lambda is being consumed with a call demand],
+-- it'll be w/w'd and so it will be CPR-ish.
+--
+-- NOTE: see notes [CPR-AND-STRICTNESS]
+--
+-- Also note that we only want to do this for something that
+-- definitely has product type, else we may get over-optimistic
+-- CPR results (e.g. from \x -> x!).
+
+extendSigsWithLam sigs id
+ = case idNewDemandInfo_maybe id of
+ Nothing -> extendVarEnv sigs id (cprSig, NotTopLevel)
+ Just (Eval (Prod ds)) -> extendVarEnv sigs id (cprSig, NotTopLevel)
+ other -> sigs
+
+
dmdTransform :: SigEnv -- The strictness environment
-> Id -- The function
-> Demand -- The demand on the function
box (Call d) = Call d -- The odd man out. Why?
box (Box d) = Box d
box (Defer _) = lazyDmd
-box Top = lazyDmd -- Box Abs and Box Top
+box Top = lazyDmd -- Box Abs and Box Top
box Abs = lazyDmd -- are the same <B,L>
box d = Box d -- Bot, Eval
\begin{code}
-#ifdef DEBUG
+#ifdef OLD_STRICTNESS
get_changes binds = vcat (map get_changes_bind binds)
get_changes_bind (Rec pairs) = vcat (map get_changes_pr pairs)