[Git][ghc/ghc][wip/dmdanal-annotation-state] DmdAnal: Explicit annotation state
Sebastian Graf (@sgraf812)
gitlab at gitlab.haskell.org
Thu Jan 4 10:47:39 UTC 2024
Sebastian Graf pushed to branch wip/dmdanal-annotation-state at Glasgow Haskell Compiler / GHC
Commits:
bd9bd454 by Sebastian Graf at 2024-01-04T11:47:16+01:00
DmdAnal: Explicit annotation state
- - - - -
5 changed files:
- compiler/GHC/Core/Opt/DmdAnal.hs
- compiler/GHC/Core/Utils.hs
- + compiler/GHC/Data/STuple.hs
- compiler/GHC/Utils/Misc.hs
- compiler/ghc.cabal.in
Changes:
=====================================
compiler/GHC/Core/Opt/DmdAnal.hs
=====================================
@@ -6,6 +6,7 @@
A demand analysis
-----------------
-}
+{-# LANGUAGE RankNTypes #-}
module GHC.Core.Opt.DmdAnal
@@ -45,11 +46,20 @@ import GHC.Types.Var.Env
import GHC.Types.Var.Set
import GHC.Types.Basic
+import GHC.Data.STuple
+
import GHC.Utils.Misc
import GHC.Utils.Panic
import GHC.Utils.Outputable
import Data.List ( mapAccumL )
+import Data.Functor.Identity
+import Data.STRef
+import Control.Monad.ST
+import Control.Monad.Trans.Reader
+import Control.Monad (zipWithM_)
+import GHC.Data.Maybe
+import Data.Foldable (foldlM)
{-
************************************************************************
@@ -77,12 +87,28 @@ data DmdAnalOpts = DmdAnalOpts
-- This is a strict alternative to (,)
-- See Note [Space Leaks in Demand Analysis]
-data WithDmdType a = WithDmdType !DmdType !a
+type WithDmdType a = SPair DmdType a
+
+type AnalM s = ReaderT (DmdAnnotations (STRef s)) (ST s)
+
+annotate :: (DmdAnnotations (STRef s) -> STRef s (IdEnv a)) -> Id -> a -> AnalM s ()
+annotate ref id !a = ReaderT $ \ann -> modifySTRef' (ref ann) (\env -> extendVarEnv env id a)
+
+readAnn :: (DmdAnnotations (STRef s) -> STRef s (IdEnv a)) -> AnalM s (IdEnv a)
+readAnn ref = ReaderT $ \ann -> readSTRef (ref ann)
-getAnnotated :: WithDmdType a -> a
-getAnnotated (WithDmdType _ a) = a
+runAnalM :: (forall s. AnalM s a) -> DmdAnnotations Identity
+runAnalM m = runST $ do
+ env <- DA <$> newSTRef emptyVarEnv <*> newSTRef emptyVarEnv
+ _a <- runReaderT m env
+ demands <- readSTRef (da_demands env)
+ sigs <- readSTRef (da_sigs env)
+ pure $! DA (Identity demands) (Identity sigs)
-data DmdResult a b = R !a !b
+discardAnnotations :: (forall s. AnalM s a) -> a
+discardAnnotations m = runST $ do
+ env <- DA <$> newSTRef emptyVarEnv <*> newSTRef emptyVarEnv
+ runReaderT m env
-- | Outputs a new copy of the Core program in which binders have been annotated
-- with demand and strictness information.
@@ -91,19 +117,16 @@ data DmdResult a b = R !a !b
-- [Stamp out space leaks in demand analysis])
dmdAnalProgram :: DmdAnalOpts -> FamInstEnvs -> [CoreRule] -> CoreProgram -> CoreProgram
dmdAnalProgram opts fam_envs rules binds
- = getAnnotated $ go (emptyAnalEnv opts fam_envs) binds
+ = annotateProgram (runAnalM $ go (emptyAnalEnv opts fam_envs) binds) binds
where
-- See Note [Analysing top-level bindings]
-- and Note [Why care for top-level demand annotations?]
- go _ [] = WithDmdType nopDmdType []
- go env (b:bs) = cons_up $ dmdAnalBind TopLevel env topSubDmd b anal_body
+ go _ [] = pure nopDmdType
+ go env (b:bs) = dmdAnalBind TopLevel env topSubDmd b anal_body
where
- anal_body env'
- | WithDmdType body_ty bs' <- go env' bs
- = WithDmdType (body_ty `plusDmdType` keep_alive_roots env' (bindersOf b)) bs'
-
- cons_up :: WithDmdType (DmdResult b [b]) -> WithDmdType [b]
- cons_up (WithDmdType dmd_ty (R b' bs')) = WithDmdType dmd_ty (b' : bs')
+ anal_body env' = do
+ body_ty <- go env' bs
+ pure $! body_ty `plusDmdType` keep_alive_roots env' (bindersOf b)
keep_alive_roots :: AnalEnv -> [Id] -> DmdEnv
-- See Note [Absence analysis for stable unfoldings and RULES]
@@ -119,7 +142,7 @@ dmdAnalProgram opts fam_envs rules binds
demandRoot :: AnalEnv -> Id -> DmdEnv
-- See Note [Absence analysis for stable unfoldings and RULES]
-demandRoot env id = fst (dmdAnalStar env topDmd (Var id))
+demandRoot env id = discardAnnotations $ dmdAnalStar env topDmd (Var id)
demandRoots :: AnalEnv -> [Id] -> DmdEnv
-- See Note [Absence analysis for stable unfoldings and RULES]
@@ -187,7 +210,7 @@ Hence DmdAnal is configured by a flag `dmd_do_boxity` that is True only
if worker/wrapper follows after DmdAnal. If it is not set, and the signature
is not subject to Note [Boxity for bottoming functions], DmdAnal tries
to transfer over the previous boxity to the new demand signature, in
-`setIdDmdAndBoxSig`.
+`annotateSig`.
Why isn't CprAnal configured with a similar flag? Because if we aren't going to
do worker/wrapper we don't run CPR analysis at all. (see GHC.Core.Opt.Pipeline)
@@ -293,9 +316,9 @@ dmdAnalBind
-> SubDemand -- ^ Demand put on the "body"
-- (important for join points)
-> CoreBind
- -> (AnalEnv -> WithDmdType a) -- ^ How to analyse the "body", e.g.
+ -> (AnalEnv -> AnalM s DmdType) -- ^ How to analyse the "body", e.g.
-- where the binding is in scope
- -> WithDmdType (DmdResult CoreBind a)
+ -> AnalM s DmdType
dmdAnalBind top_lvl env dmd bind anal_body = case bind of
NonRec id rhs
| useLetUp top_lvl id
@@ -306,17 +329,17 @@ dmdAnalBind top_lvl env dmd bind anal_body = case bind of
-- | Annotates uninteresting top level functions ('isInterestingTopLevelFn')
-- with 'topDmd', the rest with the given demand.
-setBindIdDemandInfo :: TopLevelFlag -> Id -> Demand -> Id
-setBindIdDemandInfo top_lvl id dmd = setIdDemandInfo id $ case top_lvl of
- TopLevel | not (isInterestingTopLevelFn id) -> topDmd
- _ -> dmd
+annotateBindIdDemand :: TopLevelFlag -> Id -> Demand -> AnalM s ()
+annotateBindIdDemand top_lvl id dmd = case top_lvl of
+ TopLevel | not (isInterestingTopLevelFn id) -> annotate da_demands id topDmd
+ _ -> annotate da_demands id dmd
-- | Update the demand signature, but be careful not to change boxity info if
-- `dmd_do_boxity` is True or if the signature is bottom.
-- See Note [Don't change boxity without worker/wrapper]
-- and Note [Boxity for bottoming functions].
-setIdDmdAndBoxSig :: DmdAnalOpts -> Id -> DmdSig -> Id
-setIdDmdAndBoxSig opts id sig = setIdDmdSig id $
+annotateSig :: DmdAnalOpts -> Id -> DmdSig -> AnalM s ()
+annotateSig opts id sig = annotate da_sigs id $
if dmd_do_boxity opts || isBottomingSig sig
then sig
else transferArgBoxityDmdSig (idDmdSig id) sig
@@ -338,22 +361,24 @@ dmdAnalBindLetUp :: TopLevelFlag
-> AnalEnv
-> Id
-> CoreExpr
- -> (AnalEnv -> WithDmdType a)
- -> WithDmdType (DmdResult CoreBind a)
-dmdAnalBindLetUp top_lvl env id rhs anal_body = WithDmdType final_ty (R (NonRec id' rhs') (body'))
- where
- WithDmdType body_ty body' = anal_body (addInScopeAnalEnv env id)
- -- See Note [Bringing a new variable into scope]
- WithDmdType body_ty' id_dmd = findBndrDmd env body_ty id
- -- See Note [Finalising boxity for demand signatures]
+ -> (AnalEnv -> AnalM s DmdType)
+ -> AnalM s DmdType
+dmdAnalBindLetUp top_lvl env id rhs anal_body = do
+ -- See Note [Bringing a new variable into scope]
+ body_ty <- anal_body (addInScopeAnalEnv env id)
- id_dmd' = finaliseLetBoxity env (idType id) id_dmd
- !id' = setBindIdDemandInfo top_lvl id id_dmd'
- (rhs_ty, rhs') = dmdAnalStar env id_dmd' rhs
+ -- See Note [Finalising boxity for demand signatures]
+ let S2 body_ty' id_dmd = findBndrDmd env body_ty id
+ let id_dmd' = finaliseLetBoxity env (idType id) id_dmd
+ annotateBindIdDemand top_lvl id id_dmd'
- -- See Note [Absence analysis for stable unfoldings and RULES]
- rule_fvs = bndrRuleAndUnfoldingIds id
- final_ty = body_ty' `plusDmdType` rhs_ty `plusDmdType` demandRootSet env rule_fvs
+ rhs_ty <- dmdAnalStar env id_dmd' rhs
+
+ -- See Note [Absence analysis for stable unfoldings and RULES]
+ let rule_fvs = bndrRuleAndUnfoldingIds id
+ let final_ty = body_ty' `plusDmdType` rhs_ty `plusDmdType` demandRootSet env rule_fvs
+
+ return final_ty
-- | Let bindings can be processed in two ways:
-- Down (RHS before body) or Up (body before RHS).
@@ -367,25 +392,22 @@ dmdAnalBindLetUp top_lvl env id rhs anal_body = WithDmdType final_ty (R (NonRec
-- Local non-recursive definitions without a lambda are handled with LetUp.
--
-- This is the LetDown rule in the paper “Higher-Order Cardinality Analysis”.
-dmdAnalBindLetDown :: TopLevelFlag -> AnalEnv -> SubDemand -> CoreBind -> (AnalEnv -> WithDmdType a) -> WithDmdType (DmdResult CoreBind a)
+dmdAnalBindLetDown :: TopLevelFlag -> AnalEnv -> SubDemand -> CoreBind -> (AnalEnv -> AnalM s DmdType) -> AnalM s DmdType
dmdAnalBindLetDown top_lvl env dmd bind anal_body = case bind of
- NonRec id rhs
- | (env', weak_fv, id1, rhs1) <-
- dmdAnalRhsSig top_lvl NonRecursive env dmd id rhs
- -> do_rest env' weak_fv [(id1, rhs1)] (uncurry NonRec . only)
- Rec pairs
- | (env', weak_fv, pairs') <- dmdFix top_lvl env dmd pairs
- -> do_rest env' weak_fv pairs' Rec
+ NonRec id rhs -> do
+ S2 env' weak_fv <- dmdAnalRhsSig top_lvl NonRecursive env dmd id rhs
+ do_rest env' weak_fv [id]
+ Rec pairs -> do
+ S2 env' weak_fv <- dmdFix top_lvl env dmd pairs
+ do_rest env' weak_fv (map fst pairs)
where
- do_rest env' weak_fv pairs1 build_bind = WithDmdType final_ty (R (build_bind pairs2) body')
- where
- WithDmdType body_ty body' = anal_body env'
+ do_rest env' weak_fv bndrs = do
+ body_ty <- anal_body env'
+ let dmd_ty = addWeakFVs body_ty weak_fv
-- see Note [Lazy and unleashable free variables]
- dmd_ty = addWeakFVs body_ty weak_fv
- WithDmdType final_ty id_dmds = findBndrsDmds env' dmd_ty (strictMap fst pairs1)
- -- Important to force this as build_bind might not force it.
- !pairs2 = strictZipWith do_one pairs1 id_dmds
- do_one (id', rhs') dmd = ((,) $! setBindIdDemandInfo top_lvl id' dmd) $! rhs'
+ let S2 final_ty id_dmds = findBndrsDmds env' dmd_ty bndrs
+ zipWithM_ (annotateBindIdDemand top_lvl) bndrs id_dmds
+ pure final_ty
-- 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.
@@ -418,59 +440,48 @@ anticipateANF e n
dmdAnalStar :: AnalEnv
-> Demand -- This one takes a *Demand*
-> CoreExpr
- -> (DmdEnv, CoreExpr)
-dmdAnalStar env (n :* sd) e
+ -> AnalM s DmdEnv
+dmdAnalStar env (n :* sd) e = do
-- NB: (:*) expands AbsDmd and BotDmd as needed
- | WithDmdType dmd_ty e' <- dmdAnal env sd e
- , n' <- anticipateANF e n
- -- See Note [Anticipating ANF in demand analysis]
- -- and Note [Analysing with absent demand]
- = (discardArgDmds $ multDmdType n' dmd_ty, e')
+ dmd_ty <- dmdAnal env sd e
+ let n' = anticipateANF e n
+ -- See Note [Anticipating ANF in demand analysis]
+ -- and Note [Analysing with absent demand]
+ pure $! discardArgDmds $ multDmdType n' dmd_ty
-- Main Demand Analysis machinery
dmdAnal, dmdAnal' :: AnalEnv
-> SubDemand -- The main one takes a *SubDemand*
- -> CoreExpr -> WithDmdType CoreExpr
+ -> CoreExpr -> AnalM s DmdType
dmdAnal env d e = -- pprTrace "dmdAnal" (ppr d <+> ppr e) $
dmdAnal' env d e
-dmdAnal' _ _ (Lit lit) = WithDmdType nopDmdType (Lit lit)
-dmdAnal' _ _ (Type ty) = WithDmdType nopDmdType (Type ty) -- Doesn't happen, in fact
-dmdAnal' _ _ (Coercion co)
- = WithDmdType (noArgsDmdType (coercionDmdEnv co)) (Coercion co)
+dmdAnal' env sd (Var var) = pure $! dmdTransform env var sd
-dmdAnal' env dmd (Var var)
- = WithDmdType (dmdTransform env var dmd) (Var var)
+dmdAnal' _ _ (Lit _) = pure nopDmdType
+dmdAnal' _ _ (Type _) = pure nopDmdType -- Doesn't happen, in fact
+dmdAnal' _ _ (Coercion co) = pure $! noArgsDmdType (coercionDmdEnv co)
-dmdAnal' env dmd (Cast e co)
- = WithDmdType (dmd_ty `plusDmdType` coercionDmdEnv co) (Cast e' co)
- where
- WithDmdType dmd_ty e' = dmdAnal env dmd e
+dmdAnal' env sd (Cast e co) = do
+ dmd_ty <- dmdAnal env sd e
+ pure $! dmd_ty `plusDmdType` coercionDmdEnv co
-dmdAnal' env dmd (Tick t e)
- = WithDmdType dmd_ty (Tick t e')
- where
- WithDmdType dmd_ty e' = dmdAnal env dmd e
+dmdAnal' env sd (Tick _ e) = dmdAnal env sd e
-dmdAnal' env dmd (App fun (Type ty))
- = WithDmdType fun_ty (App fun' (Type ty))
- where
- WithDmdType fun_ty fun' = dmdAnal env dmd fun
+dmdAnal' env dmd (App fun (Type _)) = dmdAnal env dmd fun
-- Lots of the other code is there to make this
-- beautiful, compositional, application rule :-)
-dmdAnal' env dmd (App fun arg)
- = -- This case handles value arguments (type args handled above)
- -- Crucially, coercions /are/ handled here, because they are
- -- value arguments (#10288)
- let
- call_dmd = mkCalledOnceDmd dmd
- WithDmdType fun_ty fun' = dmdAnal env call_dmd fun
- (arg_dmd, res_ty) = splitDmdTy fun_ty
- (arg_ty, arg') = dmdAnalStar env arg_dmd arg
- in
--- pprTrace "dmdAnal:app" (vcat
+dmdAnal' env dmd (App fun arg) = do
+ -- This case handles value arguments (type args handled above)
+ -- Crucially, coercions /are/ handled here, because they are
+ -- value arguments (#10288)
+ let call_dmd = mkCalledOnceDmd dmd
+ fun_ty <- dmdAnal env call_dmd fun
+ let (arg_dmd, res_ty) = splitDmdTy fun_ty
+ arg_ty <- dmdAnalStar env arg_dmd arg
+-- pprTraceM "dmdAnal:app" (vcat
-- [ text "dmd =" <+> ppr dmd
-- , text "expr =" <+> ppr (App fun arg)
-- , text "fun dmd_ty =" <+> ppr fun_ty
@@ -478,80 +489,64 @@ dmdAnal' env dmd (App fun arg)
-- , text "arg dmd_ty =" <+> ppr arg_ty
-- , text "res dmd_ty =" <+> ppr res_ty
-- , text "overall res dmd_ty =" <+> ppr (res_ty `plusDmdType` arg_ty) ])
- WithDmdType (res_ty `plusDmdType` arg_ty) (App fun' arg')
+ pure $! res_ty `plusDmdType` arg_ty
dmdAnal' env dmd (Lam var body)
- | isTyVar var
- = let
- WithDmdType body_ty body' = dmdAnal (addInScopeAnalEnv env var) dmd body
- -- See Note [Bringing a new variable into scope]
- in
- WithDmdType body_ty (Lam var body')
-
- | otherwise
- = let (n, body_dmd) = peelCallDmd dmd
- -- body_dmd: a demand to analyze the body
-
- WithDmdType body_ty body' = dmdAnal (addInScopeAnalEnv env var) body_dmd body
- -- See Note [Bringing a new variable into scope]
- WithDmdType lam_ty var' = annotateLamIdBndr env body_ty var
- new_dmd_type = multDmdType n lam_ty
- in
- WithDmdType new_dmd_type (Lam var' body')
+ | isTyVar var = dmdAnal body_env dmd body
+ | otherwise = do
+ let (n, body_dmd) = peelCallDmd dmd
+ body_ty <- dmdAnal body_env body_dmd body
+ -- See Note [Bringing a new variable into scope]
+ let S2 body_ty' dmd = findBndrDmd env body_ty var
+ annotate da_demands var dmd
+ let !lam_ty = addDemand dmd body_ty'
+ return $! multDmdType n lam_ty
+ where
+ body_env = addInScopeAnalEnv env var -- See Note [Bringing a new variable into scope]
-dmdAnal' env dmd (Case scrut case_bndr ty [Alt alt_con bndrs rhs])
+dmdAnal' env dmd (Case scrut case_bndr _ty [Alt alt_con bndrs rhs])
-- Only one alternative.
-- If it's a DataAlt, it should be the only constructor of the type and we
-- can consider its field demands when analysing the scrutinee.
- | want_precise_field_dmds alt_con
- = let
- rhs_env = addInScopeAnalEnvs env (case_bndr:bndrs)
- -- See Note [Bringing a new variable into scope]
- WithDmdType rhs_ty rhs' = dmdAnal rhs_env dmd rhs
- WithDmdType alt_ty1 fld_dmds = findBndrsDmds env rhs_ty bndrs
- WithDmdType alt_ty2 case_bndr_dmd = findBndrDmd env alt_ty1 case_bndr
- !case_bndr' = setIdDemandInfo case_bndr case_bndr_dmd
+ | want_precise_field_dmds alt_con = do
+ let rhs_env = addInScopeAnalEnvs env (case_bndr:bndrs)
+ -- See Note [Bringing a new variable into scope]
+ rhs_ty <- dmdAnal rhs_env dmd rhs
+ let S2 alt_ty1 fld_dmds = findBndrsDmds env rhs_ty bndrs
+ S2 alt_ty2 case_bndr_dmd = findBndrDmd env alt_ty1 case_bndr
+ annotate da_demands case_bndr case_bndr_dmd
-- Evaluation cardinality on the case binder is irrelevant and a no-op.
-- What matters is its nested sub-demand!
-- NB: If case_bndr_dmd is absDmd, boxity will say Unboxed, which is
-- what we want, because then `seq` will put a `seqDmd` on its scrut.
- (_ :* case_bndr_sd) = strictifyDmd case_bndr_dmd
-
- -- Compute demand on the scrutinee
- -- FORCE the result, otherwise thunks will end up retaining the
- -- whole DmdEnv
- !(!bndrs', !scrut_sd)
- | DataAlt _ <- alt_con
+ let !scrut_sd
+ | (_ :* case_bndr_sd) <- strictifyDmd case_bndr_dmd
-- See Note [Demand on the scrutinee of a product case]
- , let !scrut_sd = scrutSubDmd case_bndr_sd fld_dmds
- -- See Note [Demand on case-alternative binders]
- , let !fld_dmds' = fieldBndrDmds scrut_sd (length fld_dmds)
- , let !bndrs' = setBndrsDemandInfo bndrs fld_dmds'
- = (bndrs', scrut_sd)
- | otherwise
- -- DEFAULT alts. Simply add demands and discard the evaluation
- -- cardinality, as we evaluate the scrutinee exactly once.
- = assert (null bndrs) (bndrs, case_bndr_sd)
+ = scrutSubDmd case_bndr_sd fld_dmds
- alt_ty3
+ -- See Note [Demand on case-alternative binders]
+ case alt_con of
+ DataAlt _ -> annotateBndrsDemands bndrs (fieldBndrDmds scrut_sd (length fld_dmds))
+ _ -> pure ()
+
+ let alt_ty3
-- See Note [Precise exceptions and strictness analysis] in "GHC.Types.Demand"
| exprMayThrowPreciseException (ae_fam_envs env) scrut
= deferAfterPreciseException alt_ty2
| otherwise
= alt_ty2
- WithDmdType scrut_ty scrut' = dmdAnal env scrut_sd scrut
- res_ty = alt_ty3 `plusDmdType` discardArgDmds scrut_ty
- in
--- pprTrace "dmdAnal:Case1" (vcat [ text "scrut" <+> ppr scrut
+ scrut_ty <- dmdAnal env scrut_sd scrut
+ let !res_ty = alt_ty3 `plusDmdType` discardArgDmds scrut_ty
+-- pprTraceM "dmdAnal:Case1" (vcat [ text "scrut" <+> ppr scrut
-- , text "dmd" <+> ppr dmd
-- , text "case_bndr_dmd" <+> ppr (idDemandInfo case_bndr')
-- , text "scrut_sd" <+> ppr scrut_sd
-- , text "scrut_ty" <+> ppr scrut_ty
-- , text "alt_ty" <+> ppr alt_ty2
-- , text "res_ty" <+> ppr res_ty ]) $
- WithDmdType res_ty (Case scrut' case_bndr' ty [Alt alt_con bndrs' rhs'])
+ pure res_ty
where
want_precise_field_dmds (DataAlt dc)
| Nothing <- tyConSingleAlgDataCon_maybe $ dataConTyCon dc
@@ -564,36 +559,32 @@ dmdAnal' env dmd (Case scrut case_bndr ty [Alt alt_con bndrs rhs])
want_precise_field_dmds (LitAlt {}) = False -- Like the non-product datacon above
want_precise_field_dmds DEFAULT = True
-dmdAnal' env dmd (Case scrut case_bndr ty alts)
- = let -- Case expression with multiple alternatives
- WithDmdType scrut_ty scrut' = dmdAnal env topSubDmd scrut
-
- WithDmdType alt_ty1 case_bndr_dmd = findBndrDmd env alt_ty case_bndr
- !case_bndr' = setIdDemandInfo case_bndr case_bndr_dmd
- WithDmdType alt_ty alts' = dmdAnalSumAlts env dmd case_bndr alts
-
- fam_envs = ae_fam_envs env
- alt_ty2
- -- See Note [Precise exceptions and strictness analysis] in "GHC.Types.Demand"
- | exprMayThrowPreciseException fam_envs scrut
- = deferAfterPreciseException alt_ty1
- | otherwise
- = alt_ty1
- res_ty = scrut_ty `plusDmdType` discardArgDmds alt_ty2
+dmdAnal' env dmd (Case scrut case_bndr _ty alts) = do
+ -- Case expression with multiple alternatives
+ alt_tys <- traverse (dmdAnalSumAlt env dmd case_bndr) alts
+ let lub = foldr lubDmdType botDmdType
+ let S2 alt_ty1 case_bndr_dmd = findBndrDmd env (lub alt_tys) case_bndr
+ annotate da_demands case_bndr case_bndr_dmd
+ scrut_ty <- dmdAnal env topSubDmd scrut
+
+ let fam_envs = ae_fam_envs env
+ alt_ty2
+ -- See Note [Precise exceptions and strictness analysis] in "GHC.Types.Demand"
+ | exprMayThrowPreciseException fam_envs scrut
+ = deferAfterPreciseException alt_ty1
+ | otherwise
+ = alt_ty1
+ res_ty = scrut_ty `plusDmdType` discardArgDmds alt_ty2
- in
--- pprTrace "dmdAnal:Case2" (vcat [ text "scrut" <+> ppr scrut
--- , text "scrut_ty" <+> ppr scrut_ty
--- , text "alt_ty1" <+> ppr alt_ty1
--- , text "alt_ty2" <+> ppr alt_ty2
--- , text "res_ty" <+> ppr res_ty ]) $
- WithDmdType res_ty (Case scrut' case_bndr' ty alts')
+-- pprTraceM "dmdAnal:Case2" (vcat [ text "scrut" <+> ppr scrut
+-- , text "scrut_ty" <+> ppr scrut_ty
+-- , text "alt_ty1" <+> ppr alt_ty1
+-- , text "alt_ty2" <+> ppr alt_ty2
+-- , text "res_ty" <+> ppr res_ty ])
+ pure res_ty
dmdAnal' env dmd (Let bind body)
- = WithDmdType final_ty (Let bind' body')
- where
- !(WithDmdType final_ty (R bind' body')) = dmdAnalBind NotTopLevel env dmd bind go'
- go' !env' = dmdAnal env' dmd body
+ = dmdAnalBind NotTopLevel env dmd bind (\env -> dmdAnal env dmd body)
-- | A simple, syntactic analysis of whether an expression MAY throw a precise
-- exception when evaluated. It's always sound to return 'True'.
@@ -629,34 +620,24 @@ forcesRealWorld fam_envs ty
| otherwise
= False
-dmdAnalSumAlts :: AnalEnv -> SubDemand -> Id -> [CoreAlt] -> WithDmdType [CoreAlt]
-dmdAnalSumAlts _ _ _ [] = WithDmdType botDmdType []
- -- Base case is botDmdType, for empty case alternatives
- -- This is a unit for lubDmdType, and the right result
- -- when there really are no alternatives
-dmdAnalSumAlts env dmd case_bndr (alt:alts)
- = let
- WithDmdType cur_ty alt' = dmdAnalSumAlt env dmd case_bndr alt
- WithDmdType rest_ty alts' = dmdAnalSumAlts env dmd case_bndr alts
- in WithDmdType (lubDmdType cur_ty rest_ty) (alt':alts')
-
-
-dmdAnalSumAlt :: AnalEnv -> SubDemand -> Id -> CoreAlt -> WithDmdType CoreAlt
-dmdAnalSumAlt env dmd case_bndr (Alt con bndrs rhs)
- | let rhs_env = addInScopeAnalEnvs env (case_bndr:bndrs)
- -- See Note [Bringing a new variable into scope]
- , WithDmdType rhs_ty rhs' <- dmdAnal rhs_env dmd rhs
- , WithDmdType alt_ty dmds <- findBndrsDmds env rhs_ty bndrs
- , let (_ :* case_bndr_sd) = findIdDemand alt_ty case_bndr
- -- See Note [Demand on case-alternative binders]
- -- we can't use the scrut_sd, because it says 'Prod' and we'll use
- -- topSubDmd anyway for scrutinees of sum types.
- scrut_sd = scrutSubDmd case_bndr_sd dmds
- dmds' = fieldBndrDmds scrut_sd (length dmds)
- -- Do not put a thunk into the Alt
- !new_ids = setBndrsDemandInfo bndrs dmds'
- = -- pprTrace "dmdAnalSumAlt" (ppr con $$ ppr case_bndr $$ ppr dmd $$ ppr alt_ty) $
- WithDmdType alt_ty (Alt con new_ids rhs')
+dmdAnalSumAlt :: AnalEnv -> SubDemand -> Id -> CoreAlt -> AnalM s DmdType
+dmdAnalSumAlt env dmd case_bndr (Alt con bndrs rhs) = do
+ let rhs_env = addInScopeAnalEnvs env (case_bndr:bndrs)
+ -- See Note [Bringing a new variable into scope]
+ rhs_ty <- dmdAnal rhs_env dmd rhs
+ let S2 alt_ty dmds = findBndrsDmds env rhs_ty bndrs
+ -- See Note [Demand on case-alternative binders]
+ -- we can't use the scrut_sd, because it says 'Prod' and we'll use
+ -- topSubDmd anyway for scrutinees of sum types.
+ let scrut_sd
+ | (_ :* case_bndr_sd) <- findIdDemand alt_ty case_bndr
+ = scrutSubDmd case_bndr_sd dmds
+ case con of
+ DataAlt _ -> annotateBndrsDemands bndrs (fieldBndrDmds scrut_sd (length dmds))
+ _ -> pure ()
+ annotateBndrsDemands bndrs (fieldBndrDmds scrut_sd (length dmds))
+ -- pprTraceM "dmdAnalSumAlt" (ppr con $$ ppr case_bndr $$ ppr dmd $$ ppr alt_ty)
+ pure alt_ty
-- See Note [Demand on the scrutinee of a product case]
scrutSubDmd :: SubDemand -> [Demand] -> SubDemand
@@ -1005,8 +986,7 @@ dmdTransform env var sd
dmdTransformDataConSig (dataConRepStrictness con) sd
-- See Note [DmdAnal for DataCon wrappers]
| isDataConWrapId var, let rhs = uf_tmpl (realIdUnfolding var)
- , WithDmdType dmd_ty _rhs' <- dmdAnal env sd rhs
- = dmd_ty
+ = discardAnnotations $ dmdAnal env sd rhs
-- Dictionary component selectors
-- Used to be controlled by a flag.
-- See #18429 for some perf measurements.
@@ -1066,40 +1046,38 @@ dmdAnalRhsSig
-> RecFlag
-> AnalEnv -> SubDemand
-> Id -> CoreExpr
- -> (AnalEnv, WeakDmds, Id, CoreExpr)
+ -> AnalM s (SPair AnalEnv WeakDmds)
-- Process the RHS of the binding, add the strictness signature
-- to the Id, and augment the environment with the signature as well.
-- See Note [NOINLINE and strictness]
-dmdAnalRhsSig top_lvl rec_flag env let_dmd id rhs
- = -- pprTrace "dmdAnalRhsSig" (ppr id $$ ppr let_dmd $$ ppr rhs_dmds $$ ppr sig $$ ppr weak_fvs) $
- (final_env, weak_fvs, final_id, final_rhs)
- where
+dmdAnalRhsSig top_lvl rec_flag env let_sd id rhs = do
+ let
threshold_arity = thresholdArity id rhs
-
rhs_dmd = mkCalledOnceDmds threshold_arity body_dmd
-
body_dmd
| isJoinId id
-- See Note [Demand analysis for join points]
-- See Note [Invariants on join points] invariant 2b, in GHC.Core
-- threshold_arity matches the join arity of the join point
-- See Note [Unboxed demand on function bodies returning small products]
- = unboxedWhenSmall env rec_flag (resultType_maybe id) let_dmd
+ = unboxedWhenSmall env rec_flag (resultType_maybe id) let_sd
| otherwise
-- See Note [Unboxed demand on function bodies returning small products]
= unboxedWhenSmall env rec_flag (resultType_maybe id) topSubDmd
- WithDmdType rhs_dmd_ty rhs' = dmdAnal env rhs_dmd rhs
- DmdType rhs_env rhs_dmds = rhs_dmd_ty
- (final_rhs_dmds, final_rhs) = finaliseArgBoxities env id threshold_arity
- rhs_dmds (de_div rhs_env) rhs'
-
- sig = mkDmdSigForArity threshold_arity (DmdType sig_env final_rhs_dmds)
-
- opts = ae_opts env
- final_id = setIdDmdAndBoxSig opts id sig
- !final_env = extendAnalEnv top_lvl env final_id sig
+ rhs_dmd_ty <- dmdAnal env rhs_dmd rhs
+ let
+ (lam_bndrs, _) = collectBinders rhs
+ DmdType rhs_env rhs_dmds = rhs_dmd_ty
+ final_rhs_dmds = finaliseArgBoxities env id threshold_arity rhs_dmds
+ (de_div rhs_env) lam_bndrs
+ -- Attach the demands to the outer lambdas of this expression
+ -- NB: zipWithM_, not zipWithEqualM_, in contrast to annotateBndrsDemands.
+ -- We might have more demands than binders (PAP), hence don't panic (#22997).
+ zipWithM_ (annotate da_demands) (filter isRuntimeVar lam_bndrs) final_rhs_dmds
+
+ let
-- See Note [Aggregated demand for cardinality]
-- FIXME: That Note doesn't explain the following lines at all. The reason
-- is really much different: When we have a recursive function, we'd
@@ -1121,6 +1099,13 @@ dmdAnalRhsSig top_lvl rec_flag env let_dmd id rhs
-- See Note [Lazy and unleashable free variables]
!(!sig_env, !weak_fvs) = splitWeakDmds rhs_env2
+ sig = mkDmdSigForArity threshold_arity (DmdType sig_env final_rhs_dmds)
+ opts = ae_opts env
+ !final_env = extendAnalEnv top_lvl env id sig
+
+ -- pprTraceM "dmdAnalRhsSig" (ppr id $$ ppr let_sd $$ ppr rhs_dmds $$ ppr sig $$ ppr weak_fvs)
+ annotateSig opts id sig
+ pure $! S2 final_env weak_fvs
splitWeakDmds :: DmdEnv -> (DmdEnv, WeakDmds)
splitWeakDmds (DE fvs div) = (DE sig_fvs div, weak_fvs)
@@ -1246,7 +1231,7 @@ Note [Lazy and unleashable free variables].
The implementation is easy. When analysing a join point, we can
analyse its body with the demand from the entire join-binding (written
-let_dmd here).
+let_sd here).
Another win for join points! #13543.
@@ -1920,16 +1905,16 @@ positiveTopBudget (MkB n _) = n >= 0
finaliseArgBoxities :: AnalEnv -> Id -> Arity
-> [Demand] -> Divergence
- -> CoreExpr -> ([Demand], CoreExpr)
-finaliseArgBoxities env fn threshold_arity rhs_dmds div rhs
+ -> [Id] -> [Demand]
+finaliseArgBoxities env fn threshold_arity rhs_dmds div bndrs
-- Check for an OPAQUE function: see Note [OPAQUE pragma]
-- In that case, trim off all boxity info from argument demands
- -- and demand info on lambda binders
+ -- and demand info on lambda binders (#22502)
-- See Note [The OPAQUE pragma and avoiding the reboxing of arguments]
| isOpaquePragma (idInlinePragma fn)
, let trimmed_rhs_dmds = map trimBoxity rhs_dmds
- = (trimmed_rhs_dmds, set_lam_dmds trimmed_rhs_dmds rhs)
+ = trimmed_rhs_dmds
-- Check that we have enough visible binders to match the
-- threshold arity; if not, we won't do worker/wrapper
@@ -1940,7 +1925,7 @@ finaliseArgBoxities env fn threshold_arity rhs_dmds div rhs
-- It's a bit of a corner case. Anyway for now we pass on the
-- unadulterated demands from the RHS, without any boxity trimming.
| threshold_arity > count isId bndrs
- = (rhs_dmds, rhs)
+ = rhs_dmds
-- The normal case
| otherwise -- NB: threshold_arity might be less than
@@ -1950,13 +1935,12 @@ finaliseArgBoxities env fn threshold_arity rhs_dmds div rhs
-- , text "max" <+> ppr max_wkr_args
-- , text "dmds before:" <+> ppr (map idDemandInfo (filter isId bndrs))
-- , text "dmds after: " <+> ppr arg_dmds' ]) $
- (arg_dmds', set_lam_dmds arg_dmds' rhs)
+ arg_dmds'
-- set_lam_dmds: we must attach the final boxities to the lambda-binders
-- of the function, both because that's kosher, and because CPR analysis
-- uses the info on the binders directly.
where
opts = ae_opts env
- (bndrs, _body) = collectBinders rhs
unarise_arity = sum [ unariseArity (idType b) | b <- bndrs, isId b ]
max_wkr_args = dmd_max_worker_args opts `max` unarise_arity
-- This is the budget initialisation step of
@@ -2034,19 +2018,6 @@ finaliseArgBoxities env fn threshold_arity rhs_dmds div rhs
| positiveTopBudget bg_inner' = (bg_inner', dmd')
| otherwise = (bg_inner, trimBoxity dmd)
- set_lam_dmds :: [Demand] -> CoreExpr -> CoreExpr
- -- Attach the demands to the outer lambdas of this expression
- set_lam_dmds (dmd:dmds) (Lam v e)
- | isTyVar v = Lam v (set_lam_dmds (dmd:dmds) e)
- | otherwise = Lam (v `setIdDemandInfo` dmd) (set_lam_dmds dmds e)
- set_lam_dmds dmds (Cast e co) = Cast (set_lam_dmds dmds e) co
- -- This case happens for an OPAQUE function, which may look like
- -- f = (\x y. blah) |> co
- -- We give it strictness but no boxity (#22502)
- set_lam_dmds _ e = e
- -- In the OPAQUE case, the list of demands at this point might be
- -- non-empty, e.g., when looking at a PAP. Hence don't panic (#22997).
-
finaliseLetBoxity
:: AnalEnv
-> Type -- ^ Type of the let-bound Id
@@ -2178,65 +2149,63 @@ dmdFix :: TopLevelFlag
-> AnalEnv -- Does not include bindings for this binding
-> SubDemand
-> [(Id,CoreExpr)]
- -> (AnalEnv, WeakDmds, [(Id,CoreExpr)]) -- Binders annotated with strictness info
-dmdFix top_lvl env let_dmd orig_pairs
- = loop 1 initial_pairs
+ -> AnalM s (SPair AnalEnv WeakDmds)
+dmdFix top_lvl env let_sd pairs
+ = do sigs <- read_sigs; loop 1 (next_env sigs) sigs
where
- opts = ae_opts env
+ bndrs = map fst pairs
+ next_env sigs = extendAnalEnvs top_lvl env bndrs sigs
+
-- See Note [Initialising strictness]
- initial_pairs | ae_virgin env = [(setIdDmdAndBoxSig opts id botSig, rhs) | (id, rhs) <- orig_pairs ]
- | otherwise = orig_pairs
+ read_sigs = do
+ annotations <- readAnn da_sigs
+ let init_sigs = [ botSig | _ <- bndrs ]
+ pure $! traverse (lookupVarEnv annotations) bndrs `orElse` init_sigs
-- If fixed-point iteration does not yield a result we use this instead
-- See Note [Safe abortion in the fixed-point iteration]
- abort :: (AnalEnv, WeakDmds, [(Id,CoreExpr)])
- abort = (env, weak_fv', zapped_pairs)
- where (weak_fv, pairs') = step True (zapIdDmdSig orig_pairs)
- -- Note [Lazy and unleashable free variables]
- weak_fvs = plusVarEnvList $ map (de_fvs . dmdSigDmdEnv . idDmdSig . fst) pairs'
- weak_fv' = plusVarEnv_C plusDmd weak_fv $ mapVarEnv (const topDmd) weak_fvs
- zapped_pairs = zapIdDmdSig pairs'
-
- -- The fixed-point varies the idDmdSig field of the binders, and terminates if that
- -- annotation does not change any more.
- loop :: Int -> [(Id,CoreExpr)] -> (AnalEnv, WeakDmds, [(Id,CoreExpr)])
- loop n pairs = -- pprTrace "dmdFix" (ppr n <+> vcat [ ppr id <+> ppr (idDmdSig id)
- -- | (id,_) <- pairs]) $
- loop' n pairs
-
- loop' n pairs
- | found_fixpoint = (final_anal_env, weak_fv, pairs')
- | n == 10 = abort
- | otherwise = loop (n+1) pairs'
+ abort :: AnalM s (SPair AnalEnv WeakDmds)
+ abort = do
+ S3 env' sigs' weak_fv <- step (next_env [ nopSig | _ <- bndrs ])
+ -- NB: step updates the annotation
+ -- Note [Lazy and unleashable free variables]
+ let weak_fvs = plusVarEnvList $ map (de_fvs . dmdSigDmdEnv) sigs'
+ weak_fv' = plusVarEnv_C plusDmd weak_fv $ mapVarEnv (const topDmd) weak_fvs
+ pure $! S2 env' weak_fv'
+
+ -- The fixed-point varies the DmdSig stored in the AnalEnv for bndrs, and
+ -- terminates if that annotation does not change any more.
+ -- For convenience, we also pass the bndr's DmdSig instead of fetching it
+ -- from AnalEnv on every iteration.
+ loop :: Int -> AnalEnv -> [DmdSig] -> AnalM s (SPair AnalEnv WeakDmds)
+ loop n env sigs = -- pprTrace "dmdFix" (ppr n <+> vcat [ ppr id <+> ppr (idDmdSig id)
+ -- | (id,_) <- sigs]) $
+ loop' n env sigs
+
+ loop' n env sigs | n == 10 = abort
+ | otherwise = do
+ S3 env' sigs' weak_fv' <- step env
+ -- NB: step updates the annotation
+ let found_fixpoint = sigs' == sigs
+ if found_fixpoint
+ then pure $! S2 env' weak_fv'
+ else loop (n+1) env' sigs'
+
+ step :: AnalEnv -> AnalM s (STriple AnalEnv [DmdSig] WeakDmds)
+ step env = do
+ S2 env' weak_fv' <- foldlM do_one (S2 env emptyVarEnv) pairs
+ -- foldlM: 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
+ let sigs' = expectJust "dmdFix.step" $ traverse (fmap fst . lookupSigEnv env') bndrs
+ zipWithEqualM_ "dmdFix.step" (annotateSig (ae_opts env)) bndrs sigs'
+ pure $! S3 env' sigs' weak_fv'
where
- found_fixpoint = map (idDmdSig . fst) pairs' == map (idDmdSig . fst) pairs
- first_round = n == 1
- (weak_fv, pairs') = step first_round pairs
- final_anal_env = extendAnalEnvs top_lvl env (map fst pairs')
-
- step :: Bool -> [(Id, CoreExpr)] -> (WeakDmds, [(Id, CoreExpr)])
- step first_round pairs = (weak_fv, pairs')
- where
- -- In all but the first iteration, delete the virgin flag
- start_env | first_round = env
- | otherwise = nonVirgin env
-
- start = (extendAnalEnvs top_lvl start_env (map fst pairs), emptyVarEnv)
-
- !((_,!weak_fv), !pairs') = mapAccumL my_downRhs start pairs
- -- mapAccumL: 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
-
- my_downRhs (env, weak_fv) (id,rhs)
- = -- pprTrace "my_downRhs" (ppr id $$ ppr (idDmdSig id) $$ ppr sig) $
- ((env', weak_fv'), (id', rhs'))
- where
- !(!env', !weak_fv1, !id', !rhs') = dmdAnalRhsSig top_lvl Recursive env let_dmd id rhs
- !weak_fv' = plusVarEnv_C plusDmd weak_fv weak_fv1
-
- zapIdDmdSig :: [(Id, CoreExpr)] -> [(Id, CoreExpr)]
- zapIdDmdSig pairs = [(setIdDmdSig id nopSig, rhs) | (id, rhs) <- pairs ]
+ do_one (S2 env weak_fv) (id, rhs) = do
+ -- pprTraceM "my_downRhs" (ppr id $$ ppr (idDmdSig id) $$ ppr sig)
+ S2 env' weak_fv1 <- dmdAnalRhsSig top_lvl Recursive env let_sd id rhs
+ let !weak_fv' = plusVarEnv_C plusDmd weak_fv weak_fv1
+ pure $! S2 env' weak_fv'
{- Note [Safe abortion in the fixed-point iteration]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2349,32 +2318,10 @@ addWeakFVs dmd_ty weak_fvs
-- L demand doesn't get both'd with the Bot coming up from the inner
-- call to f. So we just get an L demand for x for g.
-setBndrsDemandInfo :: HasCallStack => [Var] -> [Demand] -> [Var]
-setBndrsDemandInfo (b:bs) ds
- | isTyVar b = b : setBndrsDemandInfo bs ds
-setBndrsDemandInfo (b:bs) (d:ds) =
- let !new_info = setIdDemandInfo b d
- !vars = setBndrsDemandInfo bs ds
- in new_info : vars
-setBndrsDemandInfo [] ds = assert (null ds) []
-setBndrsDemandInfo bs _ = pprPanic "setBndrsDemandInfo" (ppr bs)
-
-annotateLamIdBndr :: AnalEnv
- -> DmdType -- Demand type of body
- -> Id -- Lambda binder
- -> WithDmdType Id -- Demand type of lambda
- -- and binder annotated with demand
-
-annotateLamIdBndr env dmd_ty id
--- For lambdas we add the demand to the argument demands
--- Only called for Ids
- = assert (isId id) $
- -- pprTrace "annLamBndr" (vcat [ppr id, ppr dmd_ty, ppr final_ty]) $
- WithDmdType main_ty new_id
- where
- new_id = setIdDemandInfo id dmd
- main_ty = addDemand dmd dmd_ty'
- WithDmdType dmd_ty' dmd = findBndrDmd env dmd_ty id
+annotateBndrsDemands :: HasCallStack => [Var] -> [Demand] -> AnalM s ()
+annotateBndrsDemands bs ds =
+ zipWithEqualM_ "annotateBndrsDemands"
+ (annotate da_demands) (filter isRuntimeVar bs) ds
{- Note [NOINLINE and strictness]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2456,7 +2403,6 @@ data AnalEnv = AE
{ ae_opts :: !DmdAnalOpts
-- ^ Analysis options
, ae_sigs :: !SigEnv
- , ae_virgin :: !Bool
-- ^ True on first iteration only. See Note [Initialising strictness]
, ae_fam_envs :: !FamInstEnvs
, ae_rec_dc :: DataCon -> IsRecDataConResult
@@ -2474,15 +2420,13 @@ type SigEnv = VarEnv (DmdSig, TopLevelFlag)
instance Outputable AnalEnv where
ppr env = text "AE" <+> braces (vcat
- [ text "ae_virgin =" <+> ppr (ae_virgin env)
- , text "ae_sigs =" <+> ppr (ae_sigs env)
+ [ text "ae_sigs =" <+> ppr (ae_sigs env)
])
emptyAnalEnv :: DmdAnalOpts -> FamInstEnvs -> AnalEnv
emptyAnalEnv opts fam_envs
= AE { ae_opts = opts
, ae_sigs = emptySigEnv
- , ae_virgin = True
, ae_fam_envs = fam_envs
, ae_rec_dc = memoiseUniqueFun (isRecDataCon fam_envs 3)
}
@@ -2501,13 +2445,13 @@ emptySigEnv :: SigEnv
emptySigEnv = emptyVarEnv
-- | Extend an environment with the strictness sigs attached to the Ids
-extendAnalEnvs :: TopLevelFlag -> AnalEnv -> [Id] -> AnalEnv
-extendAnalEnvs top_lvl env vars
- = env { ae_sigs = extendSigEnvs top_lvl (ae_sigs env) vars }
+extendAnalEnvs :: TopLevelFlag -> AnalEnv -> [Id] -> [DmdSig] -> AnalEnv
+extendAnalEnvs top_lvl env vars sigs
+ = env { ae_sigs = extendSigEnvs top_lvl (ae_sigs env) vars sigs }
-extendSigEnvs :: TopLevelFlag -> SigEnv -> [Id] -> SigEnv
-extendSigEnvs top_lvl sigs vars
- = extendVarEnvList sigs [ (var, (idDmdSig var, top_lvl)) | var <- vars]
+extendSigEnvs :: TopLevelFlag -> SigEnv -> [Id] -> [DmdSig] -> SigEnv
+extendSigEnvs top_lvl env vars sigs
+ = extendVarEnvList env (zipWith (\v s -> (v, (s, top_lvl))) vars sigs)
extendAnalEnv :: TopLevelFlag -> AnalEnv -> Id -> DmdSig -> AnalEnv
extendAnalEnv top_lvl env var sig
@@ -2525,26 +2469,23 @@ addInScopeAnalEnv env id = env { ae_sigs = delVarEnv (ae_sigs env) id }
addInScopeAnalEnvs :: AnalEnv -> [Var] -> AnalEnv
addInScopeAnalEnvs env ids = env { ae_sigs = delVarEnvList (ae_sigs env) ids }
-nonVirgin :: AnalEnv -> AnalEnv
-nonVirgin env = env { ae_virgin = False }
-
findBndrsDmds :: AnalEnv -> DmdType -> [Var] -> WithDmdType [Demand]
-- Return the demands on the Ids in the [Var]
findBndrsDmds env dmd_ty bndrs
= go dmd_ty bndrs
where
- go dmd_ty [] = WithDmdType dmd_ty []
+ go dmd_ty [] = S2 dmd_ty []
go dmd_ty (b:bs)
- | isId b = let WithDmdType dmd_ty1 dmds = go dmd_ty bs
- WithDmdType dmd_ty2 dmd = findBndrDmd env dmd_ty1 b
- in WithDmdType dmd_ty2 (dmd : dmds)
+ | isId b = let S2 dmd_ty1 dmds = go dmd_ty bs
+ S2 dmd_ty2 dmd = findBndrDmd env dmd_ty1 b
+ in S2 dmd_ty2 (dmd : dmds)
| otherwise = go dmd_ty bs
findBndrDmd :: AnalEnv -> DmdType -> Id -> WithDmdType Demand
-- See Note [Trimming a demand to a type]
findBndrDmd env dmd_ty id
= -- pprTrace "findBndrDmd" (ppr id $$ ppr dmd_ty $$ ppr starting_dmd $$ ppr dmd') $
- WithDmdType dmd_ty' dmd'
+ S2 dmd_ty' dmd'
where
dmd' = strictify $
trimToType starting_dmd (findTypeShape fam_envs id_ty)
@@ -2636,6 +2577,7 @@ as strict.
Note [Initialising strictness]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+TODO: Update; now we use DmdAnnotations as state
See section 9.2 (Finding fixpoints) of the paper.
Our basic plan is to initialise the strictness of each Id in a
@@ -2734,3 +2676,28 @@ decrease because we allocate a lot fewer thunks which we immediately overwrite a
also runtime for the pass is faster! Overall, good wins.
-}
+
+---------------------------------
+-- Applying demand annotations --
+---------------------------------
+
+data DmdAnnotations f = DA
+ { da_demands :: !(f (IdEnv Demand))
+ , da_sigs :: !(f (IdEnv DmdSig))
+ }
+
+annotateProgram :: DmdAnnotations Identity -> CoreProgram -> CoreProgram
+annotateProgram anns = runIdentity . traverseBinders (Identity . annotate)
+ where
+ annotate bndr | isTyVar bndr = bndr
+ | otherwise = annotate_sig $ annotate_demand bndr
+ annotate_sig bndr
+ | Just sig <- lookupVarEnv (runIdentity $ da_sigs anns) bndr
+ = bndr `setIdDmdSig` sig
+ | otherwise
+ = bndr
+ annotate_demand bndr
+ | Just dmd <- lookupVarEnv (runIdentity $ da_demands anns) bndr
+ = bndr `setIdDemandInfo` dmd
+ | otherwise
+ = bndr
=====================================
compiler/GHC/Core/Utils.hs
=====================================
@@ -62,6 +62,9 @@ module GHC.Core.Utils (
-- * unsafeEqualityProof
isUnsafeEqualityCase,
+ -- * Traversals
+ traverseBinders,
+
-- * Dumping stuff
dumpIdInfoOfProgram
) where
@@ -2335,6 +2338,28 @@ locBind loc b1 b2 diffs = map addLoc diffs
bindLoc | b1 == b2 = ppr b1
| otherwise = ppr b1 <> char '/' <> ppr b2
+-- | A traversal over all 'CoreBndr's in the given 'CoreProgram'.
+-- Can be instantiated at 'Const' to get a setter.
+traverseBinders :: Applicative f => (CoreBndr -> f CoreBndr) -> CoreProgram -> f CoreProgram
+traverseBinders f = traverse bind
+ where
+ bind (NonRec b rhs) = NonRec <$> f b <*> expr rhs
+ bind (Rec prs) = Rec <$> traverse (\(b, rhs) -> (,) <$> f b <*> expr rhs) prs
+
+ expr e = case e of
+ Var{} -> pure e
+ Lit{} -> pure e
+ Coercion{} -> pure e
+ Type{} -> pure e
+ Tick t e' -> Tick t <$> expr e'
+ Cast e' co -> Cast <$> expr e' <*> pure co
+ Lam b body -> Lam <$> f b <*> expr body
+ App fun arg -> App <$> expr fun <*> expr arg
+ Let bs body -> Let <$> bind bs <*> expr body
+ Case scrut bndr ty alts -> Case <$> expr scrut <*> f bndr <*> pure ty <*> traverse alt alts
+
+ alt (Alt con bndrs rhs) = Alt con <$> traverse f bndrs <*> expr rhs
+{-# INLINE traverseBinders #-}
{- *********************************************************************
* *
=====================================
compiler/GHC/Data/STuple.hs
=====================================
@@ -0,0 +1,44 @@
+-- | Defines a strict tuple data types, such as 'SPair'.
+module GHC.Data.STuple
+ ( SPair(..), swap, toPair, sFirst, sSecond, sUnzip
+ , STriple(..), mapSSndOf3, mapSTrdOf3, toTriple
+ , SQuad(..), toQuad
+ ) where
+
+import GHC.Prelude
+
+-- | Strict pair data type
+data SPair a b = S2 { sFst :: !a, sSnd :: !b }
+
+swap :: SPair a b -> SPair b a
+swap (S2 a b) = (S2 b a)
+
+toPair :: SPair a b -> (a, b)
+toPair (S2 a b) = (a, b)
+
+sFirst :: (a -> a') -> SPair a b -> SPair a' b
+sFirst f (S2 a b) = S2 (f a) b
+
+sSecond :: (b -> b') -> SPair a b -> SPair a b'
+sSecond f (S2 a b) = S2 a (f b)
+
+sUnzip :: [SPair a b] -> SPair [a] [b]
+sUnzip = uncurry S2 . unzip . map toPair
+
+-- | Strict triple data type
+data STriple a b c = S3 { sFstOf3 :: !a, sSndOf3 :: !b, sTrdOf3 :: !c }
+
+mapSSndOf3 :: (b -> b') -> STriple a b c -> STriple a b' c -- feel free to add more as needed
+mapSSndOf3 f (S3 a b c) = S3 a (f b) c
+
+mapSTrdOf3 :: (c -> c') -> STriple a b c -> STriple a b c' -- feel free to add more as needed
+mapSTrdOf3 f (S3 a b c) = S3 a b (f c)
+
+toTriple :: STriple a b c -> (a, b, c)
+toTriple (S3 a b c) = (a, b, c)
+
+-- | Strict quadruple data type
+data SQuad a b c d = S4 { sFstOf4 :: !a, sSndOf4 :: !b, sTrdOf4 :: !c, sFthOf4 :: !d }
+
+toQuad :: SQuad a b c d -> (a, b, c, d)
+toQuad (S4 a b c d) = (a, b, c, d)
=====================================
compiler/GHC/Utils/Misc.hs
=====================================
@@ -15,6 +15,7 @@ module GHC.Utils.Misc (
-- * General list processing
zipEqual, zipWithEqual, zipWith3Equal, zipWith4Equal,
+ zipWithEqualM_,
stretchZipWith, zipWithAndUnzip, zipAndUnzip,
filterByList, filterByLists, partitionByList,
@@ -135,7 +136,7 @@ import qualified Data.List.NonEmpty as NE
import GHC.Exts
import GHC.Stack (HasCallStack)
-import Control.Monad ( guard )
+import Control.Monad ( guard, zipWithM_ )
import Control.Monad.IO.Class ( MonadIO, liftIO )
import System.IO.Error as IO ( isDoesNotExistError )
import System.Directory ( doesDirectoryExist, getModificationTime, renameFile )
@@ -248,11 +249,14 @@ zipWithEqual :: HasDebugCallStack => String -> (a->b->c) -> [a]->[b]->[c]
zipWith3Equal :: HasDebugCallStack => String -> (a->b->c->d) -> [a]->[b]->[c]->[d]
zipWith4Equal :: HasDebugCallStack => String -> (a->b->c->d->e) -> [a]->[b]->[c]->[d]->[e]
+zipWithEqualM_ :: (HasDebugCallStack, Applicative f) => String -> (a->b->f ()) -> [a]->[b]->f ()
+
#if !defined(DEBUG)
zipEqual _ = zip
zipWithEqual _ = zipWith
zipWith3Equal _ = zipWith3
zipWith4Equal _ = List.zipWith4
+zipWithEqualM_ _ = zipWithM_
#else
zipEqual _ [] [] = []
zipEqual msg (a:as) (b:bs) = (a,b) : zipEqual msg as bs
@@ -271,6 +275,10 @@ zipWith4Equal msg z (a:as) (b:bs) (c:cs) (d:ds)
= z a b c d : zipWith4Equal msg z as bs cs ds
zipWith4Equal _ _ [] [] [] [] = []
zipWith4Equal msg _ _ _ _ _ = panic ("zipWith4Equal: unequal lists: "++msg)
+
+zipWithEqualM_ msg z (a:as) (b:bs) = z a b *> zipWithEqualM_ msg z as bs
+zipWithEqualM_ _ _ [] [] = pure ()
+zipWithEqualM_ msg _ _ _ = panic ("zipWithEqualM_: unequal lists: "++msg)
#endif
-- | 'filterByList' takes a list of Bools and a list of some elements and
=====================================
compiler/ghc.cabal.in
=====================================
@@ -432,6 +432,7 @@ Library
GHC.Data.Stream
GHC.Data.Strict
GHC.Data.StringBuffer
+ GHC.Data.STuple
GHC.Data.TrieMap
GHC.Data.Unboxed
GHC.Data.UnionFind
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/bd9bd4540fb47a3548f131bc7de2dd29a63e247b
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/bd9bd4540fb47a3548f131bc7de2dd29a63e247b
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20240104/1cc7e919/attachment-0001.html>
More information about the ghc-commits
mailing list