#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,
#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,
* 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}
%************************************************************************
%* *
-- 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}
%************************************************************************
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)
(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
= 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}
%************************************************************************
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
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,
-- (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]
---
-- 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 -> 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
\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)
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)
squashDmd (Eval ds) = Eval (mapDmds squashDmd ds)
squashDmd (Defer ds) = Defer (mapDmds squashDmd ds)
squashDmd d = d
+#endif
\end{code}