Fix long-standing bug in CPR analysis
[ghc-hetmet.git] / ghc / compiler / stranal / DmdAnal.lhs
index 527004a..c5cfb7b 100644 (file)
@@ -13,31 +13,34 @@ module DmdAnal ( dmdAnalPgm, dmdAnalTopRhs,
 
 #include "HsVersions.h"
 
-import CmdLineOpts     ( DynFlags, DynFlag(..), opt_MaxWorkerArgs )
+import DynFlags                ( DynFlags, DynFlag(..) )
+import StaticFlags     ( opt_MaxWorkerArgs )
 import NewDemand       -- All of it
 import CoreSyn
 import PprCore 
-import CoreUtils       ( exprIsValue, exprArity )
+import CoreUtils       ( exprIsHNF, exprIsTrivial, 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, coreEqType )
 import CoreLint                ( showPass, endPass )
 import Util            ( mapAndUnzip, mapAccumL, mapAccumR, lengthIs )
 import BasicTypes      ( Arity, TopLevelFlag(..), isTopLevel, isNeverActive,
@@ -53,10 +56,6 @@ To think about
 * Consider f x = x+1 `fatbar` error (show x)
   We'd like to unbox x, even if that means reboxing it in the error case.
 
-\begin{code}
-instance Outputable TopLevelFlag where
-  ppr flag = empty
-\end{code}
 
 %************************************************************************
 %*                                                                     *
@@ -113,11 +112,18 @@ dmdAnalTopRhs :: CoreExpr -> (StrictSig, CoreExpr)
 --     a) appropriate strictness info
 --     b) the unfolding (decorated with stricntess info)
 dmdAnalTopRhs rhs
