#include "HsVersions.h"
-import CmdLineOpts ( DynFlags, DynFlag(..) )
+import CmdLineOpts ( DynFlags, DynFlag(..), opt_MaxWorkerArgs )
import NewDemand -- All of it
import CoreSyn
import CoreUtils ( exprIsValue, exprArity )
import DataCon ( dataConTyCon )
import TyCon ( isProductTyCon, isRecursiveTyCon )
-import Id ( Id, idType, idInfo, idArity, idStrictness, idCprInfo, idDemandInfo,
- modifyIdInfo, isDataConId, isImplicitId, isGlobalId )
-import IdInfo ( newStrictnessInfo, setNewStrictnessInfo, mkNewStrictnessInfo,
- newDemandInfo, setNewDemandInfo, newDemand
- )
+import Id ( Id, idType, idInfo, idArity, idCprInfo, idDemandInfo,
+ modifyIdInfo, isDataConId, isImplicitId, isGlobalId,
+ idNewStrictness, idNewStrictness_maybe, getNewStrictness, setIdNewStrictness,
+ idNewDemandInfo, setIdNewDemandInfo, newStrictnessFromOld )
+import IdInfo ( newDemand )
import Var ( Var )
import VarEnv
import UniqFM ( plusUFM_C, addToUFM_Directly, lookupUFM_Directly,
- keysUFM, minusUFM, ufmToList )
+ keysUFM, minusUFM, ufmToList, filterUFM )
import Type ( isUnLiftedType )
import CoreLint ( showPass, endPass )
import ErrUtils ( dumpIfSet_dyn )
import FastTypes
\end{code}
-ToDo: set a noinline pragma on bottoming Ids
+To think about
+
+* set a noinline pragma on bottoming Ids
+
+* Consider f x = x+1 `fatbar` error (show x)
+ We'd like to unbox x, even if that means reboxing it in the error case.
+
\begin{code}
instance Outputable TopLevelFlag where
ppr flag = empty
\begin{code}
dmdAnalPgm :: DynFlags -> [CoreBind] -> IO [CoreBind]
-#ifndef DEBUG
-
-dmdAnalPgm dflags binds = return binds
-
-#else
-
+dmdAnalPgm dflags binds
+ = panic "dmdAnalPgm called"
dmdAnalPgm dflags binds
= do {
showPass dflags "Demand analysis" ;
dmd_changes = get_changes binds_plus_dmds } ;
endPass dflags "Demand analysis"
Opt_D_dump_stranal binds_plus_dmds ;
+#ifdef DEBUG
+ -- Only if DEBUG is on, because only then is the old strictness analyser run
printDump (text "Changes in demands" $$ dmd_changes) ;
+#endif
return binds_plus_dmds
}
where
= (sigs, NonRec id rhs) -- It's pre-computed in MkId.lhs
| otherwise
= let
- (sigs', (id', rhs')) = downRhs TopLevel sigs (id, rhs)
+ (sigs', _, (id', rhs')) = downRhs TopLevel sigs (id, rhs)
in
(sigs', NonRec id' rhs')
dmdAnalTopBind sigs (Rec pairs)
= let
- (sigs', pairs') = dmdFix TopLevel sigs pairs
+ (sigs', _, pairs') = dmdFix TopLevel sigs pairs
in
(sigs', Rec pairs')
\end{code}
dmdAnal sigs dmd (Note n e)
= (dmd_ty, Note n e')
where
- (dmd_ty, e') = dmdAnal sigs dmd e
+ (dmd_ty, e') = dmdAnal sigs dmd' e
+ dmd' = case n of
+ Coerce _ _ -> Eval -- This coerce usually arises from a recursive
+ other -> dmd -- newtype, and we don't want to look inside them
+ -- for exactly the same reason that we don't look
+ -- inside recursive products -- we might not reach
+ -- a fixpoint. So revert to a vanilla Eval demand
dmdAnal sigs dmd (App fun (Type ty))
= (fun_ty, App fun' (Type ty))
in
(body_ty, Lam var body')
- | otherwise
- = let
- body_dmd = case dmd of
- Call dmd -> dmd
- other -> Lazy -- Conservative
-
+ | Call body_dmd <- dmd -- A call demand: good!
+ = let
(body_ty, body') = dmdAnal sigs body_dmd body
- (lam_ty, var') = annotateLamIdBndr body_ty var
+ (lam_ty, var') = annotateLamIdBndr body_ty var
in
(lam_ty, Lam var' body')
+ | otherwise -- Not enough demand on the lambda; but do the body
+ = let -- anyway to annotate it and gather free var info
+ (body_ty, body') = dmdAnal sigs Eval body
+ (lam_ty, var') = annotateLamIdBndr body_ty var
+ in
+ (deferType lam_ty, Lam var' body')
+
dmdAnal sigs dmd (Case scrut case_bndr [alt@(DataAlt dc,bndrs,rhs)])
| let tycon = dataConTyCon dc,
isProductTyCon tycon,
not (isRecursiveTyCon tycon)
= let
- bndr_ids = filter isId bndrs
- (alt_ty, alt') = dmdAnalAlt sigs dmd alt
- (alt_ty1, case_bndr') = annotateBndr alt_ty case_bndr
- (_, bndrs', _) = alt'
- scrut_dmd = Seq Drop Now [idNewDemandInfo b | b <- bndrs', isId b]
- (scrut_ty, scrut') = dmdAnal sigs scrut_dmd scrut
+ bndr_ids = filter isId bndrs
+ (alt_ty, alt') = dmdAnalAlt sigs dmd alt
+ (alt_ty1, case_bndr') = annotateBndr alt_ty case_bndr
+ (_, bndrs', _) = alt'
+
+ -- Figure out whether the case binder is used, and use
+ -- that to set the keepity of the demand. This is utterly essential.
+ -- Consider f x = case x of y { (a,b) -> k y a }
+ -- If we just take scrut_demand = U(L,A), then we won't pass x to the
+ -- worker, so the worker will rebuild
+ -- x = (a, absent-error)
+ -- and that'll crash.
+ dead_case_bndr = isAbsentDmd (idNewDemandInfo case_bndr')
+ keepity | dead_case_bndr = Drop
+ | otherwise = Keep
+
+ scrut_dmd = Seq keepity Now [idNewDemandInfo b | b <- bndrs', isId b]
+ (scrut_ty, scrut') = dmdAnal sigs scrut_dmd scrut
in
(alt_ty1 `bothType` scrut_ty, Case scrut' case_bndr' [alt'])
dmdAnal sigs dmd (Let (NonRec id rhs) body)
= let
- (sigs', (id1, rhs')) = downRhs NotTopLevel sigs (id, rhs)
- (body_ty, body') = dmdAnal sigs' dmd body
- (body_ty1, id2) = annotateBndr body_ty id1
+ (sigs', lazy_fv, (id1, rhs')) = downRhs NotTopLevel sigs (id, rhs)
+ (body_ty, body') = dmdAnal sigs' dmd body
+ (body_ty1, id2) = annotateBndr body_ty id1
+ body_ty2 = addLazyFVs body_ty1 lazy_fv
in
-- pprTrace "dmdLet" (ppr id <+> ppr (sig,rhs_env))
- (body_ty1, Let (NonRec id2 rhs') body')
+ (body_ty2, Let (NonRec id2 rhs') body')
dmdAnal sigs dmd (Let (Rec pairs) body)
= let
- bndrs = map fst pairs
- (sigs', pairs') = dmdFix NotTopLevel sigs pairs
- (body_ty, body') = dmdAnal sigs' dmd body
-
- -- I saw occasions where it was really worth using the
- -- call demands on the Ids to propagate demand info
- -- on the free variables. An example is 'roll' in imaginary/wheel-sieve2
- -- Something like this:
- -- roll x = letrec go y = if ... then roll (x-1) else x+1
- -- in go ms
- -- We want to see that this is strict in x.
- --
- -- This will happen because sigs' has a binding for 'go' that
- -- has a demand on x.
-
- (result_ty, _) = annotateBndrs body_ty bndrs
+ bndrs = map fst pairs
+ (sigs', lazy_fv, pairs') = dmdFix NotTopLevel sigs pairs
+ (body_ty, body') = dmdAnal sigs' dmd body
+ body_ty1 = addLazyFVs body_ty lazy_fv
+ in
+ sigs' `seq` body_ty `seq`
+ let
+ (body_ty2, _) = annotateBndrs body_ty1 bndrs
-- Don't bother to add demand info to recursive
-- binders as annotateBndr does;
-- being recursive, we can't treat them strictly.
-- But we do need to remove the binders from the result demand env
in
- (result_ty, Let (Rec pairs') body')
+ (body_ty2, Let (Rec pairs') body')
dmdAnalAlt sigs dmd (con,bndrs,rhs)
dmdFix :: TopLevelFlag
-> SigEnv -- Does not include bindings for this binding
-> [(Id,CoreExpr)]
- -> (SigEnv,
+ -> (SigEnv, DmdEnv,
[(Id,CoreExpr)]) -- Binders annotated with stricness info
dmdFix top_lvl sigs pairs
loop :: Int
-> SigEnv -- Already contains the current sigs
-> [(Id,CoreExpr)]
- -> (SigEnv, [(Id,CoreExpr)])
+ -> (SigEnv, DmdEnv, [(Id,CoreExpr)])
loop n sigs pairs
- | all (same_sig sigs sigs') bndrs = (sigs, pairs)
- -- Note: use pairs, not pairs'. Since the sigs are the same
- -- there'll be no change, unless this is the very first visit,
- -- and the first iteraion of that visit. But in that case, the
- -- function is bottom anyway, there's no point in looking.
- | n >= 5 = pprTrace "dmdFix" (ppr n <+> ppr pairs) (loop (n+1) sigs' pairs')
+ | all (same_sig sigs sigs') bndrs = (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 >= 5 = pprTrace "dmdFix" (ppr n <+> (vcat
+ [ text "Sigs:" <+> ppr [(id,lookup sigs id, lookup sigs' id) | (id,_) <- pairs],
+ text "env:" <+> ppr (ufmToList sigs),
+ text "binds:" <+> ppr pairs]))
+ (loop (n+1) sigs' pairs')
| otherwise = {- pprTrace "dmdFixLoop" (ppr id_sigs) -} (loop (n+1) sigs' pairs')
where
-- 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', pairs') = mapAccumL (downRhs top_lvl) sigs pairs
-
+ ((sigs',lazy_fv), pairs') = mapAccumL (my_downRhs top_lvl) (sigs, emptyDmdEnv) pairs
+
+ my_downRhs top_lvl (sigs,lazy_fv) (id,rhs)
+ = -- pprTrace "downRhs {" (ppr id <+> (ppr old_sig))
+ -- (new_sig `seq`
+ -- pprTrace "downRhsEnd" (ppr id <+> ppr new_sig <+> char '}' )
+ ((sigs', lazy_fv'), pair')
+ -- )
+ where
+ (sigs', lazy_fv1, pair') = downRhs top_lvl sigs (id,rhs)
+ lazy_fv' = plusUFM_C both lazy_fv lazy_fv1
+ old_sig = lookup sigs id
+ new_sig = lookup sigs' id
-- Get an initial strictness signature from the Id
-- itself. That way we make use of earlier iterations
downRhs :: TopLevelFlag
-> SigEnv -> (Id, CoreExpr)
- -> (SigEnv, (Id, CoreExpr))
--- On the way down, compute a strictness signature
--- for the function. Keep its annotated RHS and dmd env
--- for use on the way up
--- The demand-env is that computed for a vanilla call.
+ -> (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.
downRhs top_lvl sigs (id, rhs)
- = (sigs', (id', rhs'))
+ = (sigs', lazy_fv, (id', rhs'))
where
- arity = exprArity rhs -- The idArity may not be up to date
- (rhs_ty, rhs') = dmdAnal sigs (vanillaCall arity) rhs
- sig = mkStrictSig id arity (mkSigTy rhs rhs_ty)
- id' = id `setIdNewStrictness` sig
- sigs' = extendSigEnv top_lvl sigs id sig
-
-mkSigTy rhs (DmdType fv [] RetCPR)
- | not (exprIsValue rhs) = DmdType fv [] TopRes
+ arity = exprArity rhs -- The idArity may not be up to date
+ (rhs_ty, rhs') = dmdAnal sigs (vanillaCall arity) rhs
+ (lazy_fv, sig_ty) = mkSigTy id arity rhs rhs_ty
+ id' = id `setIdNewStrictness` sig_ty
+ sigs' = extendSigEnv top_lvl sigs id sig_ty
+\end{code}
+
+%************************************************************************
+%* *
+\subsection{Strictness signatures and types}
+%* *
+%************************************************************************
+
+\begin{code}
+mkSigTy :: Id -> Arity -> CoreExpr -> DmdType -> (DmdEnv, StrictSig)
+-- Take a DmdType and turn it into a StrictSig
+mkSigTy id arity rhs (DmdType fv dmds res)
+ = (lazy_fv, mkStrictSig id arity dmd_ty)
+ where
+ dmd_ty = DmdType strict_fv lazified_dmds res'
+
+ lazy_fv = filterUFM (not . isStrictDmd) fv
+ strict_fv = filterUFM isStrictDmd fv
+ -- We put the strict FVs in the DmdType of the Id, so
+ -- that at its call sites we unleash demands on its strict fvs.
+ -- An example is 'roll' in imaginary/wheel-sieve2
+ -- Something like this:
+ -- roll x = letrec
+ -- go y = if ... then roll (x-1) else x+1
+ -- in
+ -- go ms
+ -- We want to see that roll is strict in x, which is because
+ -- go is called. So we put the DmdEnv for x in go's DmdType.
+ --
+ -- Another example:
+ -- f :: Int -> Int -> Int
+ -- f x y = let t = x+1
+ -- h z = if z==0 then t else
+ -- if z==1 then x+1 else
+ -- x + h (z-1)
+ -- in
+ -- h y
+ -- Calling h does indeed evaluate x, but we can only see
+ -- that if we unleash a demand on x at the call site for t.
+ --
+ -- Incidentally, here's a place where lambda-lifting h would
+ -- lose the cigar --- we couldn't see the joint strictness in t/x
+ --
+ -- ON THE OTHER HAND
+ -- We don't want to put *all* the fv's from the RHS into the
+ -- DmdType, because that makes fixpointing very slow --- the
+ -- DmdType gets full of lazy demands that are slow to converge.
+
+ lazified_dmds = map lazify dmds
+ -- Get rid of defers in the arguments
+ final_dmds = setUnpackStrategy lazified_dmds
+ -- Set the unpacking strategy
+
+ res' = case (dmds, res) of
+ ([], RetCPR) | not (exprIsValue rhs) -> TopRes
+ other -> res
-- 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.
--
- -- ** But keep the demand unleashed on the free
- -- vars when the thing is evaluated! **
- --
-- DONE IN OLD CPR ANALYSER, BUT NOT YET HERE
-- Also, if the strictness analyser has figured out that it's strict,
-- the let-to-case transformation will happen, so again it's good.
-- ...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
+\end{code}
+
+The unpack strategy determines whether we'll *really* unpack the argument,
+or whether we'll just remember its strictness. If unpacking would give
+rise to a *lot* of worker args, we may decide not to unpack after all.
-mkSigTy rhs (DmdType fv dmds res) = DmdType fv (map lazify dmds) res
--- Get rid of defers
+\begin{code}
+setUnpackStrategy :: [Demand] -> [Demand]
+setUnpackStrategy ds
+ = snd (go (opt_MaxWorkerArgs - nonAbsentArgs ds) ds)
+ where
+ go :: Int -- Max number of args available for sub-components of [Demand]
+ -> [Demand]
+ -> (Int, [Demand]) -- Args remaining after subcomponents of [Demand] are unpacked
+
+ go n (Seq keep _ cs : ds)
+ | n' >= 0 = Seq keep Now cs' `cons` go n'' ds
+ | otherwise = Eval `cons` go n ds
+ where
+ (n'',cs') = go n' cs
+ n' = n + box - non_abs_args
+ box = case keep of
+ Keep -> 0
+ Drop -> 1 -- Add one to the budget if we drop the top-level arg
+ non_abs_args = nonAbsentArgs cs
+ -- Delete # of non-absent args to which we'll now be committed
+
+ go n (d:ds) = d `cons` go n ds
+ go n [] = (n,[])
+
+ cons d (n,ds) = (n, d:ds)
+
+nonAbsentArgs :: [Demand] -> Int
+nonAbsentArgs [] = 0
+nonAbsentArgs (Abs : ds) = nonAbsentArgs ds
+nonAbsentArgs (d : ds) = 1 + nonAbsentArgs ds
\end{code}
%************************************************************************
\begin{code}
+splitDmdTy :: DmdType -> (Demand, DmdType)
+-- Split off one function argument
+splitDmdTy (DmdType fv (dmd:dmds) res_ty) = (dmd, DmdType fv dmds res_ty)
+splitDmdTy ty@(DmdType fv [] TopRes) = (topDmd, ty)
+splitDmdTy ty@(DmdType fv [] BotRes) = (Abs, ty)
+ -- We already have a suitable demand on all
+ -- free vars, so no need to add more!
+splitDmdTy (DmdType fv [] RetCPR) = panic "splitDmdTy"
+\end{code}
+
+\begin{code}
unitVarDmd var dmd = DmdType (unitVarEnv var dmd) [] TopRes
addVarDmd top_lvl dmd_ty@(DmdType fv ds res) var dmd
| isTopLevel top_lvl = dmd_ty -- Don't record top level things
| otherwise = DmdType (extendVarEnv fv var dmd) ds res
+addLazyFVs (DmdType fv ds res) lazy_fvs
+ = DmdType (plusUFM_C both fv lazy_fvs) ds res
+
annotateBndr :: DmdType -> Var -> (DmdType, Var)
-- The returned env has the var deleted
-- The returned var is annotated with demand info
%************************************************************************
%* *
-\subsection{Demand types}
-%* *
-%************************************************************************
-
-\begin{code}
-splitDmdTy :: DmdType -> (Demand, DmdType)
--- Split off one function argument
-splitDmdTy (DmdType fv (dmd:dmds) res_ty) = (dmd, DmdType fv dmds res_ty)
-splitDmdTy ty@(DmdType fv [] TopRes) = (topDmd, ty)
-splitDmdTy ty@(DmdType fv [] BotRes) = (Abs, ty)
- -- We already have a suitable demand on all
- -- free vars, so no need to add more!
-splitDmdTy (DmdType fv [] RetCPR) = panic "splitDmdTy"
-
--------------------------
-dmdTypeRes :: DmdType -> DmdResult
-dmdTypeRes (DmdType _ _ res_ty) = res_ty
-\end{code}
-
-
-%************************************************************************
-%* *
\subsection{Strictness signatures}
%* *
%************************************************************************
------ DATA CONSTRUCTOR
| isDataConId var, -- Data constructor
Seq k Now ds <- res_dmd, -- and the demand looks inside its fields
- let StrictSig arity dmd_ty = idNewStrictness var -- It must have a strictness sig
- = if arity == length ds then -- Saturated, so unleash the demand
+ let StrictSig dmd_ty = idNewStrictness var -- It must have a strictness sig
+ = if dmdTypeDepth dmd_ty == length ds then -- Saturated, so unleash the demand
-- ds can be empty, when we are just seq'ing the thing
mkDmdType emptyDmdEnv ds (dmdTypeRes dmd_ty)
- -- Need to extract whether it's a product
+ -- Need to extract whether it's a product, hence dmdTypeRes
else
topDmdType
------ IMPORTED FUNCTION
| isGlobalId var, -- Imported function
- let StrictSig arity dmd_ty = getNewStrictness var
- = if arity <= depth then -- Saturated, so unleash the demand
+ let StrictSig dmd_ty = getNewStrictness var
+ = if dmdTypeDepth dmd_ty <= call_depth then -- Saturated, so unleash the demand
dmd_ty
else
topDmdType
------ LOCAL LET/REC BOUND THING
- | Just (StrictSig arity dmd_ty, top_lvl) <- lookupVarEnv sigs var
+ | Just (StrictSig dmd_ty, top_lvl) <- lookupVarEnv sigs var
= let
- fn_ty = if arity <= depth then dmd_ty else topDmdType
+ fn_ty | dmdTypeDepth dmd_ty <= call_depth = dmd_ty
+ | otherwise = deferType dmd_ty
+ -- NB: it's important to use deferType, and not just return topDmdType
+ -- Consider let { f x y = p + x } in f 1
+ -- The application isn't saturated, but we must nevertheless propagate
+ -- a lazy demand for p!
in
addVarDmd top_lvl fn_ty var dmd
= unitVarDmd var dmd
where
- (depth, res_dmd) = splitCallDmd dmd
-\end{code}
-
-\begin{code}
-squashDmdEnv (StrictSig a (DmdType fv ds res)) = StrictSig a (DmdType emptyDmdEnv ds res)
-
-betterStrict :: StrictSig -> StrictSig -> Bool
-betterStrict (StrictSig ar1 t1) (StrictSig ar2 t2)
- = (ar1 >= ar2) && (t1 `betterDmdType` t2)
-
-betterDmdType t1 t2 = (t1 `lubType` t2) == t2
+ (call_depth, res_dmd) = splitCallDmd dmd
\end{code}
vanillaCall n = Call (vanillaCall (n-1))
deferType :: DmdType -> DmdType
-deferType (DmdType fv ds _) = DmdType (mapVarEnv defer fv) ds TopRes
- -- Check this
+deferType (DmdType fv _ _) = DmdType (mapVarEnv defer fv) [] TopRes
+ -- Notice that we throw away info about both arguments and results
+ -- For example, f = let ... in \x -> x
+ -- We don't want to get a stricness type V->T for f.
defer :: Demand -> Demand
-- c.f. `lub` Abs
-- The 'Defer' demands are just Lazy at function boundaries
lazify (Seq k Defer ds) = Lazy
lazify (Seq k Now ds) = Seq k Now (map lazify ds)
+lazify Bot = Abs -- Don't pass args that are consumed by bottom
lazify d = d
+\end{code}
+
+\begin{code}
+betterStrictness :: StrictSig -> StrictSig -> Bool
+betterStrictness (StrictSig t1) (StrictSig t2) = betterDmdType t1 t2
+
+betterDmdType t1 t2 = (t1 `lubType` t2) == t2
betterDemand :: Demand -> Demand -> Bool
-- If d1 `better` d2, and d2 `better` d2, then d1==d2
betterDemand d1 d2 = (d1 `lub` d2) == d2
+
+squashDmdEnv (StrictSig (DmdType fv ds res)) = StrictSig (DmdType emptyDmdEnv ds res)
\end{code}
lub Abs (Seq k _ ds) = Seq k Defer ds -- Very important ('radicals' example)
lub Abs d = Lazy
-lub Eval Abs = Lazy
-lub Eval Lazy = Lazy
-lub Eval (Seq k Now ds) = Seq Keep Now ds
-lub Eval d = Eval
+lub Eval Abs = Lazy
+lub Eval Lazy = Lazy
+lub Eval (Seq k Now ds) = Seq Keep Now ds
+lub Eval (Seq k Defer ds) = Lazy
+lub Eval d = Eval
lub (Call d1) (Call d2) = Call (lub d1 d2)
-lub (Seq k1 l1 ds1) (Seq k2 l2 ds2) = Seq (k1 `vee` k2) (l1 `or_defer` l2)
- (zipWithEqual "lub" lub ds1 ds2)
+lub (Seq k1 l1 ds1) (Seq k2 l2 ds2) = Seq (k1 `vee` k2) (l1 `or_defer` l2) (lubs ds1 ds2)
-- The last clauses deal with the remaining cases for Call and Seq
lub d1@(Call _) d2@(Seq _ _ _) = pprPanic "lub" (ppr d1 $$ ppr d2)
lub d1 d2 = lub d2 d1
+-- A Seq can have an empty list of demands, in the polymorphic case.
+lubs [] ds2 = ds2
+lubs ds1 [] = ds1
+lubs ds1 ds2 = ASSERT( length ds1 == length ds2 ) zipWith lub ds1 ds2
+
or_defer Now Now = Now
or_defer _ _ = Defer
-----------------------------------
both :: Demand -> Demand -> Demand
-both Bot d = Bot
+-- The normal one
+-- both Bot d = Bot
+
+-- The experimental one
+-- The idea is that (error x) places on x
+-- both demand Bot (like on all free vars)
+-- and demand Eval (for the arg to error)
+-- and we want the result to be Eval.
+both Bot Bot = Bot
+both Bot Abs = Bot
+both Bot d = d
-both Abs Bot = Bot
both Abs d = d
-both Err Bot = Bot
+both Err Bot = Err
both Err Abs = Err
both Err d = d
-both Lazy Bot = Bot
-both Lazy Abs = Lazy
-both Lazy Err = Lazy
-both Lazy (Seq k Now ds) = Seq Keep Now ds
-both Lazy d = d
+both Lazy Bot = Lazy
+both Lazy Abs = Lazy
+both Lazy Err = Lazy
+both Lazy (Seq k l ds) = Seq Keep l ds
+both Lazy d = d
+ -- Notice that the Seq case ensures that we have the
+ -- boxed value. The equation originally said
+ -- both (Seq k Now ds) = Seq Keep Now ds
+ -- but it's important that the Keep is switched on even
+ -- for a deferred demand. Otherwise a (Seq Drop Now [])
+ -- might both'd with the result, and then we won't pass
+ -- the boxed value. Here's an example:
+ -- (x-1) `seq` (x+1, x)
+ -- From the (x+1, x) we get (U*(V) `both` L), which must give S*(V)
+ -- From (x-1) we get U(V). Combining, we must get S(V).
+ -- If we got U*(V) from the pair, we'd end up with U(V), and that
+ -- can be a disaster if a component of the data structure is absent.
+ -- [Disaster = enter an absent argument.]
-both Eval Bot = Bot
both Eval (Seq k l ds) = Seq Keep Now ds
both Eval (Call d) = Call d
both Eval d = Eval
-both (Seq k1 Defer ds1) (Seq k2 Defer ds2) = Seq (k1 `vee` k2) Defer
- (zipWithEqual "both" both ds1 ds2)
-both (Seq k1 l1 ds1) (Seq k2 l2 ds2) = Seq (k1 `vee` k2) Now
- (zipWithEqual "both" both ds1' ds2')
- where
- ds1' = case l1 of { Now -> ds1; Defer -> map defer ds1 }
- ds2' = case l2 of { Now -> ds2; Defer -> map defer ds2 }
+both (Seq k1 Defer ds1) (Seq k2 Defer ds2) = Seq (k1 `vee` k2) Defer (boths ds1 ds2)
+both (Seq k1 l1 ds1) (Seq k2 l2 ds2) = Seq (k1 `vee` k2) Now (boths ds1' ds2')
+ where
+ ds1' = case l1 of { Now -> ds1; Defer -> map defer ds1 }
+ ds2' = case l2 of { Now -> ds2; Defer -> map defer ds2 }
both (Call d1) (Call d2) = Call (d1 `both` d2)
both d1 d2 = both d2 d1
-----------------------------------
+-- A Seq can have an empty list of demands, in the polymorphic case.
+boths [] ds2 = ds2
+boths ds1 [] = ds1
+boths ds1 ds2 = ASSERT( length ds1 == length ds2 ) zipWith both ds1 ds2
+
+-----------------------------------
bothRes :: DmdResult -> DmdResult -> DmdResult
-- Left-biased for CPR info
bothRes BotRes _ = BotRes
\begin{code}
--- Move these to Id.lhs
-idNewStrictness_maybe :: Id -> Maybe StrictSig
-idNewStrictness :: Id -> StrictSig
-
-idNewStrictness_maybe id = newStrictnessInfo (idInfo id)
-idNewStrictness id = idNewStrictness_maybe id `orElse` topSig
-
-getNewStrictness :: Id -> StrictSig
--- First tries the "new-strictness" field, and then
--- reverts to the old one. This is just until we have
--- cross-module info for new strictness
-getNewStrictness id = idNewStrictness_maybe id `orElse` newStrictnessFromOld id
-
-newStrictnessFromOld :: Id -> StrictSig
-newStrictnessFromOld id = mkNewStrictnessInfo id (idArity id) (idStrictness id) (idCprInfo id)
-
-setIdNewStrictness :: Id -> StrictSig -> Id
-setIdNewStrictness id sig = modifyIdInfo (`setNewStrictnessInfo` sig) id
-
-idNewDemandInfo :: Id -> Demand
-idNewDemandInfo id = newDemandInfo (idInfo id)
-
-setIdNewDemandInfo :: Id -> Demand -> Id
-setIdNewDemandInfo id dmd = modifyIdInfo (`setNewDemandInfo` dmd) id
-\end{code}
-
-\begin{code}
get_changes binds = vcat (map get_changes_bind binds)
get_changes_bind (Rec pairs) = vcat (map get_changes_pr pairs)
get_changes_bind (NonRec id rhs) = get_changes_pr (id,rhs)
-get_changes_pr (id,rhs) = get_changes_var id $$ get_changes_expr rhs
+get_changes_pr (id,rhs)
+ | isImplicitId id = empty -- We don't look inside these
+ | otherwise = get_changes_var id $$ get_changes_expr rhs
get_changes_var var
| isId var = get_changes_str var $$ get_changes_dmd var
get_changes_expr (App e1 e2) = get_changes_expr e1 $$ get_changes_expr e2
get_changes_expr (Lam b e) = {- get_changes_var b $$ -} get_changes_expr e
get_changes_expr (Let b e) = get_changes_bind b $$ get_changes_expr e
-get_changes_expr (Case e b a) = get_changes_expr e $$ get_changes_var b $$ vcat (map get_changes_alt a)
+get_changes_expr (Case e b a) = get_changes_expr e $$ {- get_changes_var b $$ -} vcat (map get_changes_alt a)
get_changes_alt (con,bs,rhs) = {- vcat (map get_changes_var bs) $$ -} get_changes_expr rhs
info = (text "Old" <+> ppr old) $$ (text "New" <+> ppr new)
new = squashDmdEnv (idNewStrictness id) -- Don't report diffs in the env
old = newStrictnessFromOld id
- old_better = old `betterStrict` new
- new_better = new `betterStrict` old
+ old_better = old `betterStrictness` new
+ new_better = new `betterStrictness` old
get_changes_dmd id
| isUnLiftedType (idType id) = empty -- Not useful
old = newDemand (idDemandInfo id)
new_better = new `betterDemand` old
old_better = old `betterDemand` new
-#endif /* DEBUG */
\end{code}