[project @ 2001-07-17 15:28:30 by simonpj]
authorsimonpj <unknown>
Tue, 17 Jul 2001 15:28:31 +0000 (15:28 +0000)
committersimonpj <unknown>
Tue, 17 Jul 2001 15:28:31 +0000 (15:28 +0000)
--------------------------------
First cut at the demand analyser
--------------------------------

This demand analyser is intended to replace the strictness/absence
analyser, and the CPR analyser.

This commit adds it to the compiler, but in an entirely non-invasive
way.

If you build the compiler without -DDEBUG,
you won't get it at all.

If you build the compiler with -DDEBUG,
you'll get the demand analyser, but the existing
strictness analyser etc are still there.  All the
demand analyser does is to compare its output with
the existing stuff and report differences.

There's no cross-module stuff for demand info yet.

The strictness/demand info is put the IdInfo as
newStrictnessInfo
newDemandInfo

Eventually we'll remove the old ones.

Simon

ghc/compiler/basicTypes/IdInfo.lhs
ghc/compiler/basicTypes/MkId.lhs
ghc/compiler/basicTypes/NewDemand.lhs [new file with mode: 0644]
ghc/compiler/coreSyn/PprCore.lhs
ghc/compiler/main/DriverState.hs
ghc/compiler/simplCore/OccurAnal.lhs
ghc/compiler/simplCore/SimplCore.lhs
ghc/compiler/stranal/DmdAnal.lhs [new file with mode: 0644]
ghc/compiler/utils/UniqFM.lhs

index 29e644d..426d084 100644 (file)
@@ -23,6 +23,10 @@ module IdInfo (
        exactArity, atLeastArity, unknownArity, hasArity,
        arityInfo, setArityInfo, ppArityInfo, arityLowerBound,
 
+       -- New demand and strictness info
+       newStrictnessInfo, setNewStrictnessInfo, mkNewStrictnessInfo,
+       newDemandInfo, setNewDemandInfo, newDemand,
+
        -- Strictness; imported from Demand
        StrictnessInfo(..),
        mkStrictnessInfo, noStrictnessInfo,
@@ -92,8 +96,10 @@ import ForeignCall   ( ForeignCall )
 import FieldLabel      ( FieldLabel )
 import Type            ( usOnce, usMany )
 import Demand          -- Lots of stuff
+import qualified NewDemand
 import Outputable      
 import Util            ( seqList )
+import List            ( replicate )
 
 infixl         1 `setDemandInfo`,
          `setTyGenInfo`,
@@ -108,12 +114,48 @@ infixl    1 `setDemandInfo`,
          `setOccInfo`,
          `setCgInfo`,
          `setCafInfo`,
-         `setCgArity`
+         `setCgArity`,
+         `setNewStrictnessInfo`,
+         `setNewDemandInfo`
        -- infixl so you can say (id `set` a `set` b)
 \end{code}
 
 %************************************************************************
 %*                                                                     *
+\subsection{New strictness info}
+%*                                                                     *
+%************************************************************************
+
+To be removed later
+
+\begin{code}
+mkNewStrictnessInfo :: Arity -> StrictnessInfo -> CprInfo -> NewDemand.StrictSig
+mkNewStrictnessInfo arity NoStrictnessInfo cpr
+  = NewDemand.mkStrictSig 
+       arity
+       (NewDemand.mkDmdFun (replicate arity NewDemand.Lazy) (newRes False cpr))
+
+mkNewStrictnessInfo arity (StrictnessInfo ds res) cpr
+  = NewDemand.mkStrictSig 
+       arity
+       (NewDemand.mkDmdFun (map newDemand ds) (newRes res cpr))
+
+newRes True  _                  = NewDemand.BotRes
+newRes False ReturnsCPR = NewDemand.RetCPR
+newRes False NoCPRInfo  = NewDemand.TopRes
+
+newDemand :: Demand -> NewDemand.Demand
+newDemand (WwLazy True)      = NewDemand.Abs
+newDemand (WwLazy False)     = NewDemand.Lazy
+newDemand WwStrict          = NewDemand.Eval
+newDemand (WwUnpack unpk ds) = NewDemand.Seq NewDemand.Drop (map newDemand ds)
+newDemand WwPrim            = NewDemand.Lazy
+newDemand WwEnum            = NewDemand.Eval
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
 \subsection{GlobalIdDetails
 %*                                                                     *
 %************************************************************************
@@ -185,7 +227,10 @@ data IdInfo
        cprInfo         :: CprInfo,             -- Function always constructs a product result
         lbvarInfo      :: LBVarInfo,           -- Info about a lambda-bound variable
        inlinePragInfo  :: InlinePragInfo,      -- Inline pragma
-       occInfo         :: OccInfo              -- How it occurs
+       occInfo         :: OccInfo,             -- How it occurs
+
+       newStrictnessInfo :: Maybe NewDemand.StrictSig,
+       newDemandInfo     :: NewDemand.Demand
     }
 
 seqIdInfo :: IdInfo -> ()
@@ -246,6 +291,9 @@ setArityInfo          info ar = info { arityInfo = ar  }
 setCgInfo         info cg = info { cgInfo = cg }
 setCprInfo        info cp = info { cprInfo = cp }
 setLBVarInfo      info lb = info { lbvarInfo = lb }
+
+setNewDemandInfo     info dd = info { newDemandInfo = dd }
+setNewStrictnessInfo info dd = info { newStrictnessInfo = Just dd }
 \end{code}
 
 
@@ -264,7 +312,9 @@ vanillaIdInfo
            cprInfo             = NoCPRInfo,
            lbvarInfo           = NoLBVarInfo,
            inlinePragInfo      = NoInlinePragInfo,
-           occInfo             = NoOccInfo
+           occInfo             = NoOccInfo,
+           newDemandInfo       = NewDemand.topDmd,
+           newStrictnessInfo   = Nothing
           }
 
 noCafNoTyGenIdInfo = vanillaIdInfo `setTyGenInfo` TyGenNever
