X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2Fcompiler%2Fstranal%2FDmdAnal.lhs;h=b6bd92fe4a51657bd4a5aa33aad699145bb6a04c;hb=d6b7d200353e0bcc5a19a43caf252f37dee5bc6c;hp=a55be50b8c7001481ec219b1a32e830d464ad14f;hpb=c44e1c41da1657b845dc74fc38974e5bbb68d7cd;p=ghc-hetmet.git diff --git a/ghc/compiler/stranal/DmdAnal.lhs b/ghc/compiler/stranal/DmdAnal.lhs index a55be50..b6bd92f 100644 --- a/ghc/compiler/stranal/DmdAnal.lhs +++ b/ghc/compiler/stranal/DmdAnal.lhs @@ -21,14 +21,14 @@ 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 ) @@ -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 @@ -471,11 +487,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. - -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, @@ -484,7 +499,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 +570,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 +728,28 @@ 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. 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] +-- +-- 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 @@ -739,7 +761,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 @@ -835,7 +857,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}