[project @ 2002-11-08 15:16:50 by simonpj]
[ghc-hetmet.git] / ghc / compiler / stranal / DmdAnal.lhs
index a55be50..063a288 100644 (file)
@@ -23,12 +23,12 @@ import TyCon                ( isProductTyCon, isRecursiveTyCon )
 import Id              ( Id, idType, idInlinePragma,
                          isDataConId, isGlobalId, idArity,
 #ifdef OLD_STRICTNESS
-                         idDemandInfo,  idStrictness, idCprInfo,
+                         idDemandInfo,  idStrictness, idCprInfo, idName,
 #endif
                          idNewStrictness, idNewStrictness_maybe,
                          setIdNewStrictness, idNewDemandInfo,
                          idNewDemandInfo_maybe,
-                         setIdNewDemandInfo, idName 
+                         setIdNewDemandInfo
                        )
 #ifdef OLD_STRICTNESS
 import IdInfo          ( newStrictnessFromOld, newDemand )
@@ -40,7 +40,8 @@ import UniqFM         ( plusUFM_C, addToUFM_Directly, lookupUFM_Directly,
 import Type            ( isUnLiftedType )
 import CoreLint                ( showPass, endPass )
 import Util            ( mapAndUnzip, mapAccumL, mapAccumR, lengthIs )
-import BasicTypes      ( Arity, TopLevelFlag(..), isTopLevel, isNeverActive )
+import BasicTypes      ( Arity, TopLevelFlag(..), isTopLevel, isNeverActive,
+                         RecFlag(..), isRec )
 import Maybes          ( orElse, expectJust )
 import Outputable
 \end{code}
@@ -89,8 +90,8 @@ dmdAnalTopBind :: SigEnv
               -> (SigEnv, CoreBind)
 dmdAnalTopBind sigs (NonRec id rhs)
   = let
-       (    _, _, (_,   rhs1)) = dmdAnalRhs TopLevel sigs (id, rhs)
-       (sigs2, _, (id2, rhs2)) = dmdAnalRhs TopLevel sigs (id, rhs1)
+       (    _, _, (_,   rhs1)) = dmdAnalRhs TopLevel NonRecursive sigs (id, rhs)
+       (sigs2, _, (id2, rhs2)) = dmdAnalRhs TopLevel NonRecursive sigs (id, rhs1)
                -- Do two passes to improve CPR information
                -- See comments with ignore_cpr_info in mk_sig_ty
                -- and with extendSigsWithLam
@@ -264,23 +265,23 @@ dmdAnal sigs dmd (Case scrut case_bndr alts)
 
 dmdAnal sigs dmd (Let (NonRec id rhs) body) 
   = let
-       (sigs', lazy_fv, (id1, rhs')) = dmdAnalRhs NotTopLevel sigs (id, rhs)
+       (sigs', lazy_fv, (id1, rhs')) = dmdAnalRhs NotTopLevel NonRecursive sigs (id, rhs)
        (body_ty, body')              = dmdAnal sigs' dmd body
        (body_ty1, id2)               = annotateBndr body_ty id1
        body_ty2                      = addLazyFVs body_ty1 lazy_fv
     in
-#ifdef DEBUG
-       -- If the actual demand is better than the vanilla
-       -- demand, we might do better to re-analyse with the
-       -- stronger demand.
-    (let vanilla_dmd = vanillaCall (idArity id)
-        actual_dmd  = idNewDemandInfo id2
-     in
-     if actual_dmd `betterDemand` vanilla_dmd && actual_dmd /= vanilla_dmd then
-       pprTrace "dmdLet: better demand" (ppr id <+> vcat [text "vanilla" <+> ppr vanilla_dmd,
-                                                          text "actual" <+> ppr actual_dmd])
-     else \x -> x)
-#endif
+       -- If the actual demand is better than the vanilla call
+       -- demand, you might think that we might do better to re-analyse 
+       -- the RHS with the stronger demand.
+       -- But (a) That seldom happens, because it means that *every* path in 
+       --         the body of the let has to use that stronger demand
+       -- (b) It often happens temporarily in when fixpointing, because
+       --     the recursive function at first seems to place a massive demand.
+       --     But we don't want to go to extra work when the function will
+       --     probably iterate to something less demanding.  
+       -- In practice, all the times the actual demand on id2 is more than
+       -- the vanilla call demand seem to be due to (b).  So we don't
+       -- bother to re-analyse the RHS.
     (body_ty2, Let (NonRec id2 rhs') body')    
 
 dmdAnal sigs dmd (Let (Rec pairs) body) 
@@ -333,19 +334,27 @@ dmdFix top_lvl sigs orig_pairs
         -> [(Id,CoreExpr)]             
         -> (SigEnv, DmdEnv, [(Id,CoreExpr)])
     loop n sigs pairs
-      | all (same_sig sigs sigs') bndrs 
+      | found_fixpoint
       = (sigs', lazy_fv, pairs')
                -- Note: use pairs', not pairs.   pairs' is the result of 
                -- processing the RHSs with sigs (= sigs'), whereas pairs 
                -- is the result of processing the RHSs with the *previous* 
                -- iteration of sigs.
-      | n >= 10       = pprTrace "dmdFix loop" (ppr n <+> (vcat 
+
+      | n >= 10  = pprTrace "dmdFix loop" (ppr n <+> (vcat 
                                [ text "Sigs:" <+> ppr [(id,lookup sigs id, lookup sigs' id) | (id,_) <- pairs],
                                  text "env:" <+> ppr (ufmToList sigs),
                                  text "binds:" <+> pprCoreBinding (Rec pairs)]))
-                             (emptySigEnv, emptyDmdEnv, orig_pairs)    -- Safe output
+                             (emptySigEnv, lazy_fv, orig_pairs)        -- Safe output
+                       -- The lazy_fv part is really important!  orig_pairs has no strictness
+                       -- info, including nothing about free vars.  But if we have
+                       --      letrec f = ....y..... in ...f...
+                       -- where 'y' is free in f, we must record that y is mentioned, 
+                       -- otherwise y will get recorded as absent altogether
+
       | otherwise    = loop (n+1) sigs' pairs'
       where
+       found_fixpoint = all (same_sig sigs sigs') bndrs 
                -- Use the new signature to do the next pair
                -- The occurrence analyser has arranged them in a good order
                -- so this can significantly reduce the number of iterations needed
@@ -358,7 +367,7 @@ dmdFix top_lvl sigs orig_pairs
          ((sigs', lazy_fv'), pair')
          --     )
        where
-         (sigs', lazy_fv1, pair') = dmdAnalRhs top_lvl sigs (id,rhs)
+         (sigs', lazy_fv1, pair') = dmdAnalRhs top_lvl Recursive sigs (id,rhs)
          lazy_fv'                 = plusUFM_C both lazy_fv lazy_fv1   
          -- old_sig               = lookup sigs id
          -- new_sig               = lookup sigs' id
@@ -374,20 +383,20 @@ dmdFix top_lvl sigs orig_pairs
        -- since it is part of the strictness signature
 initialSig id = idNewStrictness_maybe id `orElse` botSig
 
-dmdAnalRhs :: TopLevelFlag 
+dmdAnalRhs :: TopLevelFlag -> RecFlag
        -> SigEnv -> (Id, CoreExpr)
        -> (SigEnv,  DmdEnv, (Id, CoreExpr))
 -- Process the RHS of the binding, add the strictness signature
 -- to the Id, and augment the environment with the signature as well.
 
-dmdAnalRhs top_lvl sigs (id, rhs)
+dmdAnalRhs top_lvl rec_flag sigs (id, rhs)
  = (sigs', lazy_fv, (id', rhs'))
  where
   arity                     = idArity id   -- The idArity should be up to date
                                    -- The simplifier was run just beforehand
   (rhs_dmd_ty, rhs') = dmdAnal sigs (vanillaCall arity) rhs
   (lazy_fv, sig_ty)  = WARN( arity /= dmdTypeDepth rhs_dmd_ty, ppr id )
-                      mkSigTy id rhs rhs_dmd_ty
+                      mkSigTy top_lvl rec_flag id rhs rhs_dmd_ty
   id'               = id `setIdNewStrictness` sig_ty
   sigs'                     = extendSigEnv top_lvl sigs id sig_ty
 \end{code}
@@ -404,17 +413,24 @@ mkTopSigTy :: CoreExpr -> DmdType -> StrictSig
        -- NB: not used for never-inline things; hence False
 mkTopSigTy rhs dmd_ty = snd (mk_sig_ty False False rhs dmd_ty)
 
-mkSigTy :: Id -> CoreExpr -> DmdType -> (DmdEnv, StrictSig)
-mkSigTy id rhs dmd_ty = mk_sig_ty (isNeverActive (idInlinePragma id))
-                                 ok_to_keep_cpr_info
-                                 rhs dmd_ty
+mkSigTy :: TopLevelFlag -> RecFlag -> Id -> CoreExpr -> DmdType -> (DmdEnv, StrictSig)
+mkSigTy top_lvl rec_flag id rhs dmd_ty 
+  = mk_sig_ty never_inline thunk_cpr_ok rhs dmd_ty
   where
-    ok_to_keep_cpr_info = case idNewDemandInfo_maybe id of
-                           Nothing  -> True    -- Is the case the first time round
-                           Just dmd -> isStrictDmd dmd
+    never_inline = isNeverActive (idInlinePragma id)
+    maybe_id_dmd = idNewDemandInfo_maybe id
+       -- Is Nothing the first time round
+
+    thunk_cpr_ok
+       | isTopLevel top_lvl       = False      -- Top level things don't get
+                                               -- their demandInfo set at all
+       | isRec rec_flag           = False      -- Ditto recursive things
+       | Just dmd <- maybe_id_dmd = isStrictDmd dmd
+       | otherwise                = True       -- Optimistic, first time round
+                                               -- See notes below
 \end{code}
 
-The ok_to_keep_cpr_info stuff [CPR-AND-STRICTNESS]
+The thunk_cpr_ok stuff [CPR-AND-STRICTNESS]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 If the rhs is a thunk, we usually forget the CPR info, because
 it is presumably shared (else it would have been inlined, and 
@@ -455,7 +471,7 @@ have a CPR in it or not.  Simple solution:
 
 NB: strictly_demanded is never true of a top-level Id, or of a recursive Id.
 
-The Nothing case in ok_to_keep_cpr_info [CPR-AND-STRICTNESS]
+The Nothing case in thunk_cpr_ok [CPR-AND-STRICTNESS]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Demand info now has a 'Nothing' state, just like strictness info.
 The analysis works from 'dangerous' towards a 'safe' state; so we 
@@ -473,7 +489,7 @@ In the first iteration we'd have no demand info for x, so assume
 not-demanded; then we'd get TopRes for f's CPR info.  Next iteration
 we'd see that t was demanded, and so give it the CPR property, but
 by now f has TopRes, so it will stay TopRes.  
-
+ever_in
 Instead, with the Nothing setting the first time round, we say
 'yes t is demanded' the first time.  
 
@@ -484,7 +500,7 @@ by dmdAnalTopBind.
 
 
 \begin{code}
-mk_sig_ty never_inline ok_to_keep_cpr_info rhs (DmdType fv dmds res) 
+mk_sig_ty never_inline thunk_cpr_ok rhs (DmdType fv dmds res) 
   | never_inline && not (isBotRes res)
        --                      HACK ALERT
        -- Don't strictness-analyse NOINLINE things.  Why not?  Because
@@ -555,7 +571,7 @@ mk_sig_ty never_inline ok_to_keep_cpr_info rhs (DmdType fv dmds res)
     res' = case res of
                RetCPR | ignore_cpr_info -> TopRes
                other                    -> res
-    ignore_cpr_info = not (exprIsValue rhs || ok_to_keep_cpr_info)
+    ignore_cpr_info = not (exprIsValue rhs || thunk_cpr_ok)
 \end{code}
 
 The unpack strategy determines whether we'll *really* unpack the argument,
@@ -713,21 +729,23 @@ extendSigEnvList = extendVarEnvList
 
 extendSigsWithLam :: SigEnv -> Id -> SigEnv
 -- Extend the SigEnv when we meet a lambda binder
--- If the binder is marked demanded with a product demand, 
--- then give it a CPR signature, because in the likely event
--- that this is a lambda on a fn defn [we only use this when
--- the lambda is being consumed with a call demand],
--- it'll be w/w'd and so it will be CPR-ish
--- NOTE: see notes [CPR-AND-STRICTNESS]
+--  If the binder is marked demanded with a product demand, then give it a CPR 
+-- signature, because in the likely event that this is a lambda on a fn defn 
+-- [we only use this when the lambda is being consumed with a call demand],
+-- it'll be w/w'd and so it will be CPR-ish.
+--
+--     NOTE: see notes [CPR-AND-STRICTNESS]
+--
+-- Also note that we only want to do this for something that
+-- definitely has product type, else we may get over-optimistic 
+-- CPR results (e.g. from \x -> x!).
+
 extendSigsWithLam sigs id
   = case idNewDemandInfo_maybe id of
-       Nothing        -> pprTrace "Yes (bot)" (ppr id) $ extendVarEnv sigs id (cprSig, NotTopLevel)
-       Just (Eval ds) -> pprTrace "Yes" (ppr id) $ extendVarEnv sigs id (cprSig, NotTopLevel)
-       other          -> pprTrace "No" (ppr id $$ ppr (idNewDemandInfo id)) $ sigs
+       Nothing               -> extendVarEnv sigs id (cprSig, NotTopLevel)
+       Just (Eval (Prod ds)) -> extendVarEnv sigs id (cprSig, NotTopLevel)
+       other                 -> sigs
 
-cprSig :: StrictSig
-cprSig = StrictSig (mkDmdType emptyVarEnv [] RetCPR)
-       
 
 dmdTransform :: SigEnv         -- The strictness environment
             -> Id              -- The function
@@ -835,7 +853,7 @@ argDemand (Defer d) = lazyDmd
 argDemand (Eval ds) = Eval (mapDmds argDemand ds)
 argDemand (Box Bot) = evalDmd
 argDemand (Box d)   = box (argDemand d)
-argDemand Bot      = Abs       -- Don't pass args that are consumed by bottom/err
+argDemand Bot      = Abs       -- Don't pass args that are consumed (only) by bottom
 argDemand d        = d
 \end{code}