index 477d63c..21eded9 100644 (file)
@@ -58,7 +58,7 @@ import Name           ( mkWiredInName, mkFCallName, Name )
 import OccName         ( mkVarOcc )
 import PrimOp          ( PrimOp(DataToTagOp), primOpSig, mkPrimOpIdName )
 import ForeignCall     ( ForeignCall )
-import Demand          ( wwStrict, wwPrim, mkStrictnessInfo, 
+import Demand          ( wwStrict, wwPrim, mkStrictnessInfo, noStrictnessInfo,
                          StrictnessMark(..), isMarkedUnboxed, isMarkedStrict )
 import DataCon         ( DataCon, 
                          dataConFieldLabels, dataConRepArity, dataConTyCon,
@@ -75,7 +75,8 @@ import Id             ( idType, mkGlobalId, mkVanillaGlobal, mkSysLocal,
 import IdInfo          ( IdInfo, noCafNoTyGenIdInfo,
                          exactArity, setUnfoldingInfo, setCprInfo,
                          setArityInfo, setSpecInfo,  setCgInfo,
-                         mkStrictnessInfo, setStrictnessInfo,
+                         setStrictnessInfo,
+                         mkNewStrictnessInfo, setNewStrictnessInfo,
                          GlobalIdDetails(..), CafInfo(..), CprInfo(..), 
                          CgInfo(..), setCgArity
                        )
@@ -143,11 +144,11 @@ mkDataConId work_name data_con
     info = noCafNoTyGenIdInfo
           `setCgArity`         arity
           `setArityInfo`       exactArity arity
-          `setStrictnessInfo`  strict_info
           `setCprInfo`         cpr_info
+          `setStrictnessInfo`  strict_info
+          `setNewStrictnessInfo`       mkNewStrictnessInfo arity strict_info cpr_info
 
     arity = dataConRepArity data_con
-
     strict_info = mkStrictnessInfo (dataConRepStrictness data_con, False)
 
     tycon = dataConTyCon data_con
@@ -225,6 +226,7 @@ mkDataConWrapId data_con
           `setArityInfo`       exactArity arity
                -- It's important to specify the arity, so that partial
                -- applications are treated as values
+          `setNewStrictnessInfo`       mkNewStrictnessInfo arity noStrictnessInfo cpr_info
 
     wrap_ty = mkForAllTys all_tyvars $
              mkFunTys all_arg_tys
@@ -604,6 +606,7 @@ mkPrimOpId prim_op
           `setCgArity`         arity
           `setArityInfo`       exactArity arity
           `setStrictnessInfo`  strict_info
+          `setNewStrictnessInfo`       mkNewStrictnessInfo arity strict_info NoCPRInfo
 
     rules = maybe emptyCoreRules (addRule emptyCoreRules id)
                (primOpRule prim_op)
@@ -635,6 +638,7 @@ mkFCallId uniq fcall ty
           `setCgArity`         arity
           `setArityInfo`       exactArity arity
           `setStrictnessInfo`  strict_info
+          `setNewStrictnessInfo`       mkNewStrictnessInfo arity strict_info NoCPRInfo
 
     (_, tau)    = tcSplitForAllTys ty
     (arg_tys, _) = tcSplitFunTys tau
@@ -831,8 +835,11 @@ pcMiscPrelId key mod str ty info
 pc_bottoming_Id key mod name ty
  = pcMiscPrelId key mod name ty bottoming_info
  where
+    strict_info = mkStrictnessInfo ([wwStrict], True)
     bottoming_info = noCafNoTyGenIdInfo 
-                    `setStrictnessInfo` mkStrictnessInfo ([wwStrict], True)
+                    `setStrictnessInfo`  strict_info
+                    `setNewStrictnessInfo`     mkNewStrictnessInfo 1 strict_info NoCPRInfo
+
 
        -- these "bottom" out, no matter what their arguments
 
diff --git a/ghc/compiler/basicTypes/NewDemand.lhs b/ghc/compiler/basicTypes/NewDemand.lhs
new file mode 100644 (file)
index 0000000..b7eb765
--- /dev/null
@@ -0,0 +1,121 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
+%
+\section[Demand]{@Demand@: the amount of demand on a value}
+
+\begin{code}
+module NewDemand(
+       Demand(..), Keepity(..), topDmd,
+       StrictSig(..), topSig, botSig, mkStrictSig,
+       DmdType(..), topDmdType, mkDmdFun,
+       Result(..)
+     ) where
+
+#include "HsVersions.h"
+
+import BasicTypes      ( Arity )
+import qualified Demand
+import Outputable
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection{Strictness signatures
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+data StrictSig = StrictSig Arity DmdType
+              deriving( Eq )
+       -- Equality needed when comparing strictness 
+       -- signatures for fixpoint finding
+
+topSig = StrictSig 0 topDmdType
+botSig = StrictSig 0 botDmdType
+
+mkStrictSig :: Arity -> DmdType -> StrictSig
+mkStrictSig arity ty 
+  = WARN( arity /= dmdTypeDepth ty, ppr arity $$ ppr ty )
+    StrictSig arity ty
+
+instance Outputable StrictSig where
+  ppr (StrictSig arity ty) = ppr ty
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection{Demand types}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+data DmdType = DmdRes Result | DmdFun Demand DmdType
+            deriving( Eq )
+       -- Equality needed for fixpoints in DmdAnal
+
+data Result = TopRes   -- Nothing known        
+           | RetCPR    -- Returns a constructed product
+           | BotRes    -- Diverges or errors
+           deriving( Eq )
+       -- Equality needed for fixpoints in DmdAnal
+
+instance Outputable DmdType where
+  ppr (DmdRes TopRes) = char 'T'
+  ppr (DmdRes RetCPR) = char 'M'
+  ppr (DmdRes BotRes) = char 'X'
+  ppr (DmdFun d r)    = ppr d <> ppr r
+
+topDmdType = DmdRes TopRes
+botDmdType = DmdRes BotRes
+
+mkDmdFun :: [Demand] -> Result -> DmdType
+mkDmdFun ds res = foldr DmdFun (DmdRes res) ds
+
+dmdTypeDepth :: DmdType -> Arity
+dmdTypeDepth (DmdFun _ ty) = 1 + dmdTypeDepth ty
+dmdTypeDepth (DmdRes _)    = 0
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection{Demands}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+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)
+       [Demand]
+  | Err                        -- X
+  | Bot                        -- B
+  deriving( Eq )
+       -- Equality needed for fixpoints in DmdAnal
+
+data Keepity = Keep | Drop
+            deriving( Eq )
+
+topDmd :: Demand       -- The most uninformative demand
+topDmd = Lazy
+
+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 ds) = ppr k <> parens (hcat (map ppr ds))
+
+instance Outputable Keepity where
+  ppr Keep = char 'S'
+  ppr Drop = char 'U'
+\end{code}
+
index b8c38a4..19fb641 100644 (file)
@@ -30,7 +30,8 @@ import IdInfo         ( IdInfo, megaSeqIdInfo,
                          strictnessInfo, ppStrictnessInfo, cgInfo,
                          cprInfo, ppCprInfo, 
                          workerInfo, ppWorkerInfo,
-                          tyGenInfo, ppTyGenInfo
+                          tyGenInfo, ppTyGenInfo,
+                         newDemandInfo, newStrictnessInfo
                        )
 import DataCon         ( dataConTyCon )
 import TyCon           ( tupleTyConBoxity, isTupleTyCon )
@@ -328,7 +329,8 @@ pprIdBndr id = ppr id <+>
               (megaSeqIdInfo (idInfo id) `seq`
                        -- Useful for poking on black holes
                ifPprDebug (ppr (idInlinePragma id) <+> ppr (idOccInfo id) <+> 
-                           ppr (idDemandInfo id)) <+> ppr (idLBVarInfo id))
+                           ppr (idDemandInfo id)) <+> ppr (newDemandInfo (idInfo id)) <+>
+                           ppr (idLBVarInfo id))
 \end{code}
 
 
