[project @ 2001-09-07 12:42:46 by simonpj]
authorsimonpj <unknown>
Fri, 7 Sep 2001 12:42:47 +0000 (12:42 +0000)
committersimonpj <unknown>
Fri, 7 Sep 2001 12:42:47 +0000 (12:42 +0000)
------------------------
Fix the demand analyser
------------------------

A spiffy new domain for demands, and definitions for lub/both
which are actually monotonic.   Quite a bit of related jiggling
around.

One of the original motivations was to do with functions like:

sum n []     = n
sum n (x:xs) = sum (n+x) xs

Even though n is returned boxed from the first case, we don't want
to get strictness
S(L)V -> T
because that means we pass the box for n, and that is TERRIBLE.
So the new version errs on the side of unboxing, more like the forwards
analyser, and only passes the box if it is *definitely* needed, rather
than if it *may* be needed.

ghc/compiler/basicTypes/IdInfo.lhs
ghc/compiler/basicTypes/NewDemand.lhs
ghc/compiler/parser/Lex.lhs
ghc/compiler/stranal/DmdAnal.lhs
ghc/compiler/stranal/WorkWrap.lhs
ghc/compiler/stranal/WwLib.lhs

index 049a9d6..36fef2f 100644 (file)
@@ -97,8 +97,8 @@ import FieldLabel     ( FieldLabel )
 import Type            ( usOnce, usMany )
 import Demand          hiding( Demand )
 import qualified Demand
