[project @ 2003-07-16 13:33:55 by simonmar]
[ghc-hetmet.git] / ghc / compiler / stranal / DmdAnal.lhs
index a36ebbc..b27a30e 100644 (file)
@@ -21,23 +21,25 @@ import CoreUtils    ( exprIsValue, exprArity )
 import DataCon         ( dataConTyCon )
 import TyCon           ( isProductTyCon, isRecursiveTyCon )
 import Id              ( Id, idType, idInlinePragma,
-                         isDataConId, isGlobalId, idArity,
+                         isDataConWorkId, 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 )
 #endif
 import Var             ( Var )
 import VarEnv
+import TysWiredIn      ( unboxedPairDataCon )
+import TysPrim         ( realWorldStatePrimTy )
 import UniqFM          ( plusUFM_C, addToUFM_Directly, lookupUFM_Directly,
                          keysUFM, minusUFM, ufmToList, filterUFM )
-import Type            ( isUnLiftedType )
+import Type            ( isUnLiftedType, eqType )
 import CoreLint                ( showPass, endPass )
 import Util            ( mapAndUnzip, mapAccumL, mapAccumR, lengthIs )
 import BasicTypes      ( Arity, TopLevelFlag(..), isTopLevel, isNeverActive,
@@ -270,18 +272,18 @@ dmdAnal sigs dmd (Let (NonRec id rhs) 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) 
@@ -306,8 +308,30 @@ dmdAnalAlt sigs dmd (con,bndrs,rhs)
   = let 
        (rhs_ty, rhs')   = dmdAnal sigs dmd rhs
        (alt_ty, bndrs') = annotateBndrs rhs_ty bndrs
-    in
-    (alt_ty, (con, bndrs', rhs'))
+       final_alt_ty | io_hack_reqd = alt_ty `lubType` topDmdType
+                    | otherwise    = alt_ty
+
+       -- There's a hack here for I/O operations.  Consider
+       --      case foo x s of { (# s, r #) -> y }
+       -- Is this strict in 'y'.  Normally yes, but what if 'foo' is an I/O
+       -- operation that simply terminates the program (not in an erroneous way)?
+       -- In that case we should not evaluate y before the call to 'foo'.
+       -- Hackish solution: spot the IO-like situation and add a virtual branch,
+       -- as if we had
+       --      case foo x s of 
+       --         (# s, r #) -> y 
+       --         other      -> return ()
+       -- So the 'y' isn't necessarily going to be evaluated
+       --
+       -- A more complete example where this shows up is:
+       --      do { let len = <expensive> ;
+       --         ; when (...) (exitWith ExitSuccess)
+       --         ; print len }
+
+       io_hack_reqd = con == DataAlt unboxedPairDataCon &&
+                      idType (head bndrs) `eqType` realWorldStatePrimTy
+    in 
+    (final_alt_ty, (con, bndrs', rhs'))
 \end{code}
 
 %************************************************************************
@@ -334,19 +358,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
@@ -479,11 +511,10 @@ We can't start with 'not-demanded' because then consider
 
 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.  
+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.  Instead, with the Nothing
+setting the first time round, we say 'yes t is demanded' the first
+time.
 
 However, this does mean that for non-recursive bindings we must
 iterate twice to be sure of not getting over-optimistic CPR info,
@@ -721,10 +752,15 @@ 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 
+-- 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.
+-- it'll be w/w'd and so it will be CPR-ish.  E.g.
+--     f = \x::(Int,Int).  if ...strict in x... then
+--                             x
+--                         else
+--                             (a,b)
+-- We want f to have the CPR property because x does, by the time f has been w/w'd
 --
 --     NOTE: see notes [CPR-AND-STRICTNESS]
 --
@@ -738,9 +774,6 @@ extendSigsWithLam sigs id
        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
@@ -752,7 +785,7 @@ dmdTransform :: SigEnv              -- The strictness environment
 dmdTransform sigs var dmd
 
 ------         DATA CONSTRUCTOR
-  | isDataConId var            -- Data constructor
+  | isDataConWorkId var                -- Data constructor
   = let 
        StrictSig dmd_ty    = idNewStrictness var       -- It must have a strictness sig
        DmdType _ _ con_res = dmd_ty
@@ -848,7 +881,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}