summaryrefslogtreecommitdiff
path: root/ghc/compiler/simplCore/NewOccurAnal.lhs
diff options
context:
space:
mode:
Diffstat (limited to 'ghc/compiler/simplCore/NewOccurAnal.lhs')
-rw-r--r--ghc/compiler/simplCore/NewOccurAnal.lhs720
1 files changed, 720 insertions, 0 deletions
diff --git a/ghc/compiler/simplCore/NewOccurAnal.lhs b/ghc/compiler/simplCore/NewOccurAnal.lhs
new file mode 100644
index 0000000000..5cfd5637e4
--- /dev/null
+++ b/ghc/compiler/simplCore/NewOccurAnal.lhs
@@ -0,0 +1,720 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1995
+%
+%************************************************************************
+%* *
+\section[NewOccurAnal]{The *New* Occurrence analysis pass}
+%* *
+%************************************************************************
+
+The occurrence analyser analyses the way in which variables are used
+in their scope, and pins that information on the binder. It does {\em
+not} take any strategic decisions about what to do as a result (eg
+discard binding, inline binding etc). That's the job of the
+simplifier.
+
+The occurrence analyser {\em simply} records usage information. That is,
+it pins on each binder info on how that binder occurs in its scope.
+
+Any uses within the RHS of a let(rec) binding for a variable which is
+itself unused are ignored. For example:
+@
+ let x = ...
+ y = ...x...
+ in
+ x+1
+@
+Here, y is unused, so x will be marked as appearing just once.
+
+An exported Id gets tagged as ManyOcc.
+
+IT MUST OBSERVE SCOPING: CANNOT assume unique binders.
+
+Lambdas
+~~~~~~~
+The occurrence analyser marks each binder in a lambda the same way.
+Thus:
+ \ x y -> f y x
+will have both x and y marked as single occurrence, and *not* dangerous-to-dup.
+Technically, x occurs inside a lambda, and therefore *is* dangerous-to-dup,
+but the simplifer very carefully takes care of this special case.
+(See the CoLam case in simplExpr.)
+
+Why? Because typically applications are saturated, in which case x is *not*
+dangerous-to-dup.
+
+Things to muse upon
+~~~~~~~~~~~~~~~~~~~
+
+There *is* a reason not to substitute for
+variables applied to types: it can undo the effect of floating
+Consider:
+\begin{verbatim}
+ c = /\a -> e
+ f = /\b -> let d = c b
+ in \ x::b -> ...
+\end{verbatim}
+Here, inlining c would be a Bad Idea.
+
+At present I've set it up so that the "inside-lambda" flag sets set On
+for type-lambdas too, which effectively prevents such substitutions.
+I don't *think* it disables any interesting ones either.
+
+Oh yes it does.
+Consider
+
+ let { (u6.sAMi, <1,0>) = (_build s141374) ua.sALY } in
+ let {
+ (ua.sAMj, <1,0>) =
+ /\ s141380 -> \ (u5.sAM1, <2,0>) (u6.sAMl, <2,0>) ->
+ let {
+ (u9.sAM7, <2,0>) =
+ \ (u7.sAM2, <3,0>) ->
+ let { (u8.sAM3, <3,0>) = f.sALV u7.sAM2
+ } in u5.sAM1 u8.sAM3
+ } in ((foldr s141374) s141380) u9.sAM7 u6.sAMl u6.sAMi
+ } in (_build s141376) ua.sAMj]
+
+I want to `inline' u6.sAMi, via the foldr/build rule,
+but I cant. So I need to inline through /\. I only do it when
+I've got a `linear' stack, ie actually real arguments still to apply.
+
+\begin{code}
+#include "HsVersions.h"
+
+module NewOccurAnal (
+ newOccurAnalyseBinds, newOccurAnalyseExpr,
+
+ -- and to make the interface self-sufficient...
+ CoreExpr, CoreBinding, Id, BinderInfo, GlobalSwitch,
+ PlainCoreProgram(..), PlainCoreExpr(..),
+ SimplifiableCoreExpr(..), SimplifiableCoreBinding(..)
+ ) where
+
+IMPORT_Trace
+import Outputable -- ToDo: rm; debugging
+import Pretty
+
+import PlainCore -- the stuff we read...
+import TaggedCore -- ... and produce Simplifiable*
+
+import AbsUniType
+import BinderInfo
+import CmdLineOpts ( GlobalSwitch(..), SimplifierSwitch(..) )
+import Digraph ( stronglyConnComp )
+import Id ( eqId, idWantsToBeINLINEd, isConstMethodId,
+ isSpecPragmaId_maybe, getIdArgUsageInfo,
+ SpecInfo
+ )
+import IdInfo -- ( ArgUsage(..), ArgUsageInfo, OptIdInfo(..), getArgUsage)
+import IdEnv
+import Maybes
+import UniqSet
+import Util
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection[OccurAnal-types]{Data types}
+%* *
+%************************************************************************
+
+\begin{code}
+data OccEnv = OccEnv
+ Bool -- Keep-unused-bindings flag
+ -- False <=> OK to chuck away binding
+ -- and ignore occurrences within it
+ Bool -- Keep-spec-pragma-ids flag
+ -- False <=> OK to chuck away spec pragma bindings
+ -- and ignore occurrences within it
+ Bool -- Keep-conjurable flag
+ -- False <=> OK to throw away *dead*
+ -- "conjurable" Ids; at the moment, that
+ -- *only* means constant methods, which
+ -- are top-level. A use of a "conjurable"
+ -- Id may appear out of thin air -- e.g.,
+ -- specialiser conjuring up refs to const
+ -- methods.
+ Bool -- IgnoreINLINEPragma flag
+ -- False <=> OK to use INLINEPragma information
+ -- True <=> ignore INLINEPragma information
+ (UniqSet Id) -- Candidates
+
+addNewCands :: OccEnv -> [Id] -> OccEnv
+addNewCands (OccEnv keep_dead keep_spec keep_conjurable ignore_inline_pragma cands) ids
+ = OccEnv keep_dead keep_spec keep_conjurable ignore_inline_pragma (cands `unionUniqSets` mkUniqSet ids)
+
+addNewCand :: OccEnv -> Id -> OccEnv
+addNewCand (OccEnv keep_dead keep_spec keep_conjurable ignore_inline_pragma cands) id
+ = OccEnv keep_dead keep_spec keep_conjurable ignore_inline_pragma (cands `unionUniqSets` singletonUniqSet id)
+
+isCandidate :: OccEnv -> Id -> Bool
+isCandidate (OccEnv _ _ _ _ cands) id = id `elementOfUniqSet` cands
+
+ignoreINLINEPragma :: OccEnv -> Bool
+ignoreINLINEPragma (OccEnv _ _ _ ignore_inline_pragma _) = ignore_inline_pragma
+
+keepUnusedBinding :: OccEnv -> Id -> Bool
+keepUnusedBinding (OccEnv keep_dead keep_spec keep_conjurable _ _) binder
+ = keep_dead || (keep_spec && is_spec)
+ where
+ is_spec = maybeToBool (isSpecPragmaId_maybe binder)
+
+keepBecauseConjurable :: OccEnv -> Id -> Bool
+keepBecauseConjurable (OccEnv _ _ keep_conjurable _ _) binder
+ = keep_conjurable && is_conjurable
+ where
+ is_conjurable = isConstMethodId binder
+
+type UsageDetails = IdEnv BinderInfo -- A finite map from ids to their usage
+
+combineUsageDetails, combineAltsUsageDetails
+ :: UsageDetails -> UsageDetails -> UsageDetails
+
+combineUsageDetails usage1 usage2
+ = --BSCC("combineUsages")
+ combineIdEnvs combineBinderInfo usage1 usage2
+ --ESCC
+
+combineAltsUsageDetails usage1 usage2
+ = --BSCC("combineUsages")
+ combineIdEnvs combineAltsBinderInfo usage1 usage2
+ --ESCC
+
+addOneOcc :: UsageDetails -> Id -> BinderInfo -> UsageDetails
+addOneOcc usage id info = combineIdEnvs combineBinderInfo usage (unitIdEnv id info)
+ -- ToDo: make this more efficient
+
+emptyDetails = (nullIdEnv :: UsageDetails)
+
+unitDetails id info = (unitIdEnv id info :: UsageDetails)
+
+tagBinders :: UsageDetails -- Of scope
+ -> [Id] -- Binders
+ -> (UsageDetails, -- Details with binders removed
+ [(Id,BinderInfo)]) -- Tagged binders
+
+tagBinders usage binders
+ = (usage `delManyFromIdEnv` binders,
+ [(binder, usage_of usage binder) | binder <- binders]
+ )
+
+tagBinder :: UsageDetails -- Of scope
+ -> Id -- Binders
+ -> (UsageDetails, -- Details with binders removed
+ (Id,BinderInfo)) -- Tagged binders
+
+tagBinder usage binder
+ = (usage `delOneFromIdEnv` binder,
+ (binder, usage_of usage binder)
+ )
+
+usage_of usage binder
+ | isExported binder = ManyOcc 0 -- Exported things count as many
+ | otherwise
+ = case lookupIdEnv usage binder of
+ Nothing -> DeadCode
+ Just info -> info
+
+fixStkToZero :: Id -> UsageDetails -> UsageDetails
+fixStkToZero id env = modifyIdEnv env setBinderInfoArityToZero id
+
+isNeeded env usage binder
+ = case usage_of usage binder of
+ DeadCode -> keepUnusedBinding env binder -- Maybe keep it anyway
+ other -> True
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection[OccurAnal-main]{Counting occurrences: main function}
+%* *
+%************************************************************************
+
+Here's the externally-callable interface:
+
+\begin{code}
+newOccurAnalyseBinds
+ :: [PlainCoreBinding] -- input
+ -> (GlobalSwitch -> Bool)
+ -> (SimplifierSwitch -> Bool)
+ -> [SimplifiableCoreBinding] -- output
+
+newOccurAnalyseBinds binds global_sw_chkr simplifier_sw_chkr
+ | global_sw_chkr D_dump_occur_anal = pprTrace "OccurAnal:" (ppr PprDebug binds') binds'
+ | otherwise = binds'
+ where
+ (_, binds') = do initial_env binds
+
+ initial_env = OccEnv (simplifier_sw_chkr KeepUnusedBindings)
+ (simplifier_sw_chkr KeepSpecPragmaIds)
+ (not (simplifier_sw_chkr SimplMayDeleteConjurableIds))
+ (simplifier_sw_chkr IgnoreINLINEPragma)
+ emptyUniqSet
+
+ do env [] = (emptyDetails, [])
+ do env (bind:binds)
+ = (final_usage, new_binds ++ the_rest)
+ where
+ new_env = env `addNewCands` (bindersOf bind)
+ (binds_usage, the_rest) = do new_env binds
+ (final_usage, new_binds) = --BSCC("occAnalBind1")
+ occAnalBind env bind binds_usage
+ --ESCC
+\end{code}
+
+\begin{code}
+newOccurAnalyseExpr :: UniqSet Id -- Set of interesting free vars
+ -> PlainCoreExpr
+ -> (IdEnv BinderInfo, -- Occ info for interesting free vars
+ SimplifiableCoreExpr)
+
+newOccurAnalyseExpr candidates expr
+ = occAnal initial_env initContext expr
+ where
+ initial_env = OccEnv False {- Drop unused bindings -}
+ False {- Drop SpecPragmaId bindings -}
+ True {- Keep conjurable Ids -}
+ False {- Do not ignore INLINE Pragma -}
+ candidates
+
+newOccurAnalyseGlobalExpr :: PlainCoreExpr -> SimplifiableCoreExpr
+newOccurAnalyseGlobalExpr expr
+ = -- Top level expr, so no interesting free vars, and
+ -- discard occurence info returned
+ expr' where (_, expr') = newOccurAnalyseExpr emptyUniqSet expr
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[OccurAnal-main]{Counting occurrences: main function}
+%* *
+%************************************************************************
+
+Bindings
+~~~~~~~~
+
+\begin{code}
+occAnalBind :: OccEnv
+ -> PlainCoreBinding
+ -> UsageDetails -- Usage details of scope
+ -> (UsageDetails, -- Of the whole let(rec)
+ [SimplifiableCoreBinding])
+
+occAnalBind env (CoNonRec binder rhs) body_usage
+ | isNeeded env body_usage binder -- It's mentioned in body
+ = (final_body_usage `combineUsageDetails` rhs_usage,
+ [CoNonRec tagged_binder rhs'])
+
+ | otherwise
+ = (body_usage, [])
+
+ where
+ stk = mkContextFromBinderInfo (usage_of body_usage binder)
+ (rhs_usage, rhs') = occAnalRhs env binder stk rhs
+ (final_body_usage, tagged_binder) = tagBinder body_usage binder
+
+occAnalBind env (CoRec [(binder,rhs)]) body_usage
+ | getContextSize after_stk < getContextSize stk && mentions_itself
+ -- our pre-condition does not hold!
+ -- so, we have to go back, and
+ -- *make* of pre-condition hold.
+ -- Will, you can leave out this trace
+ = {-pprTrace ("after_stk < stk (BAD, BAD, VERY VERY BAD):"
+ ++ show (getContextSize after_stk,getContextSize stk)) (ppr PprDebug binder) -}
+ (occAnalBind env (CoRec [(binder,rhs)]) (fixStkToZero binder body_usage))
+
+ | isNeeded env body_usage binder -- It's mentioned in body
+ = --BSCC("occAnalBindC")
+ (final_usage, [final_bind])
+ --ESCC
+
+ | otherwise
+ = --BSCC("occAnalBindD")
+ (body_usage, [])
+ --ESCC
+
+ where
+ stk = shareContext (mkContextFromBinderInfo (usage_of body_usage binder))
+ new_env = env `addNewCand` binder
+ (rhs_usage, rhs') = occAnalRhs new_env binder stk rhs
+ total_usage = combineUsageDetails body_usage rhs_usage
+ (final_usage, tagged_binder) = tagBinder total_usage binder
+
+ after_stk = mkContextFromBinderInfo (usage_of rhs_usage binder)
+
+ final_bind = if mentions_itself
+ then CoRec [(tagged_binder,rhs')]
+ else CoNonRec tagged_binder rhs'
+
+ mentions_itself = maybeToBool (lookupIdEnv rhs_usage binder)
+\end{code}
+
+Dropping dead code for recursive bindings is done in a very simple way:
+
+ the entire set of bindings is dropped if none of its binders are
+ mentioned in its body; otherwise none are.
+
+This seems to miss an obvious improvement.
+@
+ letrec f = ...g...
+ g = ...f...
+ in
+ ...g...
+
+===>
+
+ letrec f = ...g...
+ g = ...(...g...)...
+ in
+ ...g...
+@
+
+Now @f@ is unused. But dependency analysis will sort this out into a
+@letrec@ for @g@ and a @let@ for @f@, and then @f@ will get dropped.
+It isn't easy to do a perfect job in one blow. Consider
+
+@
+ letrec f = ...g...
+ g = ...h...
+ h = ...k...
+ k = ...m...
+ m = ...m...
+ in
+ ...m...
+@
+
+
+\begin{code}
+occAnalBind env (CoRec pairs) body_usage
+ = foldr do_final_bind (body_usage, []) sccs
+ where
+
+ (binders, rhss) = unzip pairs
+ new_env = env `addNewCands` binders
+
+ analysed_pairs :: [(Id, (UsageDetails, SimplifiableCoreExpr))]
+ analysed_pairs = [(id, occAnalRhs new_env id initContext rhs) | (id,rhs) <- pairs]
+
+ lookup :: Id -> (UsageDetails, SimplifiableCoreExpr)
+ lookup id = assoc "occAnalBind:lookup" analysed_pairs id
+
+
+ ---- stuff for dependency analysis of binds -------------------------------
+
+ edges :: [(Id,Id)] -- (a,b) means a mentions b
+ edges = concat [ edges_from binder rhs_usage
+ | (binder, (rhs_usage, _)) <- analysed_pairs]
+
+ edges_from :: Id -> UsageDetails -> [(Id,Id)]
+ edges_from id its_rhs_usage
+ = [(id,mentioned) | mentioned <- binders,
+ maybeToBool (lookupIdEnv its_rhs_usage mentioned)
+ ]
+
+ sccs :: [[Id]]
+ sccs = case binders of
+ [_] -> [binders] -- Singleton; no need to analyse
+ other -> stronglyConnComp eqId edges binders
+
+ ---- stuff to "re-constitute" bindings from dependency-analysis info ------
+
+ do_final_bind sCC@[binder] (body_usage, binds_so_far)
+ | isNeeded env body_usage binder
+ = (combined_usage, new_bind:binds_so_far)
+
+ | otherwise -- Dead
+ = (body_usage, binds_so_far)
+ where
+ total_usage = combineUsageDetails body_usage rhs_usage
+ (rhs_usage, rhs') = lookup binder
+ (combined_usage, tagged_binder) = tagBinder total_usage binder
+
+ new_bind
+ | mentions_itself binder rhs_usage = CoRec [(tagged_binder,rhs')]
+ | otherwise = CoNonRec tagged_binder rhs'
+ where
+ mentions_itself binder usage
+ = maybeToBool (lookupIdEnv usage binder)
+
+ do_final_bind sCC (body_usage, binds_so_far)
+ | any (isNeeded env body_usage) sCC
+ = (combined_usage, new_bind:binds_so_far)
+
+ | otherwise -- Dead
+ = (body_usage, binds_so_far)
+ where
+ (rhs_usages, rhss') = unzip (map lookup sCC)
+ total_usage = foldr combineUsageDetails body_usage rhs_usages
+ (combined_usage, tagged_binders) = tagBinders total_usage sCC
+
+ new_bind = CoRec (tagged_binders `zip` rhss')
+\end{code}
+
+@occAnalRhs@ deals with the question of bindings where the Id is marked
+by an INLINE pragma. For these we record that anything which occurs
+in its RHS occurs many times. This pessimistically assumes that ths
+inlined binder also occurs many times in its scope, but if it doesn't
+we'll catch it next time round. At worst this costs an extra simplifier pass.
+ToDo: try using the occurrence info for the inline'd binder.
+
+\begin{code}
+occAnalRhs :: OccEnv
+ -> Id -- Binder
+ -> Context -- Stack Style Context
+ -> PlainCoreExpr -- Rhs
+ -> (UsageDetails, SimplifiableCoreExpr)
+
+occAnalRhs env id stk rhs
+ | idWantsToBeINLINEd id && not (ignoreINLINEPragma env)
+ = (mapIdEnv markMany rhs_usage, rhs')
+
+ | otherwise
+ = (rhs_usage, rhs')
+
+ where
+ (rhs_usage, rhs') = occAnal env stk rhs
+\end{code}
+
+Expressions
+~~~~~~~~~~~
+\begin{code}
+occAnal :: OccEnv
+ -> Context
+ -> PlainCoreExpr
+ -> (UsageDetails, -- Gives info only about the "interesting" Ids
+ SimplifiableCoreExpr)
+
+occAnal env stk (CoVar v)
+ | isCandidate env v
+ = (unitIdEnv v (funOccurrence (getContextSize stk)), CoVar v)
+
+ | otherwise
+ = (emptyDetails, CoVar v)
+
+occAnal env _ (CoLit lit) = (emptyDetails, CoLit lit)
+-- PERHAPS ASSERT THAT STACK == 0 ?
+occAnal env _ (CoCon con tys args) = (occAnalAtoms env args, CoCon con tys args)
+occAnal env _ (CoPrim op tys args) = (occAnalAtoms env args, CoPrim op tys args)
+
+occAnal env stk (CoSCC lbl body)
+ = (mapIdEnv markInsideSCC usage, CoSCC lbl body')
+ where
+ (usage, body') = occAnal env initContext body
+
+occAnal env stk (CoApp fun arg)
+ = occAnalApp env (incContext stk) [ValArg arg] fun
+occAnal env stk (CoTyApp fun arg)
+ = occAnalApp env stk [TypeArg arg] fun
+{-
+occAnal env (CoApp fun arg)
+ = (fun_usage `combineUsageDetails` arg_usage, CoApp fun' arg)
+ where
+ (fun_usage, fun') = occAnal env fun
+ arg_usage = occAnalAtom env arg
+
+occAnal env (CoTyApp fun ty)
+ = (fun_usage, CoTyApp fun' ty)
+ where
+ (fun_usage, fun') = occAnal env fun
+-}
+occAnal env stk (CoLam binders body) | isLinContext stk
+ = (final_usage, mkCoLam tagged_binders body')
+ where
+ (lin_binders,other_binders) = splitAt (getContextSize stk) binders
+ new_env = env `addNewCands` lin_binders
+ (body_usage, body') = occAnal new_env (lamOnContext stk (length lin_binders))
+ (mkCoLam other_binders body)
+ (final_usage, tagged_binders) = tagBinders body_usage lin_binders
+
+occAnal env stk (CoLam binders body)
+ = (mapIdEnv markDangerousToDup final_usage, mkCoLam tagged_binders body')
+ where
+ new_env = env `addNewCands` binders
+ (body_usage, body') = occAnal new_env (lamOnContext stk (length binders)) body
+ (final_usage, tagged_binders) = tagBinders body_usage binders
+
+{-
+occAnal env (CoLam binders body)
+ = (mapIdEnv markDangerousToDup final_usage, mkCoLam tagged_binders body')
+ where
+ new_env = env `addNewCands` binders
+ (body_usage, body') = occAnal new_env body
+ (final_usage, tagged_binders) = tagBinders body_usage binders
+-}
+
+occAnal env stk (CoTyLam tyvar body)
+ = (new_body_usage, CoTyLam tyvar body')
+ where
+ (body_usage, body') = occAnal env stk body
+ new_body_usage = if isLinContext stk
+ then body_usage
+ else mapIdEnv markDangerousToDup body_usage
+
+occAnal env stk (CoCase scrut alts)
+ = (scrut_usage `combineUsageDetails` alts_usage,
+ CoCase scrut' alts')
+ where
+ (scrut_usage, scrut') = occAnal env initContext scrut
+ (alts_usage, alts') = occAnalAlts env stk alts
+
+
+occAnal env stk (CoLet bind body)
+ = (final_usage , foldr CoLet body' new_binds) -- mkCoLets* wants PlainCore... (sigh)
+ where
+ new_env = env `addNewCands` (bindersOf bind)
+ (body_usage, body') = occAnal new_env stk {- ?? -} body
+ (final_usage, new_binds) = --BSCC("occAnalBind2")
+ occAnalBind env bind body_usage
+ --ESCC
+\end{code}
+
+Case alternatives
+~~~~~~~~~~~~~~~~~
+\begin{code}
+occAnalAlts env stk (CoAlgAlts alts deflt)
+ = (foldr combineAltsUsageDetails deflt_usage alts_usage,
+ -- Note: combine*Alts*UsageDetails...
+ CoAlgAlts alts' deflt')
+ where
+ (alts_usage, alts') = unzip (map do_alt alts)
+ (deflt_usage, deflt') = occAnalDeflt env stk deflt
+
+ do_alt (con, args, rhs)
+ = (final_usage, (con, tagged_args, rhs'))
+ where
+ new_env = env `addNewCands` args
+ (rhs_usage, rhs') = occAnal new_env stk rhs
+ (final_usage, tagged_args) = tagBinders rhs_usage args
+
+occAnalAlts env stk (CoPrimAlts alts deflt)
+ = (foldr combineAltsUsageDetails deflt_usage alts_usage,
+ -- Note: combine*Alts*UsageDetails...
+ CoPrimAlts alts' deflt')
+ where
+ (alts_usage, alts') = unzip (map do_alt alts)
+ (deflt_usage, deflt') = occAnalDeflt env stk deflt
+
+ do_alt (lit, rhs)
+ = (rhs_usage, (lit, rhs'))
+ where
+ (rhs_usage, rhs') = occAnal env stk rhs
+
+occAnalDeflt env stk CoNoDefault = (emptyDetails, CoNoDefault)
+
+occAnalDeflt env stk (CoBindDefault binder rhs)
+ = (final_usage, CoBindDefault tagged_binder rhs')
+ where
+ new_env = env `addNewCand` binder
+ (rhs_usage, rhs') = occAnal new_env stk rhs
+ (final_usage, tagged_binder) = tagBinder rhs_usage binder
+\end{code}
+
+
+Atoms
+~~~~~
+\begin{code}
+occAnalAtoms :: OccEnv -> [PlainCoreAtom] -> UsageDetails
+
+occAnalAtoms env atoms
+ = foldr do_one_atom emptyDetails atoms
+ where
+ do_one_atom (CoLitAtom lit) usage = usage
+ do_one_atom (CoVarAtom v) usage
+ | isCandidate env v = addOneOcc usage v (argOccurrence 0)
+ | otherwise = usage
+
+
+occAnalArgAtoms :: OccEnv -> [(PlainCoreAtom,ArgUsage)] -> UsageDetails
+occAnalArgAtoms env atoms
+ = foldr do_one_atom emptyDetails atoms
+ where
+ do_one_atom (CoLitAtom lit,_) usage = usage
+ do_one_atom (CoVarAtom v,ArgUsage ar) usage
+ | isCandidate env v = addOneOcc usage v (argOccurrence ar)
+ | otherwise = usage
+ do_one_atom (CoVarAtom v,UnknownArgUsage) usage
+ | isCandidate env v = addOneOcc usage v (argOccurrence 0)
+ | otherwise = usage
+
+occAnalAtom :: OccEnv -> PlainCoreAtom -> UsageDetails
+
+occAnalAtom env (CoLitAtom lit) = emptyDetails
+occAnalAtom env (CoVarAtom v)
+ | isCandidate env v = unitDetails v (argOccurrence 0)
+ | otherwise = emptyDetails
+--
+-- This function looks for (fully) applied calls to special ids.
+--
+occAnalApp
+ :: OccEnv
+ -> Context
+ -> [PlainCoreArg]
+ -> PlainCoreExpr
+ -> (UsageDetails, -- Gives info only about the "interesting" Ids
+ SimplifiableCoreExpr)
+occAnalApp env stk args fun@(CoVar v)
+ | not (null aut)
+ && getContextSize stk >= length aut -- fully applied
+ = (fun_usage `combineUsageDetails` arg_usages,
+ applyToArgs fun' args)
+ where
+ val_args = [ x | ValArg x <- args ]
+ aut = getArgUsage (getIdArgUsageInfo v)
+ (fun_usage, fun') = occAnal env stk fun
+ arg_usages = occAnalArgAtoms env (zip val_args aut)
+occAnalApp env stk args (CoApp fun arg)
+ = occAnalApp env (incContext stk) (ValArg arg:args) fun
+occAnalApp env stk args (CoTyApp fun arg)
+ = occAnalApp env stk (TypeArg arg:args) fun
+occAnalApp env stk args fun
+ = (fun_usage `combineUsageDetails` arg_usages,
+ applyToArgs fun' args)
+ where
+ (fun_usage, fun') = occAnal env stk fun
+ arg_usages = occAnalAtoms env val_args
+ val_args = [ x | ValArg x <- args ]
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[OccurAnal-main]{Counting occurrences: main function}
+%* *
+%************************************************************************
+
+
+Abstract, but simple rep. for stacks.
+\begin{code}
+data Context = Context Int Bool -- if b then n > 0
+
+lamOnContext :: Context -> Int -> Context
+lamOnContext (Context n b) i = mkContext (max 0 (n - i)) b
+
+isLinContext :: Context -> Bool
+isLinContext (Context n b) = b
+
+getContextSize :: Context -> Int
+getContextSize (Context n b) = n
+
+incContext :: Context -> Context
+incContext (Context n u) = Context (n + 1) u
+
+initContext :: Context
+initContext = Context 0 False
+
+shareContext :: Context -> Context
+shareContext (Context n u) = mkContext n False
+
+mkContext :: Int -> Bool -> Context
+mkContext 0 _ = Context 0 False
+mkContext i b = Context i b
+
+mkContextFromBinderInfo :: BinderInfo -> Context
+mkContextFromBinderInfo (DeadCode) = mkContext 0 False
+mkContextFromBinderInfo (ManyOcc i) = mkContext i False
+mkContextFromBinderInfo bi@(OneOcc _ _ _ _ i)
+ = mkContext i (oneSafeOcc True bi)
+\end{code}
+