@@ -345,6 +347,7 @@ ppIdInfo b info
             ppTyGenInfo g,
            ppWorkerInfo (workerInfo info),
            ppStrictnessInfo s,
+           ppr (newStrictnessInfo info),
 --         pprCgInfo c,
             ppCprInfo m,
            pprCoreRules b p
index e4bd50b..e9e8aec 100644 (file)
@@ -1,5 +1,5 @@
 -----------------------------------------------------------------------------
--- $Id: DriverState.hs,v 1.48 2001/07/09 17:44:08 sof Exp $
+-- $Id: DriverState.hs,v 1.49 2001/07/17 15:28:30 simonpj Exp $
 --
 -- Settings for the driver
 --
@@ -248,8 +248,8 @@ buildCoreToDo = do
                -- This gets foldr inlined before strictness analysis
        ]),
 
-       if strictness then CoreDoStrictness else CoreDoNothing,
        if cpr        then CoreDoCPResult   else CoreDoNothing,
+       if strictness then CoreDoStrictness else CoreDoNothing,
        CoreDoWorkerWrapper,
        CoreDoGlomBinds,
 
index 750e2b6..48da14b 100644 (file)
@@ -35,7 +35,7 @@ import Type           ( splitFunTy_maybe, splitForAllTys )
 import Maybes          ( maybeToBool, orElse )
 import Digraph         ( stronglyConnCompR, SCC(..) )
 import PrelNames       ( buildIdKey, foldrIdKey, runSTRepIdKey, augmentIdKey )
