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,
(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)
= 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}
%************************************************************************
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,
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]
--
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
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}