-  = (sig, rhs')
+  = (sig, rhs2)
   where
-    arity         = exprArity rhs
-    (rhs_ty, rhs') = dmdAnal emptySigEnv (vanillaCall arity) rhs
+    call_dmd      = vanillaCall (exprArity rhs)
+    (_,      rhs1) = dmdAnal emptySigEnv call_dmd rhs
+    (rhs_ty, rhs2) = dmdAnal emptySigEnv call_dmd rhs1
     sig                   = mkTopSigTy rhs rhs_ty
+       -- Do two passes; see notes with extendSigsWithLam
+       -- Otherwise we get bogus CPR info for constructors like
+       --      newtype T a = MkT a
+       -- The constructor looks like (\x::T a -> x), modulo the coerce
+       -- extendSigsWithLam will optimistically give x a CPR tag the 
+       -- first time, which is wrong in the end.
 \end{code}
 
 %************************************************************************
@@ -206,7 +212,7 @@ dmdAnal sigs dmd (Lam var body)
     in
     (deferType lam_ty, Lam var' body')
 
-dmdAnal sigs dmd (Case scrut case_bndr [alt@(DataAlt dc,bndrs,rhs)])
+dmdAnal sigs dmd (Case scrut case_bndr ty [alt@(DataAlt dc,bndrs,rhs)])
   | let tycon = dataConTyCon dc,
     isProductTyCon tycon,
     not (isRecursiveTyCon tycon)
@@ -252,16 +258,16 @@ dmdAnal sigs dmd (Case scrut case_bndr [alt@(DataAlt dc,bndrs,rhs)])
 
        (scrut_ty, scrut') = dmdAnal sigs scrut_dmd scrut
     in
-    (alt_ty1 `bothType` scrut_ty, Case scrut' case_bndr' [alt'])
+    (alt_ty1 `bothType` scrut_ty, Case scrut' case_bndr' ty [alt'])
 
-dmdAnal sigs dmd (Case scrut case_bndr alts)
+dmdAnal sigs dmd (Case scrut case_bndr ty alts)
   = let
        (alt_tys, alts')        = mapAndUnzip (dmdAnalAlt sigs dmd) alts
        (scrut_ty, scrut')      = dmdAnal sigs evalDmd scrut
        (alt_ty, case_bndr')    = annotateBndr (foldr1 lubType alt_tys) case_bndr
     in
 --    pprTrace "dmdAnal:Case" (ppr alts $$ ppr alt_tys)
-    (alt_ty `bothType` scrut_ty, Case scrut' case_bndr' alts')
+    (alt_ty `bothType` scrut_ty, Case scrut' case_bndr' ty alts')
 
 dmdAnal sigs dmd (Let (NonRec id rhs) body) 
   = let
@@ -306,8 +312,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) `coreEqType` realWorldStatePrimTy
+    in 
+    (final_alt_ty, (con, bndrs', rhs'))
 \end{code}
 
 %************************************************************************
@@ -395,7 +423,9 @@ dmdAnalRhs top_lvl rec_flag sigs (id, rhs)
   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 )
+  (lazy_fv, sig_ty)  = WARN( arity /= dmdTypeDepth rhs_dmd_ty && not (exprIsTrivial rhs), ppr id )
+                               -- The RHS can be eta-reduced to just a variable, 
+                               -- in which case we should not complain. 
                       mkSigTy top_lvl rec_flag id rhs rhs_dmd_ty
   id'               = id `setIdNewStrictness` sig_ty
   sigs'                     = extendSigEnv top_lvl sigs id sig_ty
@@ -487,11 +517,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,
@@ -571,7 +600,7 @@ mk_sig_ty never_inline thunk_cpr_ok rhs (DmdType fv dmds res)
     res' = case res of
                RetCPR | ignore_cpr_info -> TopRes
                other                    -> res
-    ignore_cpr_info = not (exprIsValue rhs || thunk_cpr_ok)
+    ignore_cpr_info = not (exprIsHNF rhs || thunk_cpr_ok)
 \end{code}
 
 The unpack strategy determines whether we'll *really* unpack the argument,
@@ -729,12 +758,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.
---
---     NOTE: see notes [CPR-AND-STRICTNESS]
+-- 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
 --
 -- Also note that we only want to do this for something that
 -- definitely has product type, else we may get over-optimistic 
@@ -743,6 +775,8 @@ extendSigsWithLam :: SigEnv -> Id -> SigEnv
 extendSigsWithLam sigs id
   = case idNewDemandInfo_maybe id of
        Nothing               -> extendVarEnv sigs id (cprSig, NotTopLevel)
+               -- Optimistic in the Nothing case;
+               -- See notes [CPR-AND-STRICTNESS]
        Just (Eval (Prod ds)) -> extendVarEnv sigs id (cprSig, NotTopLevel)
        other                 -> sigs
 
@@ -757,7 +791,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
@@ -853,26 +887,15 @@ 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}
 
 \begin{code}
-betterStrictness :: StrictSig -> StrictSig -> Bool
-betterStrictness (StrictSig t1) (StrictSig t2) = betterDmdType t1 t2
-
-betterDmdType t1 t2 = (t1 `lubType` t2) == t2
-
-betterDemand :: Demand -> Demand -> Bool
--- If d1 `better` d2, and d2 `better` d2, then d1==d2
-betterDemand d1 d2 = (d1 `lub` d2) == d2
-\end{code}
-
-\begin{code}
 -------------------------
 -- Consider (if x then y else []) with demand V
 -- Then the first branch gives {y->V} and the second
--- *implicitly* has {y->A}.  So we must put {y->(V `lub` A)}
+--  *implicitly* has {y->A}.  So we must put {y->(V `lub` A)}
 -- in the result env.
 lubType (DmdType fv1 ds1 r1) (DmdType fv2 ds2 r2)
   = DmdType lub_fv2 (lub_ds ds1 ds2) (r1 `lubRes` r2)
@@ -1138,7 +1161,15 @@ get_changes_dmd id
     old = newDemand (idDemandInfo id)
     new_better = new `betterDemand` old 
     old_better = old `betterDemand` new
-#endif
+
+betterStrictness :: StrictSig -> StrictSig -> Bool
+betterStrictness (StrictSig t1) (StrictSig t2) = betterDmdType t1 t2
+
+betterDmdType t1 t2 = (t1 `lubType` t2) == t2
+
+betterDemand :: Demand -> Demand -> Bool
+-- If d1 `better` d2, and d2 `better` d2, then d1==d2
+betterDemand d1 d2 = (d1 `lub` d2) == d2
 
 squashSig (StrictSig (DmdType fv ds res))
   = StrictSig (DmdType emptyDmdEnv (map squashDmd ds) res)
@@ -1150,4 +1181,5 @@ squashDmd (Box d)    = Box (squashDmd d)
 squashDmd (Eval ds)  = Eval (mapDmds squashDmd ds)
 squashDmd (Defer ds) = Defer (mapDmds squashDmd ds)
 squashDmd d          = d
+#endif
 \end{code}