-import Unique          ( u2i )
+import Unique          ( Unique )
 import UniqFM          ( keysUFM )  
 import Util            ( zipWithEqual, mapAndUnzip )
 import FastTypes
@@ -230,7 +230,7 @@ Bindings
 \begin{code}
 type IdWithOccInfo = Id                        -- An Id with fresh PragmaInfo attached
 
-type Node details = (details, Int, [Int])      -- The Ints are gotten from the Unique,
+type Node details = (details, Unique, [Unique])        -- The Ints are gotten from the Unique,
                                                -- which is gotten from the Id.
 type Details1    = (Id, UsageDetails, CoreExpr)
 type Details2    = (IdWithOccInfo, CoreExpr)
@@ -310,7 +310,7 @@ occAnalBind env (Rec pairs) body_usage
     ---- stuff for dependency analysis of binds -------------------------------
     edges :: [Node Details1]
     edges = _scc_ "occAnalBind.assoc"
-           [ (details, iBox (u2i (idUnique id)), edges_from rhs_usage)
+           [ (details, idUnique id, edges_from rhs_usage)
            | details@(id, rhs_usage, rhs) <- analysed_pairs
            ]
 
@@ -323,7 +323,7 @@ occAnalBind env (Rec pairs) body_usage
        --               maybeToBool (lookupVarEnv rhs_usage bndr)]
        -- which has n**2 cost, and this meant that edges_from alone 
        -- consumed 10% of total runtime!
-    edges_from :: UsageDetails -> [Int]
+    edges_from :: UsageDetails -> [Unique]
     edges_from rhs_usage = _scc_ "occAnalBind.edges_from"
                           keysUFM rhs_usage
 
index b419461..a886f2b 100644 (file)
@@ -40,6 +40,7 @@ import Specialise     ( specProgram)
 import SpecConstr      ( specConstrProgram)
 import UsageSPInf       ( doUsageSPInf )
 import StrictAnal      ( saBinds )
+import DmdAnal         ( dmdAnalPgm )
 import WorkWrap                ( wwTopBinds )
 import CprAnalyse       ( cprAnalyse )
 
@@ -154,7 +155,8 @@ doCorePass dfs rb us binds (CoreDoFloatOutwards f)
 doCorePass dfs rb us binds CoreDoStaticArgs            
    = _scc_ "StaticArgs"    noStats dfs (doStaticArgs us binds)
 doCorePass dfs rb us binds CoreDoStrictness            
-   = _scc_ "Stranal"       noStats dfs (saBinds dfs binds)
+   = _scc_ "Stranal"       noStats dfs (do { binds1 <- saBinds dfs binds ;
+                                            dmdAnalPgm dfs binds1 })
 doCorePass dfs rb us binds CoreDoWorkerWrapper      
    = _scc_ "WorkWrap"      noStats dfs (wwTopBinds dfs us binds)
 doCorePass dfs rb us binds CoreDoSpecialising       
