[project @ 2005-03-07 16:46:08 by simonpj]
[ghc-hetmet.git] / ghc / compiler / simplCore / OccurAnal.lhs
index a05f907..8b6c5bb 100644 (file)
@@ -1,5 +1,5 @@
 %
-% (c) The GRASP/AQUA Project, Glasgow University, 1992-1996
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
 %
 %************************************************************************
 %*                                                                     *
@@ -11,179 +11,33 @@ The occurrence analyser re-typechecks a core expression, returning a new
 core expression with (hopefully) improved usage information.
 
 \begin{code}
-#include "HsVersions.h"
-
 module OccurAnal (
-       occurAnalyseBinds, occurAnalyseExpr, occurAnalyseGlobalExpr
+       occurAnalysePgm, occurAnalyseGlobalExpr, occurAnalyseRule, 
     ) where
 
-IMP_Ubiq(){-uitous-}
-IMPORT_1_3(List(partition))
+#include "HsVersions.h"
 
-import BinderInfo
-import CmdLineOpts     ( opt_D_dump_occur_anal, SimplifierSwitch(..) )
 import CoreSyn
-import Digraph         ( stronglyConnComp, stronglyConnCompR, SCC(..) )
-import Id              ( idWantsToBeINLINEd, addNoInlinePragma, nukeNoInlinePragma,
-                         idType, idUnique, SYN_IE(Id),
-                         isConstMethodId,
-                         emptyIdSet, unionIdSets, mkIdSet,
-                         unitIdSet, elementOfIdSet,
-                         addOneToIdSet, SYN_IE(IdSet),
-                         nullIdEnv, unitIdEnv, combineIdEnvs,
-                         delOneFromIdEnv, delManyFromIdEnv, isNullIdEnv, 
-                         mapIdEnv, lookupIdEnv, SYN_IE(IdEnv), 
-                         GenId{-instance Eq-}
+import CoreFVs         ( idRuleVars )
+import CoreUtils       ( exprIsTrivial )
+import Id              ( isDataConWorkId, isOneShotBndr, setOneShotLambda, 
+                         idOccInfo, setIdOccInfo,
+                         isExportedId, idArity, idSpecialisation, 
+                         idType, idUnique, Id
                        )
-import Name            ( isExported, isLocallyDefined )
-import Type            ( getFunTy_maybe, splitForAllTy )
-import Maybes          ( maybeToBool )
-import Outputable      ( PprStyle(..), Outputable(..){-instance * (,) -} )
-import PprCore
-import PprType         ( GenType{-instance Outputable-}, GenTyVar{-ditto-} )
-import Pretty          ( Doc, vcat, ptext, nest, punctuate, comma, hcat, text )
-import TyVar           ( GenTyVar{-instance Eq-} )
-import Unique          ( Unique{-instance Eq-}, u2i )
-import UniqFM          ( keysUFM ) 
-import Util            ( assoc, zipEqual, zipWithEqual, Ord3(..)
-                       , pprTrace, panic 
-#ifdef DEBUG
-                       , assertPanic
-#endif
-                       )
-
-isSpecPragmaId_maybe x = Nothing -- ToDo:!trace "OccurAnal.isSpecPragmaId_maybe"
-\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
-
-    (Id -> IdSet -> Bool)      -- Tells whether an Id occurrence is interesting,
-                               -- given the set of in-scope variables
-
-    IdSet      -- In-scope Ids
-
-
-addNewCands :: OccEnv -> [Id] -> OccEnv
-addNewCands (OccEnv kd ks kc ip ifun cands) ids
-  = OccEnv kd ks kc ip ifun (cands `unionIdSets` mkIdSet ids)
-
-addNewCand :: OccEnv -> Id -> OccEnv
-addNewCand (OccEnv ks kd kc ip ifun cands) id
-  = OccEnv kd ks kc ip ifun (addOneToIdSet cands id)
-
-isCandidate :: OccEnv -> Id -> Bool
-isCandidate (OccEnv _ _ _ _ ifun cands) id = ifun id cands
-
-inlineMe :: OccEnv -> Id -> Bool
-inlineMe env id
-  = {- See comments with simplIdWantsToBeINLINEd in SimplUtils.lhs 
-       not ignore_inline_prag && 
-    -}
-    idWantsToBeINLINEd id
-
-keepUnusedBinding :: OccEnv -> Id -> Bool
-keepUnusedBinding (OccEnv keep_dead keep_spec keep_conjurable _ _ _) binder
-  = keep_dead || (keep_spec && maybeToBool (isSpecPragmaId_maybe binder))
-
-keepBecauseConjurable :: OccEnv -> Id -> Bool
-keepBecauseConjurable (OccEnv _ _ keep_conjurable _ _ _) binder
-  = keep_conjurable && isConstMethodId binder
-
-type UsageDetails = IdEnv BinderInfo   -- A finite map from ids to their usage
-
-combineUsageDetails, combineAltsUsageDetails
-       :: UsageDetails -> UsageDetails -> UsageDetails
-
-combineUsageDetails usage1 usage2
-  = combineIdEnvs addBinderInfo usage1 usage2
-
-combineAltsUsageDetails usage1 usage2
-  = combineIdEnvs orBinderInfo usage1 usage2
-
-addOneOcc :: UsageDetails -> Id -> BinderInfo -> UsageDetails
-addOneOcc usage id info
-  = combineIdEnvs addBinderInfo 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 =
- let
-  usage' = usage `delManyFromIdEnv` binders
-  uss    = [ (binder, usage_of usage binder) | binder <- binders ]
- in
- if isNullIdEnv usage' then
-    (usage', uss)
- else
-    (usage', uss)
-{-
-  = (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 =
- let
-   usage'  = usage `delOneFromIdEnv` binder
-   us      = usage_of usage binder 
-   cont =
-    if isNullIdEnv usage' then  -- bogus test to force evaluation.
-       (usage', (binder, us))
-    else
-       (usage', (binder, us))
- in
- case us of { DeadCode -> cont; _ -> cont }
-
---   (binder, usage_of usage binder)
-
-
-usage_of usage binder
-  | isExported binder = ManyOcc 0 -- Visible-elsewhere things count as many
-  | otherwise
-  = case (lookupIdEnv usage binder) of
-      Nothing   -> DeadCode
-      Just info -> info
-
-isNeeded env usage binder
-  = case (usage_of usage binder) of
-      DeadCode  -> keepUnusedBinding env binder        -- Maybe keep it anyway
-      other     -> True
+import BasicTypes      ( OccInfo(..), isOneOcc )
+
+import VarSet
+import VarEnv
+
+import Type            ( isFunTy, dropForAlls )
+import Maybes          ( orElse )
+import Digraph         ( stronglyConnCompR, SCC(..) )
+import PrelNames       ( buildIdKey, foldrIdKey, runSTRepIdKey, augmentIdKey )
+import Unique          ( Unique )
+import UniqFM          ( keysUFM )  
+import Util            ( zipWithEqual, mapAndUnzip )
+import Outputable
 \end{code}
 
 
@@ -196,71 +50,37 @@ isNeeded env usage binder
 Here's the externally-callable interface:
 
 \begin{code}
-occurAnalyseBinds
-       :: [CoreBinding]                -- input
-       -> (SimplifierSwitch -> Bool)
-       -> [SimplifiableCoreBinding]    -- output
-
-occurAnalyseBinds binds simplifier_sw_chkr
-  | opt_D_dump_occur_anal = pprTrace "OccurAnal:"
-                                    (vcat (map ppr_bind binds'))
-                                    binds'
-  | otherwise            = binds'
+occurAnalysePgm :: [CoreBind] -> [CoreBind]
+occurAnalysePgm binds
+  = snd (go (initOccEnv emptyVarSet) binds)
   where
-    (_, binds') = doo initial_env binds
-
-    initial_env = OccEnv (simplifier_sw_chkr KeepUnusedBindings)
-                        (simplifier_sw_chkr KeepSpecPragmaIds)
-                        (not (simplifier_sw_chkr SimplMayDeleteConjurableIds))
-                        (simplifier_sw_chkr IgnoreINLINEPragma)
-                        (\id in_scope -> isLocallyDefined id)  -- Anything local is interesting
-                        emptyIdSet                             -- Not actually used
-
-    doo env [] = (emptyDetails, [])
-    doo env (bind:binds)
-      = (final_usage, new_binds ++ the_rest)
-      where
-       new_env                  = env `addNewCands` (bindersOf bind)
-       (binds_usage, the_rest)  = doo new_env binds
-       (final_usage, new_binds) = occAnalBind env bind binds_usage
-
-       -- This really ought to be done properly by PprCore, but
-       -- it isn't.  pprCoreBinding only works on Id binders, and
-       -- the general case is complicated by the fact that it has to work
-       -- for interface files too.  Sigh
-
-ppr_bind bind@(NonRec binder expr)
-  = ppr PprDebug bind
-
-ppr_bind bind@(Rec binds)
-  = vcat [ptext SLIT("Rec {"),
-             nest 2 (ppr PprDebug bind),
-             ptext SLIT("end Rec }")]
-\end{code}
-
-\begin{code}
-occurAnalyseExpr :: (Id -> Bool)       -- Tells if a variable is interesting
-                -> CoreExpr
-                -> (IdEnv BinderInfo,  -- Occ info for interesting free vars
-                    SimplifiableCoreExpr)
+    go :: OccEnv -> [CoreBind] -> (UsageDetails, [CoreBind])
+    go env [] 
+       = (emptyDetails, [])
+    go env (bind:binds) 
+       = (final_usage, bind' ++ binds')
+       where
+          new_env              = env `addNewCands` (bindersOf bind)
+          (bs_usage, binds')   = go new_env binds
+          (final_usage, bind') = occAnalBind env bind bs_usage
 
-occurAnalyseExpr interesting expr
-  = occAnal initial_env expr
-  where
-    initial_env = OccEnv False {- Drop unused bindings -}
-                        False {- Drop SpecPragmaId bindings -}
-                        True  {- Keep conjurable Ids -}
-                        False {- Do not ignore INLINE Pragma -}
-                        (\id locals -> interesting id || elementOfIdSet id locals)
-                        emptyIdSet
-
-occurAnalyseGlobalExpr :: CoreExpr -> SimplifiableCoreExpr
+occurAnalyseGlobalExpr :: CoreExpr -> CoreExpr
 occurAnalyseGlobalExpr expr
   =    -- Top level expr, so no interesting free vars, and
        -- discard occurence info returned
-    snd (occurAnalyseExpr (\_ -> False) expr)
+    snd (occAnal (initOccEnv emptyVarSet) expr)
+
+occurAnalyseRule :: CoreRule -> CoreRule
+occurAnalyseRule rule@(BuiltinRule _ _) = rule
+occurAnalyseRule (Rule str act tpl_vars tpl_args rhs)
+               -- Add occ info to tpl_vars, rhs
+  = Rule str act tpl_vars' tpl_args rhs'
+  where
+    (rhs_uds, rhs') = occAnal (initOccEnv (mkVarSet tpl_vars)) rhs
+    (_, tpl_vars')  = tagBinders rhs_uds tpl_vars
 \end{code}
 
+
 %************************************************************************
 %*                                                                     *
 \subsection[OccurAnal-main]{Counting occurrences: main function}
@@ -271,30 +91,31 @@ Bindings
 ~~~~~~~~
 
 \begin{code}
-type Node details = (details, Int, [Int])      -- The Ints are gotten from the Unique,
+type IdWithOccInfo = Id                        -- An Id with fresh PragmaInfo attached
+
+type Node details = (details, Unique, [Unique])        -- The Ints are gotten from the Unique,
                                                -- which is gotten from the Id.
-type Details1    = (Id, (UsageDetails, SimplifiableCoreExpr))
-type Details2    = ((Id, BinderInfo), SimplifiableCoreExpr)
+type Details1    = (Id, UsageDetails, CoreExpr)
+type Details2    = (IdWithOccInfo, CoreExpr)
 
 
 occAnalBind :: OccEnv
-           -> CoreBinding
+           -> CoreBind
            -> UsageDetails             -- Usage details of scope
            -> (UsageDetails,           -- Of the whole let(rec)
-               [SimplifiableCoreBinding])
+               [CoreBind])
 
 occAnalBind env (NonRec binder rhs) body_usage
-  | isNeeded env body_usage binder             -- It's mentioned in body
+  | not (binder `usedIn` body_usage)           -- It's not mentioned
+  = (body_usage, [])
+
+  | otherwise                  -- It's mentioned in the body
   = (final_body_usage `combineUsageDetails` rhs_usage,
      [NonRec tagged_binder rhs'])
 
-  | otherwise                  -- Not mentioned, so drop dead code
-  = (body_usage, [])
-
   where
-    binder'                          = nukeNoInlinePragma binder
-    (rhs_usage, rhs')                = occAnalRhs env binder' rhs
-    (final_body_usage, tagged_binder) = tagBinder body_usage binder'
+    (final_body_usage, tagged_binder) = tagBinder body_usage binder
+    (rhs_usage, rhs')                = occAnalRhs env tagged_binder rhs
 \end{code}
 
 Dropping dead code for recursive bindings is done in a very simple way:
@@ -336,15 +157,14 @@ It isn't easy to do a perfect job in one blow.  Consider
 occAnalBind env (Rec pairs) body_usage
   = foldr (_scc_ "occAnalBind.dofinal" do_final_bind) (body_usage, []) sccs
   where
-    pp_scc (CyclicSCC cycle) = hcat [text "Cyclic ", hcat (punctuate comma (map pp_item cycle))]
-    pp_scc (AcyclicSCC item) = hcat [text "Acyclic ", pp_item item]
-    pp_item (_, bndr, _)     = ppr PprDebug bndr
-
     binders = map fst pairs
-    new_env = env `addNewCands` binders
+    rhs_env = env `addNewCands` binders
 
     analysed_pairs :: [Details1]
-    analysed_pairs  = [(nukeNoInlinePragma bndr, occAnalRhs new_env bndr rhs) | (bndr,rhs) <- pairs]
+    analysed_pairs  = [ (bndr, rhs_usage, rhs')
+                     | (bndr, rhs) <- pairs,
+                       let (rhs_usage, rhs') = occAnalRhs rhs_env bndr rhs
+                     ]
 
     sccs :: [SCC (Node Details1)]
     sccs = _scc_ "occAnalBind.scc" stronglyConnCompR edges
@@ -353,8 +173,8 @@ occAnalBind env (Rec pairs) body_usage
     ---- stuff for dependency analysis of binds -------------------------------
     edges :: [Node Details1]
     edges = _scc_ "occAnalBind.assoc"
-           [ (pair, IBOX(u2i (idUnique id)), edges_from rhs_usage)
-           | pair@(id, (rhs_usage, rhs)) <- analysed_pairs
+           [ (details, idUnique id, edges_from rhs_usage)
+           | details@(id, rhs_usage, rhs) <- analysed_pairs
            ]
 
        -- (a -> b) means a mentions b
@@ -363,21 +183,21 @@ occAnalBind env (Rec pairs) body_usage
        -- by just extracting the keys from the finite map.  Grimy, but fast.
        -- Previously we had this:
        --      [ bndr | bndr <- bndrs,
-       --               maybeToBool (lookupIdEnv rhs_usage bndr)]
+       --               maybeToBool (lookupVarEnv rhs_usage bndr)]
        -- which has n**2 cost, and this meant that edges_from alone 
        -- consumed 10% of total runtime!
-    edges_from :: UsageDetails -> [Int]
+    edges_from :: UsageDetails -> [Unique]
     edges_from rhs_usage = _scc_ "occAnalBind.edges_from"
                           keysUFM rhs_usage
 
     ---- stuff to "re-constitute" bindings from dependency-analysis info ------
 
        -- Non-recursive SCC
-    do_final_bind (AcyclicSCC ((bndr, (rhs_usage, rhs')), _, _)) (body_usage, binds_so_far)
-      | isNeeded env body_usage bndr
-      = (combined_usage, new_bind : binds_so_far)      
-      | otherwise
+    do_final_bind (AcyclicSCC ((bndr, rhs_usage, rhs'), _, _)) (body_usage, binds_so_far)
+      | not (bndr `usedIn` body_usage)
       = (body_usage, binds_so_far)                     -- Dead code
+      | otherwise
+      = (combined_usage, new_bind : binds_so_far)      
       where
        total_usage                   = combineUsageDetails body_usage rhs_usage
        (combined_usage, tagged_bndr) = tagBinder total_usage bndr
@@ -385,20 +205,20 @@ occAnalBind env (Rec pairs) body_usage
 
        -- Recursive SCC
     do_final_bind (CyclicSCC cycle) (body_usage, binds_so_far)
-      | any (isNeeded env body_usage) bndrs
-      = (combined_usage, final_bind:binds_so_far)
-      | otherwise
+      | not (any (`usedIn` body_usage) bndrs)          -- NB: look at body_usage, not total_usage
       = (body_usage, binds_so_far)                     -- Dead code
+      | otherwise
+      = (combined_usage, final_bind:binds_so_far)
       where
-       pairs                            = [pair      | (pair, _, _) <- cycle]
-       bndrs                            = [bndr      | (bndr, _)           <- pairs]
-       rhs_usages                       = [rhs_usage | (_, (rhs_usage, _)) <- pairs]
-       total_usage                      = foldr combineUsageDetails body_usage rhs_usages
-       (combined_usage, tagged_binders) = tagBinders total_usage bndrs
-       final_bind                       = Rec (reOrderRec env new_cycle)
-
-       new_cycle = CyclicSCC (zipWithEqual "occAnalBind" mk_new_bind tagged_binders cycle)
-       mk_new_bind tagged_bndr ((_, (_, rhs')), key, keys) = ((tagged_bndr, rhs'), key, keys)
+       details                        = [details   | (details, _, _) <- cycle]
+       bndrs                          = [bndr      | (bndr, _, _)      <- details]
+       rhs_usages                     = [rhs_usage | (_, rhs_usage, _) <- details]
+       total_usage                    = foldr combineUsageDetails body_usage rhs_usages
+       (combined_usage, tagged_bndrs) = tagBinders total_usage bndrs
+       final_bind                     = Rec (reOrderRec env new_cycle)
+
+       new_cycle = CyclicSCC (zipWithEqual "occAnalBind" mk_new_bind tagged_bndrs cycle)
+       mk_new_bind tagged_bndr ((_, _, rhs'), key, keys) = ((tagged_bndr, rhs'), key, keys)
 \end{code}
 
 @reOrderRec@ is applied to the list of (binder,rhs) pairs for a cyclic
@@ -416,6 +236,10 @@ on the no-inline Ids then the binds are topologically sorted.  This means
 that the simplifier will generally do a good job if it works from top bottom,
 recording inlinings for any Ids which aren't marked as "no-inline" as it goes.
 
+==============
+[June 98: I don't understand the following paragraphs, and I've 
+         changed the a=b case again so that it isn't a special case any more.]
+
 Here's a case that bit me:
 
        letrec
@@ -425,9 +249,32 @@ Here's a case that bit me:
        ...a...a...a....
 
 Re-ordering doesn't change the order of bindings, but there was no loop-breaker.
-(The first binding was a var-rhs; the second was a one-occ.)  So the simplifier looped.
+
 My solution was to make a=b bindings record b as Many, rather like INLINE bindings.
 Perhaps something cleverer would suffice.
+===============
+
+You might think that you can prevent non-termination simply by making
+sure that we simplify a recursive binding's RHS in an environment that
+simply clones the recursive Id.  But no.  Consider
+
+               letrec f = \x -> let z = f x' in ...
+
+               in
+               let n = f y
+               in
+               case n of { ... }
+
+We bind n to its *simplified* RHS, we then *re-simplify* it when
+we inline n.  Then we may well inline f; and then the same thing
+happens with z!
+
+I don't think it's possible to prevent non-termination by environment
+manipulation in this way.  Apart from anything else, successive
+iterations of the simplifier may unroll recursive loops in cases like
+that above.  The idea of beaking every recursive loop with an
+IMustNotBeINLINEd pragma is much much better.
+
 
 \begin{code}
 reOrderRec
@@ -438,54 +285,66 @@ reOrderRec
                        --      dontINLINE pragmas that there are no loops left.
 
        -- Non-recursive case
-reOrderRec env (AcyclicSCC (pair, _, _)) = [pair]
+reOrderRec env (AcyclicSCC (bind, _, _)) = [bind]
 
        -- Common case of simple self-recursion
 reOrderRec env (CyclicSCC [bind])
-  = [((addNoInlinePragma bndr, occ_info), rhs)]
+  = [(setIdOccInfo tagged_bndr IAmALoopBreaker, rhs)]
   where
-    (((bndr,occ_info), rhs), _, _) = bind
+    ((tagged_bndr, rhs), _, _) = bind
 
-reOrderRec env (CyclicSCC binds)
+reOrderRec env (CyclicSCC (bind : binds))
   =    -- Choose a loop breaker, mark it no-inline,
        -- do SCC analysis on the rest, and recursively sort them out
     concat (map (reOrderRec env) (stronglyConnCompR unchosen))
     ++ 
-    [((addNoInlinePragma bndr, occ_info), rhs)]
+    [(setIdOccInfo tagged_bndr IAmALoopBreaker, rhs)]
 
   where
-    (chosen_pair, unchosen) = choose_loop_breaker binds
-    ((bndr,occ_info), rhs)  = chosen_pair
-
-       -- Choosing the loop breaker; heursitic
-    choose_loop_breaker (bind@(pair, _, _) : rest)
-       |  not (null rest) &&
-          bad_choice pair
-       =  (chosen, bind : unchosen)    -- Don't pick it
-        | otherwise                    -- Pick it
-       = (pair,rest)
+    (chosen_pair, unchosen) = choose_loop_breaker bind (score bind) [] binds
+    (tagged_bndr, rhs)      = chosen_pair
+
+       -- This loop looks for the bind with the lowest score
+       -- to pick as the loop  breaker.  The rest accumulate in 
+    choose_loop_breaker (details,_,_) loop_sc acc []
+       = (details, acc)        -- Done
+
+    choose_loop_breaker loop_bind loop_sc acc (bind : binds)
+       | sc < loop_sc  -- Lower score so pick this new one
+       = choose_loop_breaker bind sc (loop_bind : acc) binds
+
+       | otherwise     -- No lower so don't pick it
+       = choose_loop_breaker loop_bind loop_sc (bind : acc) binds
        where
-         (chosen, unchosen) = choose_loop_breaker rest
-
-    bad_choice ((bndr, occ_info), rhs)
-       =    var_rhs rhs                -- Dont pick var RHS
-         || inlineMe env bndr          -- Dont pick INLINE thing
-         || one_occ occ_info           -- Dont pick single-occ thing
-         || not_fun_ty (idType bndr)   -- Dont pick data-ty thing
-
-    not_fun_ty ty = not (maybeToBool (getFunTy_maybe rho_ty))
-                 where
-                   (_, rho_ty) = splitForAllTy ty
-
-       -- A variable RHS
-    var_rhs (Var v)   = True
-    var_rhs other_rhs = False
-
-       -- One textual occurrence, whether inside lambda or whatever
-       -- We stick to just FunOccs because if we're not going to be able
-       -- to inline the thing on this round it might be better to pick
-       -- this one as the loop breaker.  Real example (the Enum Ordering instance
-       -- from PrelBase):
+         sc = score bind
+         
+    score :: Node Details2 -> Int      -- Higher score => less likely to be picked as loop breaker
+    score ((bndr, rhs), _, _)
+       | exprIsTrivial rhs        = 4  -- Practically certain to be inlined
+               -- Used to have also: && not (isExportedId bndr)
+               -- But I found this sometimes cost an extra iteration when we have
+               --      rec { d = (a,b); a = ...df...; b = ...df...; df = d }
+               -- where df is the exported dictionary. Then df makes a really
+               -- bad choice for loop breaker
+         
+       | not_fun_ty (idType bndr) = 3  -- Data types help with cases
+               -- This used to have a lower score than inlineCandidate, but
+               -- it's *really* helpful if dictionaries get inlined fast,
+               -- so I'm experimenting with giving higher priority to data-typed things
+
+       | inlineCandidate bndr rhs = 2  -- Likely to be inlined
+
+       | not (isEmptyCoreRules (idSpecialisation bndr)) = 1
+               -- Avoid things with specialisations; we'd like
+               -- to take advantage of them in the subsequent bindings
+
+       | otherwise = 0
+
+    inlineCandidate :: Id -> CoreExpr -> Bool
+    inlineCandidate id (Note InlineMe _) = True
+    inlineCandidate id rhs              = isOneOcc (idOccInfo id)
+
+       -- Real example (the Enum Ordering instance from PrelBase):
        --      rec     f = \ x -> case d of (p,q,r) -> p x
        --              g = \ x -> case d of (p,q,r) -> q x
        --              d = (v, f, g)
@@ -493,9 +352,9 @@ reOrderRec env (CyclicSCC binds)
        -- Here, f and g occur just once; but we can't inline them into d.
        -- On the other hand we *could* simplify those case expressions if
        -- we didn't stupidly choose d as the loop breaker.
+       -- But we won't because constructor args are marked "Many".
 
-    one_occ (OneOcc fun_or_arg _ _ _ _) = isFun fun_or_arg
-    one_occ other_bind                 = False
+    not_fun_ty ty = not (isFunTy (dropForAlls ty))
 \end{code}
 
 @occAnalRhs@ deals with the question of bindings where the Id is marked
@@ -506,28 +365,57 @@ 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.
 
 [March 97] We do the same for atomic RHSs.  Reason: see notes with reOrderRec.
+[June 98, SLPJ]  I've undone this change; I don't understand it.  See notes with reOrderRec.
+
 
 \begin{code}
 occAnalRhs :: OccEnv
           -> Id -> CoreExpr    -- Binder and rhs
-          -> (UsageDetails, SimplifiableCoreExpr)
-
-occAnalRhs env id (Var v)
-  | isCandidate env v
-  = (unitIdEnv v (markMany (funOccurrence 0)), Var v)
-
-  | otherwise
-  = (emptyDetails, Var v)
+                               -- For non-recs the binder is alrady tagged
+                               -- with occurrence info
+          -> (UsageDetails, CoreExpr)
 
 occAnalRhs env id rhs
-  | inlineMe env id
-  = (mapIdEnv markMany rhs_usage, rhs')
-
-  | otherwise
-  = (rhs_usage, rhs')
-
+  = (final_usage, rhs')
   where
-    (rhs_usage, rhs') = occAnal env rhs
+    (rhs_usage, rhs') = occAnal ctxt rhs
+    ctxt | certainly_inline id = env
+        | otherwise           = rhsCtxt env
+       -- Note that we generally use an rhsCtxt.  This tells the occ anal n
+       -- that it's looking at an RHS, which has an effect in occAnalApp
+       --
+       -- But there's a problem.  Consider
+       --      x1 = a0 : []
+       --      x2 = a1 : x1
+       --      x3 = a2 : x2
+       --      g  = f x3
+       -- First time round, it looks as if x1 and x2 occur as an arg of a 
+       -- let-bound constructor ==> give them a many-occurrence.
+       -- But then x3 is inlined (unconditionally as it happens) and
+       -- next time round, x2 will be, and the next time round x1 will be
+       -- Result: multiple simplifier iterations.  Sigh.  
+       -- Crude solution: use rhsCtxt for things that occur just once...
+
+    certainly_inline id = case idOccInfo id of
+                           OneOcc in_lam one_br -> not in_lam && one_br
+                           other                -> False
+
+       -- [March 98] A new wrinkle is that if the binder has specialisations inside
+       -- it then we count the specialised Ids as "extra rhs's".  That way
+       -- the "parent" keeps the specialised "children" alive.  If the parent
+       -- dies (because it isn't referenced any more), then the children will
+       -- die too unless they are already referenced directly.
+
+    final_usage = addRuleUsage rhs_usage id
+
+addRuleUsage :: UsageDetails -> Id -> UsageDetails
+-- Add the usage from RULES in Id to the usage
+addRuleUsage usage id
+  = foldVarSet add usage (idRuleVars id)
+  where
+    add v u = addOneOcc u v NoOccInfo          -- Give a non-committal binder info
+                                               -- (i.e manyOcc) because many copies
+                                               -- of the specialised thing can appear
 \end{code}
 
 Expressions
@@ -536,17 +424,23 @@ Expressions
 occAnal :: OccEnv
        -> CoreExpr
        -> (UsageDetails,       -- Gives info only about the "interesting" Ids
-           SimplifiableCoreExpr)
+           CoreExpr)
+
+occAnal env (Type t)  = (emptyDetails, Type t)
 
-occAnal env (Var v)
-  | isCandidate env v
-  = (unitIdEnv v (funOccurrence 0), Var v)
+occAnal env (Var v) 
+  = (var_uds, Var v)
+  where
+    var_uds | isCandidate env v = unitVarEnv v oneOcc
+           | otherwise         = emptyDetails
 
-  | otherwise
-  = (emptyDetails, Var v)
+    -- At one stage, I gathered the idRuleVars for v here too,
+    -- which in a way is the right thing to do.
+    -- But that went wrong right after specialisation, when
+    -- the *occurrences* of the overloaded function didn't have any
+    -- rules in them, so the *specialised* versions looked as if they
+    -- weren't used at all.
 
-occAnal env (Lit lit)     = (emptyDetails, Lit lit)
-occAnal env (Prim op args) = (occAnalArgs env args, Prim op args)
 \end{code}
 
 We regard variables that occur as constructor arguments as "dangerousToDup":
@@ -565,139 +459,403 @@ If we aren't careful we duplicate the (expensive x) call!
 Constructors are rather like lambdas in this way.
 
 \begin{code}
-occAnal env (Con con args) = (mapIdEnv markDangerousToDup (occAnalArgs env args), 
-                             Con con args)
+occAnal env expr@(Lit lit) = (emptyDetails, expr)
+\end{code}
 
-occAnal env (SCC cc body)
-  = (mapIdEnv markInsideSCC usage, SCC cc body')
-  where
-    (usage, body') = occAnal env body
+\begin{code}
+occAnal env (Note InlineMe body)
+  = case occAnal env body of { (usage, body') -> 
+    (mapVarEnv markMany usage, Note InlineMe body')
+    }
+
+occAnal env (Note note@(SCC cc) body)
+  = case occAnal env body of { (usage, body') ->
+    (mapVarEnv markInsideSCC usage, Note note body')
+    }
+
+occAnal env (Note note body)
+  = case occAnal env body of { (usage, body') ->
+    (usage, Note note body')
+    }
+\end{code}
 
-occAnal env (Coerce c ty body)
-  = (usage, Coerce c ty body')
-  where
-    (usage, body') = occAnal env body
+\begin{code}
+occAnal env app@(App fun arg)
+  = occAnalApp env (collectArgs app) False
 
-occAnal env (App fun arg)
-  = (fun_usage `combineUsageDetails` arg_usage, App fun' arg)
-  where
-    (fun_usage, fun') = occAnal    env fun
-    arg_usage        = occAnalArg env arg
+-- Ignore type variables altogether
+--   (a) occurrences inside type lambdas only not marked as InsideLam
+--   (b) type variables not in environment
+
+occAnal env expr@(Lam x body) | isTyVar x
+  = case occAnal env body of { (body_usage, body') ->
+    (body_usage, Lam x body')
+    }
 
 -- For value lambdas we do a special hack.  Consider
 --     (\x. \y. ...x...)
 -- If we did nothing, x is used inside the \y, so would be marked
 -- as dangerous to dup.  But in the common case where the abstraction
 -- is applied to two arguments this is over-pessimistic.
--- So instead we don't take account of the \y when dealing with x's usage;
--- instead, the simplifier is careful when partially applying lambdas
-
-occAnal env expr@(Lam (ValBinder binder) body)
-  = (mapIdEnv markDangerousToDup final_usage,
-     foldr ( \v b -> Lam (ValBinder v) b) body' tagged_binders)
+-- So instead, we just mark each binder with its occurrence
+-- info in the *body* of the multiple lambda.
+-- Then, the simplifier is careful when partially applying lambdas.
+
+occAnal env expr@(Lam _ _)
+  = case occAnal env_body body of { (body_usage, body') ->
+    let
+        (final_usage, tagged_binders) = tagBinders body_usage binders
+       --      URGH!  Sept 99: we don't seem to be able to use binders' here, because
+       --      we get linear-typed things in the resulting program that we can't handle yet.
+       --      (e.g. PrelShow)  TODO 
+
+       really_final_usage = if linear then
+                               final_usage
+                            else
+                               mapVarEnv markInsideLam final_usage
+    in
+    (really_final_usage,
+     mkLams tagged_binders body') }
   where
-    (binders,body)               = collectValBinders expr
-    (body_usage, body')          = occAnal (env `addNewCands` binders) body
-    (final_usage, tagged_binders) = tagBinders body_usage binders
-
--- ANDY: WE MUST THINK ABOUT THIS! (ToDo)
-occAnal env (Lam (TyBinder tyvar) body)
-  = case occAnal env body of { (body_usage, body') ->
-     (mapIdEnv markDangerousToDup body_usage,
-      Lam (TyBinder tyvar) body') }
---  where
---    (body_usage, body') = occAnal env body
-
-occAnal env (Lam (UsageBinder _) _) = panic "OccurAnal.occAnal Lam UsageBinder"
-
-occAnal env (Case scrut alts)
-  = case occAnalAlts env alts of { (alts_usage, alts')   -> 
-     case occAnal env scrut   of { (scrut_usage, scrut') ->
-       let
-        det = scrut_usage `combineUsageDetails` alts_usage
-       in
-       if isNullIdEnv det then
-          (det, Case scrut' alts')
-       else
-          (det, Case scrut' alts') }}
-{-
-       (scrut_usage `combineUsageDetails` alts_usage,
-        Case scrut' alts')
+    (binders, body)   = collectBinders expr
+    (linear, env1, _) = oneShotGroup env binders
+    env2             = env1 `addNewCands` binders      -- Add in-scope binders
+    env_body         = vanillaCtxt env2                -- Body is (no longer) an RhsContext
+
+occAnal env (Case scrut bndr ty alts)
+  = case mapAndUnzip (occAnalAlt alt_env bndr) alts of { (alts_usage_s, alts')   -> 
+    case occAnal (vanillaCtxt env) scrut           of { (scrut_usage, scrut') ->
+       -- No need for rhsCtxt
+    let
+       alts_usage  = foldr1 combineAltsUsageDetails alts_usage_s
+       alts_usage' = addCaseBndrUsage alts_usage
+       (alts_usage1, tagged_bndr) = tagBinder alts_usage' bndr
+        total_usage = scrut_usage `combineUsageDetails` alts_usage1
+    in
+    total_usage `seq` (total_usage, Case scrut' tagged_bndr ty alts') }}
   where
-    (scrut_usage, scrut') = occAnal env scrut
-    (alts_usage, alts')   = occAnalAlts env alts
--}
+    alt_env = env `addNewCand` bndr
+
+       -- The case binder gets a usage of either "many" or "dead", never "one".
+       -- Reason: we like to inline single occurrences, to eliminate a binding,
+       -- but inlining a case binder *doesn't* eliminate a binding.
+       -- We *don't* want to transform
+       --      case x of w { (p,q) -> f w }
+       -- into
+       --      case x of w { (p,q) -> f (p,q) }
+    addCaseBndrUsage usage = case lookupVarEnv usage bndr of
+                               Nothing  -> usage
+                               Just occ -> extendVarEnv usage bndr (markMany occ)
 
 occAnal env (Let bind body)
   = case occAnal new_env body            of { (body_usage, body') ->
     case occAnalBind env bind body_usage of { (final_usage, new_binds) ->
-       (final_usage, foldr Let body' new_binds) }} -- mkCoLet* wants Core... (sigh)
+       (final_usage, mkLets new_binds body') }}
+  where
+    new_env = env `addNewCands` (bindersOf bind)
+
+occAnalArgs env args
+  = case mapAndUnzip (occAnal arg_env) args of { (arg_uds_s, args') ->
+    (foldr combineUsageDetails emptyDetails arg_uds_s, args')}
   where
-    new_env                 = env `addNewCands` (bindersOf bind)
---    (body_usage, body')      = occAnal new_env body
---    (final_usage, new_binds) = occAnalBind env bind body_usage
+    arg_env = vanillaCtxt env
 \end{code}
 
+Applications are dealt with specially because we want
+the "build hack" to work.
+
+\begin{code}
+-- Hack for build, fold, runST
+occAnalApp env (Var fun, args) is_rhs
+  = case args_stuff of { (args_uds, args') ->
+    let
+       -- We mark the free vars of the argument of a constructor or PAP 
+       -- as "many", if it is the RHS of a let(rec).
+       -- This means that nothing gets inlined into a constructor argument
+       -- position, which is what we want.  Typically those constructor
+       -- arguments are just variables, or trivial expressions.
+       --
+       -- This is the *whole point* of the isRhsEnv predicate
+        final_args_uds
+               | isRhsEnv env,
+                 isDataConWorkId fun || valArgCount args < idArity fun
+               = mapVarEnv markMany args_uds
+               | otherwise = args_uds
+    in
+    (fun_uds `combineUsageDetails` final_args_uds, mkApps (Var fun) args') }
+  where
+    fun_uniq = idUnique fun
+
+    fun_uds | isCandidate env fun = unitVarEnv fun oneOcc
+           | otherwise           = emptyDetails
+
+    args_stuff | fun_uniq == buildIdKey    = appSpecial env 2 [True,True]  args
+               | fun_uniq == augmentIdKey  = appSpecial env 2 [True,True]  args
+               | fun_uniq == foldrIdKey    = appSpecial env 3 [False,True] args
+               | fun_uniq == runSTRepIdKey = appSpecial env 2 [True]       args
+                       -- (foldr k z xs) may call k many times, but it never
+                       -- shares a partial application of k; hence [False,True]
+                       -- This means we can optimise
+                       --      foldr (\x -> let v = ...x... in \y -> ...v...) z xs
+                       -- by floating in the v
+
+               | otherwise = occAnalArgs env args
+
+
+occAnalApp env (fun, args) is_rhs
+  = case occAnal (addAppCtxt env args) fun of  { (fun_uds, fun') ->
+       -- The addAppCtxt is a bit cunning.  One iteration of the simplifier
+       -- often leaves behind beta redexs like
+       --      (\x y -> e) a1 a2
+       -- Here we would like to mark x,y as one-shot, and treat the whole
+       -- thing much like a let.  We do this by pushing some True items
+       -- onto the context stack.
+
+    case occAnalArgs env args of       { (args_uds, args') ->
+    let
+       final_uds = fun_uds `combineUsageDetails` args_uds
+    in
+    (final_uds, mkApps fun' args') }}
+    
+appSpecial :: OccEnv 
+          -> Int -> CtxtTy     -- Argument number, and context to use for it
+          -> [CoreExpr]
+          -> (UsageDetails, [CoreExpr])
+appSpecial env n ctxt args
+  = go n args
+  where
+    arg_env = vanillaCtxt env
+
+    go n [] = (emptyDetails, [])       -- Too few args
+
+    go 1 (arg:args)                    -- The magic arg
+      = case occAnal (setCtxt arg_env ctxt) arg of     { (arg_uds, arg') ->
+       case occAnalArgs env args of                    { (args_uds, args') ->
+       (combineUsageDetails arg_uds args_uds, arg':args') }}
+    
+    go n (arg:args)
+      = case occAnal arg_env arg of    { (arg_uds, arg') ->
+       case go (n-1) args of           { (args_uds, args') ->
+       (combineUsageDetails arg_uds args_uds, arg':args') }}
+\end{code}
+
+    
 Case alternatives
 ~~~~~~~~~~~~~~~~~
+If the case binder occurs at all, the other binders effectively do too.  
+For example
+       case e of x { (a,b) -> rhs }
+is rather like
+       let x = (a,b) in rhs
+If e turns out to be (e1,e2) we indeed get something like
+       let a = e1; b = e2; x = (a,b) in rhs
+
 \begin{code}
-occAnalAlts env (AlgAlts alts deflt)
-  = (foldr combineAltsUsageDetails deflt_usage alts_usage,
-       -- Note: combine*Alts*UsageDetails...
-     AlgAlts alts' deflt')
-  where
-    (alts_usage,  alts')  = unzip (map do_alt alts)
-    (deflt_usage, deflt') = occAnalDeflt env deflt
+occAnalAlt env case_bndr (con, bndrs, rhs)
+  = case occAnal (env `addNewCands` bndrs) rhs of { (rhs_usage, rhs') ->
+    let
+        (final_usage, tagged_bndrs) = tagBinders rhs_usage bndrs
+       final_bndrs | case_bndr `elemVarEnv` final_usage = bndrs
+                   | otherwise                         = tagged_bndrs
+               -- Leave the binders untagged if the case 
+               -- binder occurs at all; see note above
+    in
+    (final_usage, (con, final_bndrs, rhs')) }
+\end{code}
 
-    do_alt (con, args, rhs)
-      = (final_usage, (con, tagged_args, rhs'))
-      where
-       new_env            = env `addNewCands` args
-       (rhs_usage, rhs')          = occAnal new_env rhs
-       (final_usage, tagged_args) = tagBinders rhs_usage args
-
-occAnalAlts env (PrimAlts alts deflt)
-  = (foldr combineAltsUsageDetails deflt_usage alts_usage,
-       -- Note: combine*Alts*UsageDetails...
-     PrimAlts alts' deflt')
-  where
-    (alts_usage, alts')   = unzip (map do_alt alts)
-    (deflt_usage, deflt') = occAnalDeflt env deflt
 
-    do_alt (lit, rhs)
-      = (rhs_usage, (lit, rhs'))
-      where
-       (rhs_usage, rhs') = occAnal env rhs
+%************************************************************************
+%*                                                                     *
+\subsection[OccurAnal-types]{OccEnv}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+data OccEnv
+  = OccEnv IdSet       -- In-scope Ids; we gather info about these only
+          OccEncl      -- Enclosing context information
+          CtxtTy       -- Tells about linearity
+
+-- OccEncl is used to control whether to inline into constructor arguments
+-- For example:
+--     x = (p,q)               -- Don't inline p or q
+--     y = /\a -> (p a, q a)   -- Still don't inline p or q
+--     z = f (p,q)             -- Do inline p,q; it may make a rule fire
+-- So OccEncl tells enought about the context to know what to do when
+-- we encounter a contructor application or PAP.
+
+data OccEncl
+  = OccRhs             -- RHS of let(rec), albeit perhaps inside a type lambda
+                       -- Don't inline into constructor args here
+  | OccVanilla         -- Argument of function, body of lambda, scruintee of case etc.
+                       -- Do inline into constructor args here
+
+type CtxtTy = [Bool]
+       -- []           No info
+       --
+       -- True:ctxt    Analysing a function-valued expression that will be
+       --                      applied just once
+       --
+       -- False:ctxt   Analysing a function-valued expression that may
+       --                      be applied many times; but when it is, 
+       --                      the CtxtTy inside applies
+
+initOccEnv :: VarSet -> OccEnv
+initOccEnv vars = OccEnv vars OccRhs []
 
-occAnalDeflt env NoDefault = (emptyDetails, NoDefault)
+isRhsEnv (OccEnv _ OccRhs     _) = True
+isRhsEnv (OccEnv _ OccVanilla _) = False
 
-occAnalDeflt env (BindDefault binder rhs)
-  = (final_usage, BindDefault tagged_binder rhs')
+isCandidate :: OccEnv -> Id -> Bool
+isCandidate (OccEnv cands encl _) id = id `elemVarSet` cands 
+
+addNewCands :: OccEnv -> [Id] -> OccEnv
+addNewCands (OccEnv cands encl ctxt) ids
+  = OccEnv (extendVarSetList cands ids) encl ctxt
+
+addNewCand :: OccEnv -> Id -> OccEnv
+addNewCand (OccEnv cands encl ctxt) id
+  = OccEnv (extendVarSet cands id) encl ctxt
+
+setCtxt :: OccEnv -> CtxtTy -> OccEnv
+setCtxt (OccEnv cands encl _) ctxt = OccEnv cands encl ctxt
+
+oneShotGroup :: OccEnv -> [CoreBndr] -> (Bool, OccEnv, [CoreBndr])
+       -- True <=> this is a one-shot linear lambda group
+       -- The [CoreBndr] are the binders.
+
+       -- The result binders have one-shot-ness set that they might not have had originally.
+       -- This happens in (build (\cn -> e)).  Here the occurrence analyser
+       -- linearity context knows that c,n are one-shot, and it records that fact in
+       -- the binder. This is useful to guide subsequent float-in/float-out tranformations
+
+oneShotGroup (OccEnv cands encl ctxt) bndrs 
+  = case go ctxt bndrs [] of
+       (new_ctxt, new_bndrs) -> (all is_one_shot new_bndrs, OccEnv cands encl new_ctxt, new_bndrs)
   where
-    new_env                     = env `addNewCand` binder
-    (rhs_usage, rhs')           = occAnal new_env rhs
-    (final_usage, tagged_binder) = tagBinder rhs_usage binder
+    is_one_shot b = isId b && isOneShotBndr b
+
+    go ctxt [] rev_bndrs = (ctxt, reverse rev_bndrs)
+
+    go (lin_ctxt:ctxt) (bndr:bndrs) rev_bndrs
+       | isId bndr = go ctxt bndrs (bndr':rev_bndrs)
+       where
+         bndr' | lin_ctxt  = setOneShotLambda bndr
+               | otherwise = bndr
+
+    go ctxt (bndr:bndrs) rev_bndrs = go ctxt bndrs (bndr:rev_bndrs)
+
+
+vanillaCtxt (OccEnv cands _ _) = OccEnv cands OccVanilla []
+rhsCtxt     (OccEnv cands _ _) = OccEnv cands OccRhs     []
+
+addAppCtxt (OccEnv cands encl ctxt) args 
+  = OccEnv cands encl (replicate (valArgCount args) True ++ ctxt)
 \end{code}
 
+%************************************************************************
+%*                                                                     *
+\subsection[OccurAnal-types]{OccEnv}
+%*                                                                     *
+%************************************************************************
 
-Atoms
-~~~~~
 \begin{code}
-occAnalArgs :: OccEnv -> [CoreArg] -> UsageDetails
+type UsageDetails = IdEnv OccInfo      -- A finite map from ids to their usage
+
+combineUsageDetails, combineAltsUsageDetails
+       :: UsageDetails -> UsageDetails -> UsageDetails
+
+combineUsageDetails usage1 usage2
+  = plusVarEnv_C addOccInfo usage1 usage2
+
+combineAltsUsageDetails usage1 usage2
+  = plusVarEnv_C orOccInfo usage1 usage2
+
+addOneOcc :: UsageDetails -> Id -> OccInfo -> UsageDetails
+addOneOcc usage id info
+  = plusVarEnv_C addOccInfo usage (unitVarEnv id info)
+       -- ToDo: make this more efficient
+
+emptyDetails = (emptyVarEnv :: UsageDetails)
+
+usedIn :: Id -> UsageDetails -> Bool
+v `usedIn` details =  isExportedId v || v `elemVarEnv` details
+
+tagBinders :: UsageDetails         -- Of scope
+          -> [Id]                  -- Binders
+          -> (UsageDetails,        -- Details with binders removed
+             [IdWithOccInfo])    -- Tagged binders
 
-occAnalArgs env atoms
-  = foldr do_one_atom emptyDetails atoms
+tagBinders usage binders
+ = let
+     usage' = usage `delVarEnvList` binders
+     uss    = map (setBinderOcc usage) binders
+   in
+   usage' `seq` (usage', uss)
+
+tagBinder :: UsageDetails          -- Of scope
+         -> Id                     -- Binders
+         -> (UsageDetails,         -- Details with binders removed
+             IdWithOccInfo)        -- Tagged binders
+
+tagBinder usage binder
+ = let
+     usage'  = usage `delVarEnv` binder
+     binder' = setBinderOcc usage binder
+   in
+   usage' `seq` (usage', binder')
+
+setBinderOcc :: UsageDetails -> CoreBndr -> CoreBndr
+setBinderOcc usage bndr
+  | isTyVar bndr      = bndr
+  | isExportedId bndr = case idOccInfo bndr of
+                         NoOccInfo -> bndr
+                         other     -> setIdOccInfo bndr NoOccInfo
+           -- Don't use local usage info for visible-elsewhere things
+           -- BUT *do* erase any IAmALoopBreaker annotation, because we're
+           -- about to re-generate it and it shouldn't be "sticky"
+                         
+  | otherwise = setIdOccInfo bndr occ_info
   where
-    do_one_atom (VarArg v) usage
-       | isCandidate env v = addOneOcc usage v (argOccurrence 0)
-       | otherwise         = usage
-    do_one_atom other_arg  usage = usage
+    occ_info = lookupVarEnv usage bndr `orElse` IAmDead
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection{Operations over OccInfo}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+oneOcc :: OccInfo
+oneOcc = OneOcc False True
+
+markMany, markInsideLam, markInsideSCC :: OccInfo -> OccInfo
+
+markMany IAmDead = IAmDead
+markMany other   = NoOccInfo
+
+markInsideSCC occ = markMany occ
+
+markInsideLam (OneOcc _ one_br) = OneOcc True one_br
+markInsideLam occ              = occ
+
+addOccInfo, orOccInfo :: OccInfo -> OccInfo -> OccInfo
+
+addOccInfo IAmDead info2 = info2
+addOccInfo info1 IAmDead = info1
+addOccInfo info1 info2   = NoOccInfo
 
+-- (orOccInfo orig new) is used
+-- when combining occurrence info from branches of a case
 
-occAnalArg  :: OccEnv -> CoreArg -> UsageDetails
+orOccInfo IAmDead info2 = info2
+orOccInfo info1 IAmDead = info1
+orOccInfo (OneOcc in_lam1 one_branch1)
+         (OneOcc in_lam2 one_branch2)
+  = OneOcc (in_lam1 || in_lam2)
+          False        -- False, because it occurs in both branches
 
-occAnalArg env (VarArg v)
-  | isCandidate env v = unitDetails v (argOccurrence 0)
-  | otherwise         = emptyDetails
-occAnalArg _   _      = emptyDetails
+orOccInfo info1 info2 = NoOccInfo
 \end{code}