-----------------
\begin{code}
-module DmdAnal ( dmdAnalPgm ) where
+module DmdAnal ( dmdAnalPgm, dmdAnalTopRhs,
+ both {- needed by WwLib -}
+ ) where
#include "HsVersions.h"
-import CmdLineOpts ( DynFlags, DynFlag(..) )
+import CmdLineOpts ( DynFlags, DynFlag(..), opt_MaxWorkerArgs )
import NewDemand -- All of it
import CoreSyn
+import PprCore
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, idDemandInfo,
+ isDataConId, isGlobalId, idArity,
+ 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, filterUFM )
import Type ( isUnLiftedType )
import CoreLint ( showPass, endPass )
-import ErrUtils ( dumpIfSet_dyn )
-import Util ( mapAndUnzip, mapAccumL, mapAccumR, zipWithEqual )
+import Util ( mapAndUnzip, mapAccumL, mapAccumR )
import BasicTypes ( Arity, TopLevelFlag(..), isTopLevel )
import Maybes ( orElse, expectJust )
import Outputable
-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
= 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
-> CoreBind
-> (SigEnv, CoreBind)
dmdAnalTopBind sigs (NonRec id rhs)
- | isImplicitId id -- Don't touch the info on constructors, selectors etc
- = (sigs, NonRec id rhs) -- It's pre-computed in MkId.lhs
- | otherwise
= let
- (sigs', _, (id', rhs')) = downRhs TopLevel sigs (id, rhs)
+ (sigs', _, (id', rhs')) = dmdAnalRhs TopLevel sigs (id, rhs)
in
(sigs', NonRec id' rhs')
(sigs', Rec pairs')
\end{code}
+\begin{code}
+dmdAnalTopRhs :: CoreExpr -> (StrictSig, CoreExpr)
+-- Analyse the RHS and return
+-- a) appropriate strictness info
+-- b) the unfolding (decorated with stricntess info)
+dmdAnalTopRhs rhs
+ = (sig, rhs')
+ where
+ arity = exprArity rhs
+ (rhs_ty, rhs') = dmdAnal emptySigEnv (vanillaCall arity) rhs
+ (_, sig) = mkSigTy rhs rhs_ty
+\end{code}
%************************************************************************
%* *
dmdAnal :: SigEnv -> Demand -> CoreExpr -> (DmdType, CoreExpr)
dmdAnal sigs Abs e = (topDmdType, e)
+dmdAnal sigs Bot e = (botDmdType, e)
dmdAnal sigs Lazy e = let
(res_ty, e') = dmdAnal sigs Eval e
-- We still want to mark x as demanded, because it will be when we
-- enter the let. If we analyse f's arg with a Lazy demand, we'll
-- just mark x as Lazy
+ -- c) The application rule wouldn't be right either
+ -- Evaluating (f x) in a L demand does *not* cause
+ -- evaluation of f in a C(L) demand!
dmdAnal sigs dmd (Lit lit)
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))
where
(fun_ty, fun') = dmdAnal sigs dmd fun
-dmdAnal sigs dmd (App fun arg) -- Non-type arguments
+-- Lots of the other code is there to make this
+-- beautiful, compositional, application rule :-)
+dmdAnal sigs dmd e@(App fun arg) -- Non-type arguments
= let -- [Type arg handled above]
(fun_ty, fun') = dmdAnal sigs (Call dmd) fun
(arg_ty, arg') = dmdAnal sigs arg_dmd arg
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
+ (alt_ty, alt') = dmdAnalAlt sigs dmd alt
+ (alt_ty1, case_bndr') = annotateBndr alt_ty case_bndr
+ (_, bndrs', _) = alt'
+
+ -- Figure out whether the demand on the case binder is used, and use
+ -- that to set the scrut_dmd. 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.
+ -- So at one stage I had:
+ -- dead_case_bndr = isAbsentDmd (idNewDemandInfo case_bndr')
+ -- keepity | dead_case_bndr = Drop
+ -- | otherwise = Keep
+ --
+ -- But then consider
+ -- case x of y { (a,b) -> h y + a }
+ -- where h : U(LL) -> T
+ -- The above code would compute a Keep for x, since y is not Abs, which is silly
+ -- The insight is, of course, that a demand on y is a demand on the
+ -- scrutinee, so we need to `both` it with the scrut demand
+
+ scrut_dmd = mkSeq Drop [idNewDemandInfo b | b <- bndrs', isId b]
+ `both`
+ idNewDemandInfo case_bndr'
+
+ (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', lazy_fv, (id1, rhs')) = downRhs NotTopLevel sigs (id, rhs)
+ (sigs', lazy_fv, (id1, rhs')) = dmdAnalRhs 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
-> (SigEnv, DmdEnv,
[(Id,CoreExpr)]) -- Binders annotated with stricness info
-dmdFix top_lvl sigs pairs
- = loop 1 initial_sigs pairs
+dmdFix top_lvl sigs orig_pairs
+ = loop 1 initial_sigs orig_pairs
where
- bndrs = map fst pairs
+ bndrs = map fst orig_pairs
initial_sigs = extendSigEnvList sigs [(id, (initial_sig id, top_lvl)) | id <- bndrs]
loop :: Int
-> [(Id,CoreExpr)]
-> (SigEnv, DmdEnv, [(Id,CoreExpr)])
loop n sigs pairs
- | all (same_sig sigs sigs') bndrs = (sigs', lazy_fv, 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 <+> ppr pairs) (loop (n+1) sigs' pairs')
- | otherwise = {- pprTrace "dmdFixLoop" (ppr id_sigs) -} (loop (n+1) sigs' pairs')
+ | 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
+ | otherwise = 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
((sigs', lazy_fv'), pair')
-- )
where
- (sigs', lazy_fv1, pair') = downRhs top_lvl sigs (id,rhs)
+ (sigs', lazy_fv1, pair') = dmdAnalRhs top_lvl sigs (id,rhs)
lazy_fv' = plusUFM_C both lazy_fv lazy_fv1
- old_sig = lookup sigs id
- new_sig = lookup sigs' id
+ -- 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
lookup sigs var = case lookupVarEnv sigs var of
Just (sig,_) -> sig
-downRhs :: TopLevelFlag
+dmdAnalRhs :: TopLevelFlag
-> 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.
-downRhs top_lvl sigs (id, rhs)
+dmdAnalRhs top_lvl 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
- (lazy_fv, sig_ty) = mkSigTy rhs rhs_ty
- sig = mkStrictSig id arity sig_ty
- id' = id `setIdNewStrictness` sig
- sigs' = extendSigEnv top_lvl sigs id sig
+ (lazy_fv, sig_ty) = WARN( arity /= dmdTypeDepth rhs_ty, ppr id )
+ mkSigTy 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 :: CoreExpr -> DmdType -> (DmdEnv, StrictSig)
+-- Take a DmdType and turn it into a StrictSig
mkSigTy rhs (DmdType fv dmds res)
- = (lazy_fv, DmdType strict_fv lazified_dmds res')
+ = (lazy_fv, mkStrictSig dmd_ty)
where
+ dmd_ty = DmdType strict_fv final_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
-- DmdType, because that makes fixpointing very slow --- the
-- DmdType gets full of lazy demands that are slow to converge.
- lazified_dmds = map lazify dmds
+ lazified_dmds = map funArgDemand dmds
-- Get rid of defers in the arguments
-
- res' = case (dmds, res) of
- ([], RetCPR) | not (exprIsValue rhs) -> TopRes
- other -> res
+ final_dmds = setUnpackStrategy lazified_dmds
+ -- Set the unpacking strategy
+
+ res' = case 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.
-- 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.
+
+\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 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
+-- We already have a suitable demand on all
+-- free vars, so no need to add more!
+splitDmdTy (DmdType fv (dmd:dmds) res_ty) = (dmd, DmdType fv dmds res_ty)
+splitDmdTy ty@(DmdType fv [] TopRes) = (Lazy, ty)
+splitDmdTy ty@(DmdType fv [] BotRes) = (Bot, ty)
+ -- NB: Bot not Abs
+splitDmdTy ty@(DmdType fv [] RetCPR) = panic "splitDmdTy"
+ -- We should not be applying a product as a function!
+\end{code}
+
+\begin{code}
unitVarDmd var dmd = DmdType (unitVarEnv var dmd) [] TopRes
addVarDmd top_lvl dmd_ty@(DmdType fv ds res) var dmd
| otherwise = DmdType (extendVarEnv fv var dmd) ds res
addLazyFVs (DmdType fv ds res) lazy_fvs
- = DmdType (plusUFM_C both fv lazy_fvs) ds res
+ = DmdType both_fv1 ds res
+ where
+ both_fv = (plusUFM_C both fv lazy_fvs)
+ both_fv1 = modifyEnv (isBotRes res) (`both` Bot) lazy_fvs fv both_fv
+ -- This modifyEnv is vital. Consider
+ -- let f = \x -> (x,y)
+ -- in error (f 3)
+ -- Here, y is treated as a lazy-fv of f, but we must `both` that L
+ -- demand with the bottom coming up from 'error'
+ --
+ -- I got a loop in the fixpointer without this, due to an interaction
+ -- with the lazy_fv filtering in mkSigTy. Roughly, it was
+ -- letrec f n x
+ -- = letrec g y = x `fatbar`
+ -- letrec h z = z + ...g...
+ -- in h (f (n-1) x)
+ -- in ...
+ -- In the initial iteration for f, f=Bot
+ -- Suppose h is found to be strict in z, but the occurrence of g in its RHS
+ -- is lazy. Now consider the fixpoint iteration for g, esp the demands it
+ -- places on its free variables. Suppose it places none. Then the
+ -- x `fatbar` ...call to h...
+ -- will give a x->V demand for x. That turns into a L demand for x,
+ -- which floats out of the defn for h. Without the modifyEnv, that
+ -- L demand doesn't get both'd with the Bot coming up from the inner
+ -- call to f. So we just get an L demand for x for g.
+ --
+ -- A better way to say this is that the lazy-fv filtering should give the
+ -- same answer as putting the lazy fv demands in the function's type.
annotateBndr :: DmdType -> Var -> (DmdType, Var)
-- The returned env has the var deleted
-- No effect on the argument demands
annotateBndr dmd_ty@(DmdType fv ds res) var
| isTyVar var = (dmd_ty, var)
- | otherwise = (DmdType fv' ds res, setIdNewDemandInfo var dmd)
+ | otherwise = (DmdType fv' ds res, setIdNewDemandInfo var hacked_dmd)
where
(fv', dmd) = removeFV fv var res
+ hacked_dmd | isUnLiftedType (idType var) = unliftedDemand dmd
+ | otherwise = dmd
annotateBndrs = mapAccumR annotateBndr
-- For lambdas we add the demand to the argument demands
-- Only called for Ids
= ASSERT( isId id )
- (DmdType fv' (dmd:ds) res, setIdNewDemandInfo id dmd)
+ (DmdType fv' (hacked_dmd:ds) res, setIdNewDemandInfo id hacked_dmd)
where
(fv', dmd) = removeFV fv id res
+ hacked_dmd | isUnLiftedType (idType id) = unliftedDemand dmd
+ | otherwise = funArgDemand dmd
+ -- This call to funArgDemand is vital, because otherwise we label
+ -- a lambda binder with demand 'B'. But in terms of calling
+ -- conventions that's Abs, because we don't pass it. But
+ -- when we do a w/w split we get
+ -- fw x = (\x y:B -> ...) x (error "oops")
+ -- And then the simplifier things the 'B' is a strict demand
+ -- and evaluates the (error "oops"). Sigh
removeFV fv var res = (fv', dmd)
where
%************************************************************************
%* *
-\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
- -- 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
+ Seq k ds <- res_dmd -- and the demand looks inside its fields
+ = let
+ StrictSig dmd_ty = idNewStrictness var -- It must have a strictness sig
+ DmdType _ _ con_res = dmd_ty
+ arity = idArity var
+ in
+ if arity == call_depth then -- Saturated, so unleash the demand
+ let
+ -- ds can be empty, when we are just seq'ing the thing
+ -- If so we must make up a suitable bunch of demands
+ dmd_ds | null ds = replicate arity Abs
+ | otherwise = ASSERT( length ds == arity ) ds
+
+ arg_ds = case k of
+ Keep -> bothLazy_s dmd_ds
+ Drop -> dmd_ds
+ Defer -> pprTrace "dmdTransform: surprising!" (ppr var)
+ -- I don't think this can happen
+ dmd_ds
+ -- Important! If we Keep the constructor application, then
+ -- we need the demands the constructor places (always lazy)
+ -- If not, we don't need to. For example:
+ -- f p@(x,y) = (p,y) -- S(AL)
+ -- g a b = f (a,b)
+ -- It's vital that we don't calculate Absent for a!
+ in
+ mkDmdType emptyDmdEnv arg_ds con_res
+ -- Must remember whether it's a product, hence con_res, not TopRes
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 | arity <= depth = dmd_ty
- | otherwise = deferType dmd_ty
+ 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
= 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}
-- 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.
+ -- Peter??
-defer :: Demand -> Demand
--- c.f. `lub` Abs
-defer Abs = Abs
-defer (Seq k _ ds) = Seq k Defer ds
-defer other = Lazy
-
-isStrictDmd :: Demand -> Bool
-isStrictDmd Bot = True
-isStrictDmd Err = True
-isStrictDmd (Seq _ Now _) = True
-isStrictDmd Eval = True
-isStrictDmd (Call _) = True
-isStrictDmd other = False
-
-lazify :: Demand -> Demand
--- 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
+---------------
+bothLazy :: Demand -> Demand
+bothLazy = both Lazy
+bothLazy_s :: [Demand] -> [Demand]
+bothLazy_s = map bothLazy
-betterDemand :: Demand -> Demand -> Bool
--- If d1 `better` d2, and d2 `better` d2, then d1==d2
-betterDemand d1 d2 = (d1 `lub` d2) == d2
+funArgDemand :: Demand -> Demand
+-- The 'Defer' demands are just Lazy at function boundaries
+-- Ugly! Ask John how to improve it.
+funArgDemand (Seq Defer ds) = Lazy
+funArgDemand (Seq k ds) = Seq k (map funArgDemand ds)
+funArgDemand Err = Eval -- Args passed to a bottoming function
+funArgDemand Bot = Abs -- Don't pass args that are consumed by bottom/err
+funArgDemand d = d
+
+unliftedDemand :: Demand -> Demand
+-- Same idea, but for unlifted types the domain is much simpler:
+-- Either we use it (Lazy) or we don't (Abs)
+unliftedDemand Bot = Abs
+unliftedDemand Abs = Abs
+unliftedDemand other = Lazy
\end{code}
-
-%************************************************************************
-%* *
-\subsection{LUB and BOTH}
-%* *
-%************************************************************************
-
\begin{code}
-lub :: Demand -> Demand -> Demand
-
-lub Bot d = d
-
-lub Lazy d = Lazy
-
-lub Err Bot = Err
-lub Err d = d
+betterStrictness :: StrictSig -> StrictSig -> Bool
+betterStrictness (StrictSig t1) (StrictSig t2) = betterDmdType t1 t2
-lub Abs Bot = Abs
-lub Abs Err = Abs
-lub Abs Abs = Abs
-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 (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)
+betterDmdType t1 t2 = (t1 `lubType` t2) == t2
--- 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
+betterDemand :: Demand -> Demand -> Bool
+-- If d1 `better` d2, and d2 `better` d2, then d1==d2
+betterDemand d1 d2 = (d1 `lub` d2) == d2
-or_defer Now Now = Now
-or_defer _ _ = Defer
+squashDmdEnv (StrictSig (DmdType fv ds res)) = StrictSig (DmdType emptyDmdEnv ds res)
+\end{code}
+\begin{code}
-------------------------
-- Consider (if x then y else []) with demand V
-- Then the first branch gives {y->V} and the second
= DmdType lub_fv2 (zipWith lub ds1 ds2) (r1 `lubRes` r2)
where
lub_fv = plusUFM_C lub fv1 fv2
- lub_fv1 = modifyEnv (not (isBotRes r1)) (Abs `lub`) fv2 fv1 lub_fv
- lub_fv2 = modifyEnv (not (isBotRes r2)) (Abs `lub`) fv1 fv2 lub_fv1
+ lub_fv1 = modifyEnv (not (isBotRes r1)) defer fv2 fv1 lub_fv
+ lub_fv2 = modifyEnv (not (isBotRes r2)) defer fv1 fv2 lub_fv1
-- lub is the identity for Bot
--------------------------
-lubRes BotRes r = r
-lubRes r BotRes = r
-lubRes RetCPR RetCPR = RetCPR
-lubRes r1 r2 = TopRes
-
------------------------------------
-vee :: Keepity -> Keepity -> Keepity
-vee Drop Drop = Drop
-vee k1 k2 = Keep
-
------------------------------------
-both :: Demand -> Demand -> Demand
-
--- The normal one
--- both Bot d = Bot
-
--- The experimental one
-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 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
-
--- Part of the Bot like Err experiment
--- 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 (Call d1) (Call d2) = Call (d1 `both` d2)
-
--- The last clauses deal with the remaining cases for Call and Seq
-both d1@(Call _) d2@(Seq _ _ _) = pprPanic "both" (ppr d1 $$ ppr d2)
-both d1 d2 = both d2 d1
-
------------------------------------
-bothRes :: DmdResult -> DmdResult -> DmdResult
--- Left-biased for CPR info
-bothRes BotRes _ = BotRes
-bothRes _ BotRes = BotRes
-bothRes r1 _ = r1
-
-----------------------------------
-- (t1 `bothType` t2) takes the argument/result info from t1,
-- using t2 just for its free-var info
+-- NB: Don't forget about r2! It might be BotRes, which is
+-- a bottom demand on all the in-scope variables.
+-- Peter: can this be done more neatly?
bothType (DmdType fv1 ds1 r1) (DmdType fv2 ds2 r2)
- = DmdType both_fv2 ds1 r1
+ = DmdType both_fv2 ds1 (r1 `bothRes` r2)
where
both_fv = plusUFM_C both fv1 fv2
both_fv1 = modifyEnv (isBotRes r1) (`both` Bot) fv2 fv1 both_fv
-- both is the identity for Abs
\end{code}
+
+\begin{code}
+lubRes BotRes r = r
+lubRes r BotRes = r
+lubRes RetCPR RetCPR = RetCPR
+lubRes r1 r2 = TopRes
+
+-- If either diverges, the whole thing does
+-- Otherwise take CPR info from the first
+bothRes r1 BotRes = BotRes
+bothRes r1 r2 = r1
+\end{code}
+
+\begin{code}
+-- 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
+
+-----------------------------------
+-- 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
+\end{code}
+
\begin{code}
modifyEnv :: Bool -- No-op if False
-> (Demand -> Demand) -- The zapper
%************************************************************************
%* *
-\subsection{Miscellaneous
+\subsection{LUB and BOTH}
%* *
%************************************************************************
\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
+lub :: Demand -> Demand -> Demand
+
+lub Bot d = d
+
+lub Err Bot = Err
+lub Err Abs = Lazy -- E.g. f x = if ... then True else error x
+lub Err d = d
+
+lub Lazy d = Lazy
+
+lub Abs d = defer d
+
+lub Eval Abs = Lazy
+lub Eval Lazy = Lazy
+lub Eval (Seq Drop ds) | not (null ds) = Seq Drop [Lazy | d <- ds]
+lub Eval d = Eval
+ -- For the Seq case, consier
+ -- f n [] = n
+ -- f n (x:xs) = f (n+x) xs
+ -- Here we want to do better than just V for n. It's
+ -- unboxed in the (x:xs) case, and we might be prepared to
+ -- rebox it in the [] case.
+ -- But if we don't use *any* of the components, give up
+ -- and revert to V
+
+lub (Call d1) (Call d2) = Call (lub d1 d2)
+lub d1@(Call _) d2 = d2 `lub` d1
+
+lub (Seq k1 ds1) (Seq k2 ds2)
+ = Seq (k1 `lub_keep` k2) (lub_ds k1 ds1 k2 ds2)
+ where
+ ------------------
+ lub_ds Keep ds1 Keep ds2 = ds1 `lubs` ds2
+ lub_ds Keep ds1 non_keep ds2 | null ds1 = [Lazy | d <- ds2]
+ | otherwise = bothLazy_s ds1 `lubs` ds2
+
+ lub_ds non_keep ds1 Keep ds2 | null ds2 = [Lazy | d <- ds1]
+ | otherwise = ds1 `lubs` bothLazy_s ds2
+
+ lub_ds k1 ds1 k2 ds2 = ds1 `lubs` ds2
+
+ ------------------
+ lub_keep Keep k = k
+
+ lub_keep Drop Defer = Defer
+ lub_keep Drop k = Drop
+
+ lub_keep Defer k = Defer
+
+lub d1@(Seq _ _) d2 = d2 `lub` d1
+
+---------------
+both :: Demand -> Demand -> Demand
+
+both Bot Bot = Bot
+both Bot Abs = Bot
+both Bot d = Err
+
+both Err d = Err
+
+both Abs d = d
+
+both Lazy Bot = Err
+both Lazy Err = Err
+both Lazy Eval = Eval
+both Lazy (Call d) = Call d
+both Lazy (Seq Defer ds) = Lazy
+both Lazy (Seq k ds) = Seq Keep ds
+both Lazy d = Lazy
+
+-- For the (Eval `both` Bot) case, consider
+-- f x = error x
+-- From 'error' itself we get demand Bot on x
+-- From the arg demand on x we get Eval
+-- So we want Eval `both` Bot to be Err.
+-- That's what Err is *for*
+both Eval Bot = Err
+both Eval Err = Err
+both Eval (Seq k ds) = Seq Keep ds
+both Eval d = Eval
+
+both (Call d1) (Call d2) = Call (d1 `both` d2)
+both d1@(Call _) d2 = d2 `both` d1
+
+both (Seq k1 ds1) (Seq k2 ds2)
+ = Seq (k1 `both_keep` k2) (both_ds k1 ds1 k2 ds2)
+ where
+ ----------------
+ both_keep Keep k2 = Keep
+
+ both_keep Drop Keep = Keep
+ both_keep Drop k2 = Drop
+
+ both_keep Defer k2 = k2
+
+ ----------------
+ both_ds Defer ds1 Defer ds2 = ds1 `boths` ds2
+ both_ds Defer ds1 non_defer ds2 = map defer ds1 `boths` ds2
+
+ both_ds non_defer ds1 Defer ds2 = ds1 `boths` map defer ds2
+
+ both_ds k1 ds1 k2 ds2 = ds1 `boths` ds2
+
+both d1@(Seq _ _) d2 = d2 `both` d1
\end{code}
+
+%************************************************************************
+%* *
+\subsection{Miscellaneous
+%* *
+%************************************************************************
+
+
\begin{code}
get_changes binds = vcat (map get_changes_bind binds)
get_changes_bind (NonRec id rhs) = get_changes_pr (id,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 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
where
message word = text word <+> text "demand for" <+> ppr id <+> info
info = (text "Old" <+> ppr old) $$ (text "New" <+> ppr new)
- new = lazify (idNewDemandInfo id) -- Lazify to avoid spurious improvements
+ new = funArgDemand (idNewDemandInfo id) -- FunArgDemand to avoid spurious improvements
old = newDemand (idDemandInfo id)
new_better = new `betterDemand` old
old_better = old `betterDemand` new
-#endif /* DEBUG */
\end{code}