diff --git a/ghc/compiler/stranal/DmdAnal.lhs b/ghc/compiler/stranal/DmdAnal.lhs
new file mode 100644 (file)
index 0000000..31ebc7b
--- /dev/null
@@ -0,0 +1,624 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1993-1998
+%
+
+                       -----------------
+                       A demand analysis
+                       -----------------
+
+\begin{code}
+module DmdAnal ( dmdAnalPgm ) where
+
+#include "HsVersions.h"
+
+import CmdLineOpts     ( DynFlags, DynFlag(..) )
+import NewDemand       -- All of it
+import CoreSyn
+import DataCon         ( dataConTyCon )
+import TyCon           ( isProductTyCon, isRecursiveTyCon )
+import Id              ( Id, idInfo, idArity, idStrictness, idCprInfo, idDemandInfo,
+                         modifyIdInfo, isDataConId, isImplicitId )
+import IdInfo          ( newStrictnessInfo, setNewStrictnessInfo, mkNewStrictnessInfo,
+                         newDemandInfo, setNewDemandInfo, newDemand
+                       )
+import Var             ( Var )
+import VarEnv
+import UniqFM          ( plusUFM_C, addToUFM_Directly, keysUFM, minusUFM )
+import CoreLint                ( showPass, endPass )
+import ErrUtils                ( dumpIfSet_dyn )
+import Util            ( mapAccumL, mapAccumR, zipWithEqual )
+import BasicTypes      ( Arity )
+import Maybes          ( orElse )
+import Outputable
+import FastTypes
+\end{code}
+
+ToDo:  set a noinline pragma on bottoming Ids
+
+%************************************************************************
+%*                                                                     *
+\subsection{Top level stuff}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+dmdAnalPgm :: DynFlags -> [CoreBind] -> IO [CoreBind]
+#ifndef DEBUG
+
+dmdAnalPgm dflags binds = return binds
+
+#else
+
+dmdAnalPgm dflags binds
+  = do {
+       showPass dflags "Demand analysis" ;
+       let { binds_plus_dmds = do_prog binds ;
+             dmd_changes = get_changes binds_plus_dmds } ;
+       endPass dflags "Demand analysis" 
+               Opt_D_dump_stranal binds_plus_dmds ;
+       printDump (text "Changes in demands" $$ dmd_changes) ;
+       return binds_plus_dmds
+    }
+  where
+    do_prog :: [CoreBind] -> [CoreBind]
+    do_prog binds = snd $ mapAccumL dmdAnalTopBind emptySigEnv binds
+
+dmdAnalTopBind :: SigEnv
+              -> 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
+       (sig, rhs_env, (id', rhs')) = downRhs sigs (id, rhs)
+       sigs'                       = extendSigEnv sigs id sig
+    in
+    (sigs', NonRec id' rhs')    
+
+dmdAnalTopBind sigs (Rec pairs)
+  = let
+       (sigs', _, pairs')  = dmdFix sigs pairs
+    in
+    (sigs', Rec pairs')
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection{The analyser itself}       
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+dmdAnal :: SigEnv -> Demand -> CoreExpr -> (DmdType, DmdEnv, CoreExpr)
+
+dmdAnal sigs Abs  e = (DmdRes TopRes, emptyDmdEnv, e)
+
+dmdAnal sigs Lazy e = let 
+                       (res_ty, dmd_env, e') = dmdAnal sigs Eval e
+                     in
+                     (res_ty, lazify dmd_env, e')
+       -- It's important not to analyse e with a lazy demand because
+       -- a) When we encounter   case s of (a,b) -> 
+       --      we demand s with U(d1d2)... but if the overall demand is lazy
+       --      that is wrong, and we'd need to reduce the demand on s (inconvenient)
+       -- b) More important, consider
+       --      f (let x = R in x+x), where f is lazy
+       --    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
+
+
+dmdAnal sigs dmd (Var var)
+  = (res_ty, 
+     blackHoleEnv res_ty (unitDmdEnv var dmd), 
+     Var var)
+  where
+    res_ty = dmdTransform sigs var dmd
+
+dmdAnal sigs dmd (Lit lit)
+  = (topDmdType, emptyDmdEnv, Lit lit)
+
+dmdAnal sigs dmd (Note n e)
+  = (dmd_ty, dmd_env, Note n e')
+  where
+    (dmd_ty, dmd_env, e') = dmdAnal sigs dmd e 
+
+dmdAnal sigs dmd (App fun (Type ty))
+  = (fun_ty, fun_env, App fun' (Type ty))
+  where
+    (fun_ty, fun_env, fun') = dmdAnal sigs dmd fun
+
+dmdAnal sigs dmd (App fun arg) -- Non-type arguments
+  = let                                -- [Type arg handled above]
+       (fun_ty, fun_env, fun') = dmdAnal sigs (Call dmd) fun
+       (arg_ty, arg_env, arg') = dmdAnal sigs arg_dmd arg
+       (arg_dmd, res_ty)       = splitDmdTy fun_ty
+    in
+    (res_ty, 
+     blackHoleEnv res_ty (fun_env `bothEnv` arg_env), 
+     App fun' arg')
+
+dmdAnal sigs dmd (Lam var body)
+  | isTyVar var
+  = let   
+       (body_ty, body_env, body') = dmdAnal sigs dmd body
+    in
+    (body_ty, body_env, Lam var body')
+
+  | otherwise
+  = let
+       body_dmd = case dmd of
+                       Call dmd -> dmd
+                       other    -> Lazy        -- Conservative
+
+       (body_ty, body_env, body') = dmdAnal sigs body_dmd body
+       (lam_env, var')            = annotateBndr body_env var
+    in
+    (DmdFun (idNewDemandInfo var') body_ty,
+     body_env `delDmdEnv` var,
+     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_env, alt') = dmdAnalAlt sigs dmd alt
+       (_, scrut_env, scrut')  = dmdAnal sigs scrut_dmd scrut
+       (alt_env2, case_bndr')  = annotateBndr alt_env case_bndr
+       (_, bndrs', _)          = alt'
+        scrut_dmd              = Seq Drop [idNewDemandInfo b | b <- bndrs', isId b]
+    in
+    (alt_ty,
+     alt_env2 `bothEnv` scrut_env,
+     Case scrut' case_bndr' [alt'])
+
+dmdAnal sigs dmd (Case scrut case_bndr alts)
+  = let
+       (alt_tys, alt_envs, alts')    = unzip3 (map (dmdAnalAlt sigs dmd) alts)
+       (scrut_ty, scrut_env, scrut') = dmdAnal sigs Eval scrut
+       (alt_env2, case_bndr')        = annotateBndr (foldr1 lubEnv alt_envs) case_bndr
+    in
+    (foldr1 lubDmdTy alt_tys,
+     alt_env2 `bothEnv` scrut_env,
+     Case scrut' case_bndr' alts')
+
+dmdAnal sigs dmd (Let (NonRec id rhs) body) 
+  | idArity id == 0    -- A thunk; analyse the body first, then the thunk
+  = let
+       (body_ty, body_env, body') = dmdAnal sigs dmd body
+       (rhs_ty, rhs_env, rhs')    = dmdAnal sigs (lookupDmd body_env id) rhs
+       (body_env1, id1)           = annotateBndr body_env id
+    in
+    (body_ty, body_env1 `bothEnv` rhs_env, Let (NonRec id1 rhs') body')    
+
+  | otherwise  -- A function; analyse the function first, then the body
+  = let
+       (sig, rhs_env, (id1, rhs')) = downRhs sigs (id, rhs)
+       sigs'                       = extendSigEnv sigs id sig
+       (body_ty, body_env, body')  = dmdAnal sigs' dmd body
+       rhs_env1                    = weaken body_env id rhs_env
+       (body_env1, id2)            = annotateBndr body_env id1
+    in
+    (body_ty, body_env1 `bothEnv` rhs_env1, Let (NonRec id2 rhs') body')    
+
+dmdAnal sigs dmd (Let (Rec pairs) body) 
+  = let
+       bndrs                      = map fst pairs
+       (sigs', rhs_envs, pairs')  = dmdFix sigs pairs
+       (body_ty, body_env, body') = dmdAnal sigs' dmd body
+
+       weakened_rhs_envs = zipWithEqual "dmdAnal:Let" (weaken body_env) bndrs rhs_envs
+               -- 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.
+
+       rhs_env1 = foldr1 bothEnv weakened_rhs_envs
+
+       result_env = delDmdEnvList (body_env `bothEnv` rhs_env1) 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
+    (body_ty, result_env, Let (Rec pairs') body')
+\end{code}
+
+\begin{code}
+dmdAnalAlt sigs dmd (con,bndrs,rhs) 
+  = let 
+       (rhs_ty, rhs_env, rhs') = dmdAnal sigs dmd rhs
+       (alt_env, bndrs')       = annotateBndrs rhs_env bndrs
+    in
+    (rhs_ty, alt_env, (con, bndrs', rhs'))
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection{Bindings}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+dmdFix :: SigEnv               -- Does not include bindings for this binding
+       -> [(Id,CoreExpr)]
+       -> (SigEnv,
+          [DmdEnv],            -- Demands from RHSs
+          [(Id,CoreExpr)])     -- Binders annotated with stricness info
+
+dmdFix sigs pairs
+  = loop (map initial_sig pairs) pairs
+  where
+    loop id_sigs pairs
+      | id_sigs == id_sigs' = (sigs', rhs_envs, pairs')
+      | otherwise          = loop id_sigs' pairs'
+      where
+       extra_sigs = [(id,sig) | ((id,_),sig) <- pairs `zip` id_sigs]
+       sigs'      = extendSigEnvList sigs extra_sigs
+       (id_sigs', rhs_envs, pairs') = unzip3 (map (downRhs sigs') pairs) 
+          
+       -- Get an initial strictness signature from the Id
+       -- itself.  That way we make use of earlier iterations
+       -- of the fixpoint algorithm.  (Cunning plan.)
+    initial_sig (id,_) = idNewStrictness_maybe id `orElse` botSig
+
+
+downRhs :: SigEnv -> (Id, CoreExpr)
+       -> (StrictSig, DmdEnv, (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.
+
+downRhs sigs (id, rhs)
+ = (sig, rhs_env, (id', rhs'))
+ where
+  arity                          = idArity id
+  (rhs_ty, rhs_env, rhs') = dmdAnal sigs (vanillaCall arity) rhs
+  sig                    = mkStrictSig arity rhs_ty
+  id'                    = id `setIdNewStrictness` sig
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection{Strictness signatures and types}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+data DmdEnv
+  = DmdEnv (VarEnv Demand)     -- All the explicitly mentioned variables
+          Bool                 -- True  <=> all the others are Bot
+                               -- False <=> all the others are Abs
+
+emptyDmdEnv       = DmdEnv emptyVarEnv          False
+unitDmdEnv var dmd = DmdEnv (unitVarEnv var dmd) False
+
+lookupDmd :: DmdEnv -> Var -> Demand
+lookupDmd (DmdEnv env bh) var = lookupVarEnv env var `orElse` deflt
+                             where
+                               deflt | bh        = Bot
+                                     | otherwise = Abs
+
+delDmdEnv :: DmdEnv -> Var -> DmdEnv
+delDmdEnv (DmdEnv env b) var = DmdEnv (env `delVarEnv` var) b
+
+delDmdEnvList :: DmdEnv -> [Var] -> DmdEnv
+delDmdEnvList (DmdEnv env b) vars = DmdEnv (env `delVarEnvList` vars) b
+
+
+blackHoleEnv :: DmdType -> DmdEnv -> DmdEnv
+blackHoleEnv (DmdRes BotRes) (DmdEnv env _) = DmdEnv env True
+blackHoleEnv other          env            = env
+
+bothEnv (DmdEnv env1 b1) (DmdEnv env2 b2)
+  = DmdEnv both_env2 (b1 || b2)
+  where
+    both_env  = plusUFM_C both env1 env2
+    both_env1 = modifyEnv b1 Bot env2 env1 both_env
+    both_env2 = modifyEnv b2 Bot env1 env2 both_env1
+
+lubEnv (DmdEnv env1 b1) (DmdEnv env2 b2)
+  = DmdEnv lub_env2 (b1 && b2)
+  where
+    lub_env  = plusUFM_C lub env1 env2
+    lub_env1 = modifyEnv (not b1) Lazy env2 env1 lub_env
+    lub_env2 = modifyEnv (not b2) Lazy env1 env2 lub_env1
+
+modifyEnv :: Bool                              -- No-op if False
+         -> Demand                             -- The zap value
+         -> VarEnv Demand -> VarEnv Demand     -- Env1 and Env2
+         -> VarEnv Demand -> VarEnv Demand     -- Transform this env
+       -- Zap anything in Env1 but not in Env2
+       -- Assume: dom(env) includes dom(Env1) and dom(Env2)
+
+modifyEnv need_to_modify zap_value env1 env2 env
+  | need_to_modify = foldr zap env (keysUFM (env1 `minusUFM` env2))
+  | otherwise     = env
+  where
+    zap uniq env = addToUFM_Directly env uniq zap_value
+
+annotateBndr :: DmdEnv -> Var -> (DmdEnv, Var)
+-- The returned env has the var deleted
+-- The returned var is annotated with demand info
+annotateBndr dmd_env var
+  | isTyVar var = (dmd_env,                var)
+  | otherwise   = (dmd_env `delDmdEnv` var, setIdNewDemandInfo var (lookupDmd dmd_env var))
+
+annotateBndrs = mapAccumR annotateBndr
+
+weaken :: DmdEnv       -- How the Id is used in its scope
+       -> Id
+       -> DmdEnv       -- The RHS env for the Id, assuming a vanilla call demand
+       -> DmdEnv       -- The RHS env given the actual demand
+-- Consider    let f = \x -> R in B
+-- The vanilla call demand is C(V), and that's what we use to 
+-- compute f's strictness signature.  If the *actual* demand on
+-- f from B is less than this, we must weaken, or lazify, the 
+-- demands in R to match this
+
+weaken body_env id rhs_env
+  | depth >= idArity id                -- Enough demand
+  = rhs_env
+  | otherwise                  -- Not enough demand
+  = lazify rhs_env 
+  where
+    (depth,_) = splitCallDmd (lookupDmd body_env id)
+
+lazify (DmdEnv env _) = DmdEnv (mapVarEnv (\_ -> Lazy) env) False
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection{Demand types}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+splitDmdTy :: DmdType -> (Demand, DmdType)
+-- Split off one function argument
+splitDmdTy (DmdFun dmd res_ty) = (dmd, res_ty)
+splitDmdTy (DmdRes TopRes)     = (topDmd, topDmdType)
+splitDmdTy (DmdRes BotRes)     = (Abs, DmdRes BotRes)
+       -- We already have a suitable demand on all
+       -- free vars, so no need to add more!
+splitDmdTy (DmdRes RetCPR)     = panic "splitDmdTy"
+
+-------------------------
+dmdTypeRes :: DmdType -> Result
+dmdTypeRes (DmdFun dmd res_ty) = dmdTypeRes res_ty
+dmdTypeRes (DmdRes res)               = res
+
+-------------------------
+lubDmdTy :: DmdType -> DmdType -> DmdType
+lubDmdTy (DmdFun d1 t1) (DmdFun d2 t2) = DmdFun (d1 `lub` d2) (t1 `lubDmdTy` t2)
+lubDmdTy (DmdRes r1)    (DmdRes r2)    = DmdRes (r1 `lubRes` r2)
+lubDmdTy t1            t2             = topDmdType
+
+-------------------------
+lubRes BotRes r      = r
+lubRes r      BotRes = r
+lubRes RetCPR RetCPR = RetCPR
+lubRes r1     r2     = TopRes
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection{Strictness signatures}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+type SigEnv  = VarEnv StrictSig
+emptySigEnv  = emptyVarEnv
+extendSigEnv = extendVarEnv
+extendSigEnvList = extendVarEnvList
+lookupSig sigs v = case lookupVarEnv sigs v of
+                       Just sig -> Just sig
+                       Nothing  -> idNewStrictness_maybe v
+
+dmdTransform :: SigEnv         -- The strictness environment
+            -> Id              -- The function
+            -> Demand          -- The demand on the function
+            -> DmdType         -- The demand type of the function in this context
+
+dmdTransform sigs var dmd
+  | isDataConId var,           -- Data constructor
+    Seq k ds <- res_dmd,       -- and the demand looks inside its fields
+    StrictSig arity dmd_ty <- idNewStrictness var,     -- It must have a strictness sig
+    length ds == arity         -- It's saturated
+  = mkDmdFun ds (dmdTypeRes dmd_ty)
+       -- Need to extract whether it's a product
+
+
+  | Just (StrictSig arity dmd_ty) <- lookupSig sigs var,
+    arity <= depth             -- Saturated function;
+  = dmd_ty                     -- Unleash the demand!
+
+  | otherwise                  -- Default case
+  = topDmdType
+
+  where
+    (depth, res_dmd) = splitCallDmd dmd
+
+betterStrict :: StrictSig -> StrictSig -> Bool
+betterStrict (StrictSig ar1 t1) (StrictSig ar2 t2)
+  = (ar1 >= ar2) && (t1 `betterDmdType` t2)
+
+betterDmdType t1 t2 = (t1 `lubDmdTy` t2) == t2
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection{Demands}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+splitCallDmd :: Demand -> (Int, Demand)
+splitCallDmd (Call d) = case splitCallDmd d of
+                         (n, r) -> (n+1, r)
+splitCallDmd d       = (0, d)
+
+vanillaCall :: Arity -> Demand
+vanillaCall 0 = Eval
+vanillaCall n = Call (vanillaCall (n-1))
+
+-----------------------------------
+lub :: Demand -> Demand -> Demand
+
+lub Bot  d = d
+
+lub Lazy d = Lazy
+
+lub Err Bot = Err 
+lub Err d   = d 
+
+lub Abs Bot = Abs
+lub Abs Err = Abs
+lub Abs Abs = Abs
+lub Abs d   = d
+
+lub Eval Abs       = Lazy
+lub Eval Lazy      = Lazy
+lub Eval (Seq k ds) = Seq Keep ds
+lub Eval d         = Eval
+
+lub (Call d1) (Call d2) = Call (lub d1 d2)
+
+lub (Seq k1 ds1) (Seq k2 ds2) = Seq (k1 `vee` k2) 
+                                   (zipWithEqual "lub" lub 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
+
+-----------------------------------
+vee :: Keepity -> Keepity -> Keepity
+vee Drop Drop = Drop
+vee k1   k2   = Keep
+
+-----------------------------------
+both :: Demand -> Demand -> Demand
+
+both Bot d = Bot
+
+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 ds) = Seq Keep ds
+both Lazy d         = d
+
+both Eval Bot       = Bot
+both Eval (Seq k ds) = Seq Keep ds
+both Eval (Call d)   = Call d
+both Eval d         = Eval
+
+both (Seq k1 ds1) (Seq k2 ds2) = Seq (k1 `vee` k2)
+                                    (zipWithEqual "both" both ds1 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
+
+betterDemand :: Demand -> Demand -> Bool
+-- If d1 `better` d2, and d2 `better` d2, then d1==d2
+betterDemand d1 d2 = (d1 `lub` d2) == d2
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection{Miscellaneous
+%*                                                                     *
+%************************************************************************
+
+
+\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
+
+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_var var
+  | isId var  = get_changes_str var $$ get_changes_dmd var
+  | otherwise = empty
+
+get_changes_expr (Type t)     = empty
+get_changes_expr (Var v)      = empty
+get_changes_expr (Lit l)      = empty
+get_changes_expr (Note n e)   = get_changes_expr e
+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_alt (con,bs,rhs) = vcat (map get_changes_var bs) $$ get_changes_expr rhs
+
+get_changes_str id
+  | new_better && old_better = empty
+  | new_better              = message "BETTER"
+  | old_better              = message "WORSE"
+  | otherwise               = message "INCOMPARABLE" 
+  where
+    message word = text word <+> text "strictness for" <+> ppr id <+> info
+    info = (text "Old" <+> ppr old) $$ (text "New" <+> ppr new)
+    new = idNewStrictness id
+    old = mkNewStrictnessInfo (idArity id) (idStrictness id) (idCprInfo id)
+    old_better = old `betterStrict` new
+    new_better = new `betterStrict` old
+
+get_changes_dmd id
+  | new_better && old_better = empty
+  | new_better              = message "BETTER"
+  | old_better              = message "WORSE"
+  | otherwise               = message "INCOMPARABLE" 
+  where
+    message word = text word <+> text "demand for" <+> ppr id <+> info
+    info = (text "Old" <+> ppr old) $$ (text "New" <+> ppr new)
+    new = idNewDemandInfo id
+    old = newDemand (idDemandInfo id)
+    new_better = new `betterDemand` old 
+    old_better = old `betterDemand` new
+#endif         /* DEBUG */
+\end{code}
+
index 124d6be..7e46e47 100644 (file)
@@ -117,7 +117,7 @@ lookupWithDefaultUFM
 lookupWithDefaultUFM_Directly
                :: UniqFM elt -> elt -> Unique -> elt
 
-keysUFM                :: UniqFM elt -> [Int]          -- Get the keys
+keysUFM                :: UniqFM elt -> [Unique]       -- Get the keys
 eltsUFM                :: UniqFM elt -> [elt]
 ufmToList      :: UniqFM elt -> [(Unique, elt)]
 \end{code}
@@ -579,7 +579,7 @@ eltsUFM fm = foldUFM (:) [] fm
 
 ufmToList fm = fold_tree (\ iu elt rest -> (mkUniqueGrimily iu, elt) : rest) [] fm
 
-keysUFM fm = fold_tree (\ iu elt rest -> iBox iu : rest) [] fm
+keysUFM fm = fold_tree (\ iu elt rest -> mkUniqueGrimily iu : rest) [] fm
 
 fold_tree f a (NodeUFM _ _ t1 t2) = fold_tree f (fold_tree f a t2) t1
 fold_tree f a (LeafUFM iu obj)    = f iu obj a