-import NewDemand       ( Demand(..), Keepity(..), Deferredness(..), DmdResult(..),
-                         lazyDmd, topDmd,
+import NewDemand       ( Demand(..), Keepity(..), DmdResult(..),
+                         lazyDmd, topDmd, dmdTypeDepth,
                          StrictSig, mkStrictSig, mkTopDmdType
                        )
 import Outputable      
@@ -138,15 +138,19 @@ mkNewStrictnessInfo id arity (Demand.StrictnessInfo ds res) cpr
   | length ds <= arity
        -- Sometimes the old strictness analyser has more
        -- demands than the arity justifies
-  = mkStrictSig id arity $
+  = mk_strict_sig id arity $
     mkTopDmdType (map newDemand ds) (newRes res cpr)
 
 mkNewStrictnessInfo id arity other cpr
   =    -- Either no strictness info, or arity is too small
        -- In either case we can't say anything useful
-    mkStrictSig id arity $
+    mk_strict_sig id arity $
     mkTopDmdType (replicate arity lazyDmd) (newRes False cpr)
 
+mk_strict_sig id arity dmd_ty
+  = WARN( arity /= dmdTypeDepth dmd_ty, ppr id <+> (ppr arity $$ ppr dmd_ty) )
+    mkStrictSig dmd_ty
+
 newRes True  _                 = BotRes
 newRes False ReturnsCPR = RetCPR
 newRes False NoCPRInfo  = TopRes
@@ -155,18 +159,18 @@ newDemand :: Demand.Demand -> NewDemand.Demand
 newDemand (WwLazy True)      = Abs
 newDemand (WwLazy False)     = Lazy
 newDemand WwStrict          = Eval
-newDemand (WwUnpack unpk ds) = Seq Drop Now (map newDemand ds)
+newDemand (WwUnpack unpk ds) = Seq Drop (map newDemand ds)
 newDemand WwPrim            = Lazy
 newDemand WwEnum            = Eval
 
 oldDemand :: NewDemand.Demand -> Demand.Demand
-oldDemand Abs         = WwLazy True
-oldDemand Lazy        = WwLazy False
-oldDemand Bot         = WwStrict
-oldDemand Err         = WwStrict
-oldDemand Eval        = WwStrict
-oldDemand (Seq _ _ ds) = WwUnpack True (map oldDemand ds)
-oldDemand (Call _)     = WwStrict
+oldDemand Abs       = WwLazy True
+oldDemand Lazy      = WwLazy False
+oldDemand Bot       = WwStrict
+oldDemand Err       = WwStrict
+oldDemand Eval      = WwStrict
+oldDemand (Seq _ ds) = WwUnpack True (map oldDemand ds)
+oldDemand (Call _)   = WwStrict
 \end{code}
 
 
index 735c5ef..58d682a 100644 (file)
@@ -5,10 +5,10 @@
 
 \begin{code}
 module NewDemand(
-       Demand(..), Keepity(..), Deferredness(..), 
-       topDmd, lazyDmd, seqDmd, evalDmd, isStrictDmd, isAbsentDmd,
+       Demand(..), Keepity(..), 
+       mkSeq, topDmd, lazyDmd, seqDmd, evalDmd, isStrictDmd, defer,
 
-       DmdType(..), topDmdType, mkDmdType, mkTopDmdType, 
+       DmdType(..), topDmdType, botDmdType, mkDmdType, mkTopDmdType, 
                dmdTypeDepth, dmdTypeRes,
        DmdEnv, emptyDmdEnv,
        DmdResult(..), isBotRes, returnsCPR,
@@ -21,10 +21,8 @@ module NewDemand(
 #include "HsVersions.h"
 
 import BasicTypes      ( Arity )
-import Var             ( Id )
 import VarEnv          ( VarEnv, emptyVarEnv )
 import UniqFM          ( ufmToList )
-import qualified Demand
 import Outputable
 \end{code}
 
@@ -145,10 +143,8 @@ instance Outputable StrictSig where
 instance Show StrictSig where
    show (StrictSig ty) = showSDoc (ppr ty)
 
-mkStrictSig :: Id -> Arity -> DmdType -> StrictSig
-mkStrictSig id arity dmd_ty
-  = WARN( arity /= dmdTypeDepth dmd_ty, ppr id <+> (ppr arity $$ ppr dmd_ty) )
-    StrictSig dmd_ty
+mkStrictSig :: DmdType -> StrictSig
+mkStrictSig dmd_ty = StrictSig dmd_ty
 
 splitStrictSig :: StrictSig -> ([Demand], DmdResult)
 splitStrictSig (StrictSig (DmdType _ dmds res)) = (dmds, res)
@@ -184,58 +180,69 @@ data Demand
   = Lazy               -- L; used for unlifted types too, so that
                        --      A `lub` L = L
   | Abs                        -- A
+
   | Call Demand                -- C(d)
   | Eval               -- V
-  | Seq Keepity                -- S/U(ds)
-       Deferredness
-       [Demand]
+  | Seq Keepity                -- S/U/D(ds)
+       [Demand]        --      S(ds) = L `both` U(ds)
+                       --      D(ds) = A `lub`  U(ds)
+                       -- *** Invariant: these demands are never Bot or Abs
+                       -- *** Invariant: if all demands are Abs, get []
+
   | Err                        -- X
   | Bot                        -- B
   deriving( Eq )
        -- Equality needed for fixpoints in DmdAnal
 
-data Deferredness = Now | Defer
-                 deriving( Eq )
-
-data Keepity = Keep | Drop
+data Keepity = Keep | Drop | Defer
             deriving( Eq )
 
+mkSeq :: Keepity -> [Demand] -> Demand
+mkSeq k ds | all is_absent ds = Seq k []
+          | otherwise        = Seq k ds
+          where
+            is_absent Abs = True
+            is_absent d   = False
+
+defer :: Demand -> Demand
+-- Computes (Abs `lub` d)
+-- For the Bot case consider
+--     f x y = if ... then x else error x
+--   Then for y we get Abs `lub` Bot, and we really
+--   want Abs overall
+defer Bot          = Abs
+defer Abs          = Abs
+defer (Seq Keep ds) = Lazy
+defer (Seq _    ds) = Seq Defer ds
+defer d                    = Lazy
+
 topDmd, lazyDmd, seqDmd :: Demand
-topDmd  = Lazy                 -- The most uninformative demand
+topDmd  = Lazy         -- The most uninformative demand
 lazyDmd = Lazy
-seqDmd  = Seq Keep Now []      -- Polymorphic seq demand
+seqDmd  = Seq Keep []  -- Polymorphic seq demand
 evalDmd = Eval
 
 isStrictDmd :: Demand -> Bool
-isStrictDmd Bot          = True
-isStrictDmd Err          = True           
-isStrictDmd (Seq _ Now _) = True
-isStrictDmd Eval         = True
-isStrictDmd (Call _)     = True
-isStrictDmd other        = False
-
-isAbsentDmd :: Demand -> Bool
-isAbsentDmd Bot          = True
-isAbsentDmd Err          = True
-isAbsentDmd Abs          = True
-isAbsentDmd other = False
+isStrictDmd Bot       = True
+isStrictDmd Err              = True               
+isStrictDmd (Seq _ _) = True
+isStrictDmd Eval      = True
+isStrictDmd (Call _)  = True
+isStrictDmd other     = False
 
 instance Outputable Demand where
-    ppr Lazy        = char 'L'
-    ppr Abs         = char 'A'
-    ppr Eval         = char 'V'
-    ppr Err          = char 'X'
-    ppr Bot          = char 'B'
-    ppr (Call d)     = char 'C' <> parens (ppr d)
-    ppr (Seq k l []) = ppr k <> ppr l
-    ppr (Seq k l ds) = ppr k <> ppr l <> parens (hcat (map ppr ds))
-
-instance Outputable Deferredness where
-  ppr Now   = empty
-  ppr Defer = char '*'
+    ppr Lazy      = char 'L'
+    ppr Abs       = char 'A'
+    ppr Eval       = char 'V'
+    ppr Err        = char 'X'
+    ppr Bot        = char 'B'
+    ppr (Call d)   = char 'C' <> parens (ppr d)
+    ppr (Seq k []) = ppr k
+    ppr (Seq k ds) = ppr k <> parens (hcat (map ppr ds))
 
 instance Outputable Keepity where
-  ppr Keep = char 'S'
-  ppr Drop = char 'U'
+  ppr Keep  = char 'S'
+  ppr Drop  = char 'U'
+  ppr Defer = char 'D'
 \end{code}
 
index 5e82b53..3bdaed4 100644 (file)
@@ -41,7 +41,7 @@ import PrelNames      ( mkTupNameStr )
 import CmdLineOpts     ( opt_HiVersion, opt_NoHiCheck )
 import ForeignCall     ( Safety(..) )
 import NewDemand       ( StrictSig(..), Demand(..), Keepity(..), 
-                         DmdResult(..), Deferredness(..), mkTopDmdType )
+                         DmdResult(..), mkTopDmdType )
 import UniqFM           ( listToUFM, lookupUFM )
 import BasicTypes      ( Boxity(..) )
 import SrcLoc          ( SrcLoc, incSrcLine, srcLocFile, srcLocLine,
@@ -833,19 +833,19 @@ lex_demand cont buf =
     'B'# -> read_em (Bot : acc) (stepOn buf)
     ')'# -> (reverse acc, stepOn buf)
     'C'# -> do_call acc (stepOnBy# buf 2#)
-    'U'# -> do_unpack1 Drop Now acc (stepOnBy# buf 1#)
-    'S'# -> do_unpack1 Keep Now acc (stepOnBy# buf 1#)
+    'D'# -> do_unpack1 Defer acc (stepOnBy# buf 1#)
+    'U'# -> do_unpack1 Drop acc (stepOnBy# buf 1#)
+    'S'# -> do_unpack1 Keep acc (stepOnBy# buf 1#)
     _    -> (reverse acc, buf)
 
-  do_unpack1 keepity defer acc buf
+  do_unpack1 keepity acc buf
     = case currentChar# buf of
-       '*'# -> do_unpack1 keepity Defer acc (stepOnBy# buf 1#)
-       '('# -> do_unpack2 keepity defer acc (stepOnBy# buf 1#)
-       _    -> read_em (Seq keepity defer [] : acc) buf
+       '('# -> do_unpack2 keepity acc (stepOnBy# buf 1#)
+       _    -> read_em (Seq keepity [] : acc) buf
 
-  do_unpack2 keepity defer acc buf
+  do_unpack2 keepity acc buf
     = case read_em [] buf of
-        (stuff, rest) -> read_em (Seq keepity defer stuff : acc) rest
+        (stuff, rest) -> read_em (Seq keepity stuff : acc) rest
 
   do_call acc buf
     = case read_em [] buf of
index cc4916b..66e1395 100644 (file)
@@ -7,17 +7,20 @@
                        -----------------
 
 \begin{code}
-module DmdAnal ( dmdAnalPgm, both {- needed by WwLib -} ) where
+module DmdAnal ( dmdAnalPgm, dmdAnalTopRhs, 
+                both {- needed by WwLib -}
+   ) where
 
 #include "HsVersions.h"
 
 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, idDemandInfo,
+import Id              ( Id, idType, idDemandInfo, 
                          isDataConId, isImplicitId, isGlobalId,
                          idNewStrictness, idNewStrictness_maybe, getNewStrictness, setIdNewStrictness,
                          idNewDemandInfo, setIdNewDemandInfo, newStrictnessFromOld )
@@ -90,6 +93,18 @@ dmdAnalTopBind sigs (Rec pairs)
     (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}
 
 %************************************************************************
 %*                                                                     *
@@ -101,6 +116,7 @@ dmdAnalTopBind sigs (Rec pairs)
 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
@@ -197,7 +213,7 @@ dmdAnal sigs dmd (Case scrut case_bndr [alt@(DataAlt dc,bndrs,rhs)])
        -- 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               = Seq Drop Now [idNewDemandInfo b | b <- bndrs', isId b]
+        scrut_dmd               = mkSeq Drop [idNewDemandInfo b | b <- bndrs', isId b]
                                   `both`
                                   idNewDemandInfo case_bndr'
 
@@ -274,17 +290,18 @@ dmdFix top_lvl sigs pairs
         -> [(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 <+> (vcat 
+      | n >= 5       = 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:" <+> ppr pairs]))
+                                 text "binds:" <+> pprCoreBinding (Rec pairs)]))
                              (loop (n+1) sigs' pairs')
-      | otherwise          = {- pprTrace "dmdFixLoop" (ppr id_sigs) -} (loop (n+1) sigs' pairs')
+      | 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
@@ -325,7 +342,8 @@ downRhs top_lvl sigs (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 id arity rhs rhs_ty
+  (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}
@@ -337,10 +355,10 @@ downRhs top_lvl sigs (id, rhs)
 %************************************************************************
 
 \begin{code}
-mkSigTy :: Id -> Arity -> CoreExpr -> DmdType -> (DmdEnv, StrictSig)
+mkSigTy :: 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)
+mkSigTy rhs (DmdType fv dmds res) 
+  = (lazy_fv, mkStrictSig dmd_ty)
   where
     dmd_ty = DmdType strict_fv final_dmds res'
 
@@ -376,14 +394,14 @@ mkSigTy id arity rhs (DmdType fv dmds res)
        -- 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
     final_dmds = setUnpackStrategy lazified_dmds
        -- Set the unpacking strategy
        
-    res' = case (dmds, res) of
-               ([], RetCPR) | not (exprIsValue rhs) -> TopRes
-               other                                -> res
+    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.
@@ -413,8 +431,8 @@ setUnpackStrategy ds
        -> [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
+    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
@@ -447,11 +465,12 @@ nonAbsentArgs (d   : ds) = 1 + nonAbsentArgs ds
 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)
+splitDmdTy ty@(DmdType fv [] TopRes)         = (Lazy, ty)
+splitDmdTy ty@(DmdType fv [] BotRes)         = (Bot,  ty)
+       -- NB: Bot not Abs
+splitDmdTy (DmdType fv [] RetCPR)        = panic "splitDmdTy"
        -- 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}
@@ -462,7 +481,35 @@ 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
@@ -470,9 +517,11 @@ annotateBndr :: DmdType -> Var -> (DmdType, Var)
 -- 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
 
@@ -483,11 +532,9 @@ annotateLamIdBndr dmd_ty@(DmdType fv ds res) id
     (DmdType fv' (hacked_dmd:ds) res, setIdNewDemandInfo id hacked_dmd)
   where
     (fv', dmd) = removeFV fv id res
-    hacked_dmd = case dmd of
-                   Bot   -> Abs
-                   Err   -> Abs
-                   other -> dmd
-       -- This gross hack is needed because otherwise we label
+    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
@@ -536,15 +583,16 @@ dmdTransform sigs var dmd
 
 ------         DATA CONSTRUCTOR
   | isDataConId var,           -- Data constructor
-    Seq k Now ds <- res_dmd,   -- and the demand looks inside its fields
+    Seq k ds <- res_dmd,       -- and the demand looks inside its fields
     let StrictSig dmd_ty = idNewStrictness var,        -- It must have a strictness sig
     let DmdType _ con_ds con_res = dmd_ty
   = if length con_ds == length ds then -- Saturated, so unleash the demand
        -- ds can be empty, when we are just seq'ing the thing
        let 
           arg_ds = case k of
-                       Keep -> zipWith lub ds con_ds
-                       Drop -> ds
+                       Keep  -> zipWith lub ds con_ds
+                       Drop  -> ds
+                       Defer -> ds
                -- Important!  If we Keep the constructor application, then
                -- we need the demands the constructor places (usually lazy)
                -- If not, we don't need to.  For example:
@@ -608,17 +656,27 @@ deferType (DmdType fv _ _) = DmdType (mapVarEnv defer fv) [] TopRes
        -- For example,   f = let ... in \x -> x
        -- We don't want to get a stricness type V->T for f.
 
-defer :: Demand -> Demand
-defer = lub Abs
+---------------
+bothLazy :: Demand -> Demand
+bothLazy   = both Lazy
+bothLazy_s :: [Demand] -> [Demand]
+bothLazy_s = map bothLazy
 
-lazify :: Demand -> Demand
+funArgDemand :: Demand -> Demand
 -- The 'Defer' demands are just Lazy at function boundaries
 -- Ugly!  Ask John how to improve it.
-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/err
-lazify Err             = Abs
-lazify d               = d
+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}
 
 \begin{code}
@@ -634,71 +692,7 @@ betterDemand d1 d2 = (d1 `lub` d2) == d2
 squashDmdEnv (StrictSig (DmdType fv ds res)) = StrictSig (DmdType emptyDmdEnv ds res)
 \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 
-
-lub Abs Bot         = Abs      -- E.g f x y = if ... then x else error x
-                               -- Then for y we get Abs `lub` Bot, and we really
-                               -- want Abs overall
-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) = Eval                -- Urk!  Is this monotonic?
-       -- Was (incorrectly): 
-       --      lub Eval (Seq k Now ds) = Seq Keep Now ds
-       -- Incorrect because 
-       --      Eval `lub` U(VV) is not S(VV)
-       -- (because the components aren't necessarily evaluated)
-       --
-       -- Was (correctly, but pessimistically): 
-       --      lub Eval (Seq k Now ds) = Eval
-       -- Pessimistic because
-       --      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.
-       -- To achieve this we could perhaps consider Eval to be equivalent to
-       --      U(L), or S(A)
-
-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) (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
-
 -------------------------
 -- Consider (if x then y else []) with demand V
 -- Then the first branch gives {y->V} and the second
@@ -708,93 +702,47 @@ lubType (DmdType fv1 ds1 r1) (DmdType fv2 ds2 r2)
   = 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
 
--------------------------
+-----------------------------------
+-- (t1 `bothType` t2) takes the argument/result info from t1,
+-- using t2 just for its free-var info
+bothType (DmdType fv1 ds1 r1) (DmdType fv2 ds2 r2)
+  = 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_fv2 = modifyEnv (isBotRes r2) (`both` Bot) fv1 fv2 both_fv1
+       -- 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
 
------------------------------------
-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
--- 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 d   = d
+-- If either diverges, the whole thing does
+-- Otherwise take CPR info from the first
+bothRes BotRes r2     = BotRes
+bothRes r1     BotRes = BotRes
+bothRes r1     r2     = r1
+\end{code}
 
-both Err Bot = Err
-both Err Abs = Err
-both Err 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 (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 (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)
-
--- 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
+\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
-
------------------------------------
--- (t1 `bothType` t2) takes the argument/result info from t1,
--- using t2 just for its free-var info
-bothType (DmdType fv1 ds1 r1) (DmdType fv2 ds2 r2)
-  = DmdType both_fv2 ds1 r1
-  where
-    both_fv  = plusUFM_C both fv1 fv2
-    both_fv1 = modifyEnv (isBotRes r1) (`both` Bot) fv2 fv1 both_fv
-    both_fv2 = modifyEnv (isBotRes r2) (`both` Bot) fv1 fv2 both_fv1
-       -- both is the identity for Abs
 \end{code}
 
 \begin{code}
@@ -817,6 +765,120 @@ modifyEnv need_to_modify zapper env1 env2 env
 
 %************************************************************************
 %*                                                                     *
+\subsection{LUB and BOTH}
+%*                                                                     *
+%************************************************************************
+
+
+\begin{code}
+lub :: Demand -> Demand -> Demand
+
+lub Bot d = d
+
+lub Err Bot = Err 
+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
 %*                                                                     *
 %************************************************************************
@@ -869,7 +931,7 @@ get_changes_dmd id
   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
index fff7082..331b623 100644 (file)
@@ -14,12 +14,9 @@ import CoreLint              ( showPass, endPass )
 import CoreUtils       ( exprType )
 import Id              ( Id, idType, idNewStrictness, idArity, isOneShotLambda,
                          setIdNewStrictness, zapIdNewStrictness, idInlinePragma, mkWorkerId,
-                         setIdWorkerInfo, idCprInfo, setInlinePragma )
+                         setIdWorkerInfo, setInlinePragma )
 import Type            ( Type )
-import IdInfo          ( mkStrictnessInfo, noStrictnessInfo, StrictnessInfo(..),
-                         CprInfo(..), InlinePragInfo(..), isNeverInlinePrag,
-                         WorkerInfo(..)
-                       )
+import IdInfo          ( InlinePragInfo(..), isNeverInlinePrag, WorkerInfo(..) )
 import NewDemand        ( Demand(..), StrictSig(..), DmdType(..), DmdResult(..), 
                          mkTopDmdType, isBotRes, returnsCPR
                        )
@@ -297,9 +294,9 @@ worthSplitting (StrictSig (DmdType _ ds res))
        -- [We don't do reboxing now, but in general it's better to pass 
        --  an unboxed thing to f, and have it reboxed in the error cases....]
   where
-    worth_it Abs         = True        -- Absent arg
-    worth_it (Seq _ _ ds) = True       -- Arg to evaluate
-    worth_it other       = False
+    worth_it Abs       = True  -- Absent arg
+    worth_it (Seq _ ds) = True -- Arg to evaluate
+    worth_it other     = False
 \end{code}
 
 
@@ -324,5 +321,3 @@ mkWrapper fun_ty (StrictSig (DmdType _ demands res_info))
 
 noOneShotInfo = repeat False
 \end{code}
-
-
index 2cad15a..4177a05 100644 (file)
@@ -16,7 +16,7 @@ import Id             ( Id, idType, mkSysLocal, idNewDemandInfo, setIdNewDemandInfo,
                        )
 import IdInfo          ( vanillaIdInfo )
 import DataCon         ( splitProductType_maybe, splitProductType )
-import NewDemand       ( Demand(..), Keepity(..), DmdResult(..), isAbsentDmd ) 
+import NewDemand       ( Demand(..), Keepity(..), DmdResult(..) ) 
 import DmdAnal         ( both )
 import PrelInfo                ( realWorldPrimId, aBSENT_ERROR_ID, eRROR_CSTRING_ID )
 import TysPrim         ( realWorldStatePrimTy )
@@ -271,7 +271,7 @@ mkWWstr :: Type                                     -- Result type
                                                -- but *with* lambdas
 
 mkWWstr res_ty wrap_args
-  = mk_ww_str wrap_args                `thenUs` \ (work_args, take_apart, put_together) ->
+  = mk_ww_str_s wrap_args              `thenUs` \ (work_args, take_apart, put_together) ->
     let
        work_dmds = [idNewDemandInfo v | v <- work_args, isId v]
        apply_to args fn = mkVarApps fn args
@@ -297,17 +297,23 @@ mkWWstr res_ty wrap_args
              take_apart . applyToVars [realWorldPrimId] . apply_to work_args,
              mkLams work_args . Lam void_arg . put_together)
 
-       -- Empty case
-mk_ww_str []
-  = returnUs ([],
-             \ wrapper_body -> wrapper_body,
-             \ worker_body  -> worker_body)
+----------------------
+nop_fn body = body
 
+----------------------
+mk_ww_str_s []
+  = returnUs ([], nop_fn, nop_fn)
 
-mk_ww_str (arg : ds)
+mk_ww_str_s (arg : args)
+  = mk_ww_str arg              `thenUs` \ (args1, wrap_fn1, work_fn1) ->
+    mk_ww_str_s args           `thenUs` \ (args2, wrap_fn2, work_fn2) ->
+    returnUs (args1 ++ args2, wrap_fn1 . wrap_fn2, work_fn1 . work_fn2)
+
+
+----------------------
+mk_ww_str arg
   | isTyVar arg
-  = mk_ww_str ds               `thenUs` \ (worker_args, wrap_fn, work_fn) ->
-    returnUs (arg : worker_args, wrap_fn, work_fn)
+  = returnUs ([arg],  nop_fn, nop_fn)
 
   | otherwise
   = case idNewDemandInfo arg of
@@ -316,19 +322,16 @@ mk_ww_str (arg : ds)
        -- though, because it's not so easy to manufacture a placeholder
        -- We'll see if this turns out to be a problem
       Abs | not (isUnLiftedType (idType arg)) ->
-       mk_ww_str ds            `thenUs` \ (worker_args, wrap_fn, work_fn) ->
-       returnUs (worker_args, wrap_fn, mk_absent_let arg . work_fn)
+       returnUs ([], nop_fn, mk_absent_let arg) 
 
        -- Seq and keep
-      Seq _ _ cs 
-       | all isAbsentDmd cs
-       -> mk_ww_str ds         `thenUs` \ (worker_args, wrap_fn, work_fn) ->
-          let
+      Seq _ []
+       -> let
                arg_w_unf = arg `setIdUnfolding` mkOtherCon []
                -- Tell the worker arg that it's sure to be evaluated
                -- so that internal seqs can be dropped
           in
-          returnUs (arg_w_unf : worker_args, mk_seq_case arg . wrap_fn, work_fn)
+          returnUs ([arg_w_unf], mk_seq_case arg, nop_fn)
                -- Pass the arg, anyway, even if it is in theory discarded
                -- Consider
                --      f x y = x `seq` y
@@ -342,9 +345,8 @@ mk_ww_str (arg : ds)
                -- But the Evald flag is pretty wierd, and I worry that it might disappear
                -- during simplification, so for now I've just nuked this whole case
                        
-
        -- Unpack case
-      Seq keep _ cs 
+      Seq keep cs 
        | Just (arg_tycon, tycon_arg_tys, data_con, inst_con_arg_tys) 
                <- splitProductType_maybe (idType arg)
        -> getUniquesUs                 `thenUs` \ uniqs ->
@@ -352,7 +354,8 @@ mk_ww_str (arg : ds)
             unpk_args      = zipWith mk_ww_local uniqs inst_con_arg_tys
             unpk_args_w_ds = zipWithEqual "mk_ww_str" set_worker_arg_info unpk_args cs'
             unbox_fn       = mk_unpk_case arg unpk_args data_con arg_tycon
-            rebox_fn       = mk_pk_let arg data_con tycon_arg_tys unpk_args
+            rebox_fn       = Let (NonRec arg con_app) 
+            con_app        = mkConApp data_con (map Type tycon_arg_tys ++ map Var unpk_args)
 
             cs' = case keep of
                        Keep -> map (DmdAnal.both Lazy) cs      -- Careful! Now we don't pass
@@ -361,7 +364,7 @@ mk_ww_str (arg : ds)
                                                                --      S(LA) -->  U(LL)
                        Drop -> cs
           in
-          mk_ww_str (unpk_args_w_ds ++ ds)             `thenUs` \ (worker_args, wrap_fn, work_fn) ->
+          mk_ww_str_s unpk_args_w_ds           `thenUs` \ (worker_args, wrap_fn, work_fn) ->
 
 --        case keep of
 --          Keep -> returnUs (arg : worker_args, unbox_fn . wrap_fn, work_fn)
@@ -380,13 +383,11 @@ mk_ww_str (arg : ds)
 
        | otherwise -> 
           WARN( True, ppr arg )
-          mk_ww_str ds         `thenUs` \ (worker_args, wrap_fn, work_fn) ->
-          returnUs (arg : worker_args, wrap_fn, work_fn)
+          returnUs ([arg], nop_fn, nop_fn)
 
        -- Other cases
-      other_demand ->
-       mk_ww_str ds            `thenUs` \ (worker_args, wrap_fn, work_fn) ->
-       returnUs (arg : worker_args, wrap_fn, work_fn)
+      other_demand -> returnUs ([arg], nop_fn, nop_fn)
+
   where
        -- If the wrapper argument is a one-shot lambda, then
        -- so should (all) the corresponding worker arguments be
@@ -512,10 +513,5 @@ sanitiseCaseBndr :: Id -> Id
 -- like                (x+y) `seq` ....
 sanitiseCaseBndr id = id `setIdInfo` vanillaIdInfo
 
-mk_pk_let arg boxing_con con_tys unpk_args body
-  = Let (NonRec arg (mkConApp boxing_con con_args)) body
-  where
-    con_args = map Type con_tys ++ map Var unpk_args
-
 mk_ww_local uniq ty = mkSysLocal SLIT("ww") uniq ty
 \end{code}