[project @ 2005-03-07 16:46:08 by simonpj]
[ghc-hetmet.git] / ghc / compiler / simplCore / OccurAnal.lhs
index 87927ec..8b6c5bb 100644 (file)
@@ -12,38 +12,31 @@ core expression with (hopefully) improved usage information.
 
 \begin{code}
 module OccurAnal (
-       occurAnalyseBinds, occurAnalyseExpr, occurAnalyseGlobalExpr,
-       markBinderInsideLambda, tagBinders,
-       UsageDetails
+       occurAnalysePgm, occurAnalyseGlobalExpr, occurAnalyseRule, 
     ) where
 
 #include "HsVersions.h"
 
-import BinderInfo
-import CmdLineOpts     ( SimplifierSwitch(..) )
 import CoreSyn
 import CoreFVs         ( idRuleVars )
 import CoreUtils       ( exprIsTrivial )
-import Const           ( Con(..), Literal(..) )
-import Id              ( isSpecPragmaId,
-                         getInlinePragma, setInlinePragma,
-                         isExportedId, modifyIdInfo, idInfo,
-                         getIdSpecialisation, 
+import Id              ( isDataConWorkId, isOneShotBndr, setOneShotLambda, 
+                         idOccInfo, setIdOccInfo,
+                         isExportedId, idArity, idSpecialisation, 
                          idType, idUnique, Id
                        )
-import IdInfo          ( InlinePragInfo(..), OccInfo(..), copyIdInfo )
+import BasicTypes      ( OccInfo(..), isOneOcc )
 
 import VarSet
 import VarEnv
 
-import ThinAir         ( noRepStrIds, noRepIntegerIds )
-import Name            ( isLocallyDefined )
-import Type            ( splitFunTy_maybe, splitForAllTys )
-import Maybes          ( maybeToBool )
+import Type            ( isFunTy, dropForAlls )
+import Maybes          ( orElse )
 import Digraph         ( stronglyConnCompR, SCC(..) )
-import Unique          ( u2i, buildIdKey, foldrIdKey, runSTRepIdKey, augmentIdKey )
+import PrelNames       ( buildIdKey, foldrIdKey, runSTRepIdKey, augmentIdKey )
+import Unique          ( Unique )
 import UniqFM          ( keysUFM )  
-import Util            ( zipWithEqual, mapAndUnzip, count )
+import Util            ( zipWithEqual, mapAndUnzip )
 import Outputable
 \end{code}
 
@@ -57,152 +50,34 @@ import Outputable
 Here's the externally-callable interface:
 
 \begin{code}
-occurAnalyseExpr :: (Id -> Bool)       -- Tells if a variable is interesting
-                -> CoreExpr
-                -> (IdEnv BinderInfo,  -- Occ info for interesting free vars
-                    CoreExpr)
-
-occurAnalyseExpr interesting expr
-  = occAnal initial_env expr
+occurAnalysePgm :: [CoreBind] -> [CoreBind]
+occurAnalysePgm binds
+  = snd (go (initOccEnv emptyVarSet) binds)
   where
-    initial_env = OccEnv interesting emptyVarSet []
+    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
 
 occurAnalyseGlobalExpr :: CoreExpr -> CoreExpr
 occurAnalyseGlobalExpr expr
   =    -- Top level expr, so no interesting free vars, and
        -- discard occurence info returned
-    snd (occurAnalyseExpr (\_ -> False) expr)
-\end{code}
-
-
-%************************************************************************
-%*                                                                     *
-\subsection{Top level stuff}
-%*                                                                     *
-%************************************************************************
-
-In @occAnalTop@ we do indirection-shorting.  That is, if we have this:
-
-       loc = <expression>
-       ...
-       exp = loc
+    snd (occAnal (initOccEnv emptyVarSet) expr)
 
-where exp is exported, and loc is not, then we replace it with this:
-
-       loc = exp
-       exp = <expression>
-       ...
-
-Without this we never get rid of the exp = loc thing.
-This save a gratuitous jump
-(from \tr{x_exported} to \tr{x_local}), and makes strictness
-information propagate better.
-This used to happen in the final phase, but it's tidier to do it here.
-
-
-If more than one exported thing is equal to a local thing (i.e., the
-local thing really is shared), then we do one only:
-\begin{verbatim}
-       x_local = ....
-       x_exported1 = x_local
-       x_exported2 = x_local
-==>
-       x_exported1 = ....
-
-       x_exported2 = x_exported1
-\end{verbatim}
-
-We rely on prior eta reduction to simplify things like
-\begin{verbatim}
-       x_exported = /\ tyvars -> x_local tyvars
-==>
-       x_exported = x_local
-\end{verbatim}
-Hence,there's a possibility of leaving unchanged something like this:
-\begin{verbatim}
-       x_local = ....
-       x_exported1 = x_local Int
-\end{verbatim}
-By the time we've thrown away the types in STG land this 
-could be eliminated.  But I don't think it's very common
-and it's dangerous to do this fiddling in STG land 
-because we might elminate a binding that's mentioned in the
-unfolding for something.
-
-\begin{code}
-occurAnalyseBinds :: [CoreBind] -> [CoreBind]
-
-occurAnalyseBinds binds
-  = binds'
+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
-    (_, _, binds') = go initialTopEnv binds
-
-    go :: OccEnv -> [CoreBind]
-       -> (UsageDetails,       -- Occurrence info
-          IdEnv Id,            -- Indirection elimination info
-          [CoreBind])
-
-    go env [] = (emptyDetails, emptyVarEnv, [])
-
-    go env (bind : binds)
-      = let
-           new_env                        = env `addNewCands` (bindersOf bind)
-           (scope_usage, ind_env, binds') = go new_env binds
-           (final_usage, new_binds)       = occAnalBind env (zapBind ind_env bind) scope_usage
-                                               -- NB: I zap before occur-analysing, so
-                                               -- I don't need to worry about getting the
-                                               -- occ info on the new bindings right.
-       in
-        case bind of
-           NonRec exported_id (Var local_id) 
-               | shortMeOut ind_env exported_id local_id
-               -- Special case for eliminating indirections
-               --   Note: it's a shortcoming that this only works for
-               --         non-recursive bindings.  Elminating indirections
-               --         makes perfect sense for recursive bindings too, but
-               --         it's more complicated to implement, so I haven't done so
-               -> (scope_usage, ind_env', binds')
-               where
-                  ind_env' = extendVarEnv ind_env local_id exported_id
-
-           other ->    -- Ho ho! The normal case
-                  (final_usage, ind_env, new_binds ++ binds')
-                  
-initialTopEnv = OccEnv isLocallyDefined        -- Anything local is interesting
-                      emptyVarSet
-                      []
-
-
--- Deal with any indirections
-zapBind ind_env (NonRec bndr rhs) 
-  | bndr `elemVarEnv` ind_env                     = Rec (zap ind_env (bndr,rhs))
-               -- The Rec isn't strictly necessary, but it's convenient
-zapBind ind_env (Rec pairs)
-  | or [id `elemVarEnv` ind_env | (id,_) <- pairs] = Rec (concat (map (zap ind_env) pairs))
-
-zapBind ind_env bind = bind
-
-zap ind_env pair@(bndr,rhs)
-  = case lookupVarEnv ind_env bndr of
-       Nothing          -> [pair]
-       Just exported_id -> [(bndr, Var exported_id),
-                            (exported_id_w_info, rhs)]
-                        where
-                          exported_id_w_info = modifyIdInfo (copyIdInfo (idInfo bndr)) exported_id
-                               -- See notes with copyIdInfo about propagating IdInfo from
-                               -- one to t'other
-                       
-shortMeOut ind_env exported_id local_id
-  = isExportedId exported_id &&                -- Only if this is exported
-
-    isLocallyDefined local_id &&       -- Only if this one is defined in this
-                                       --      module, so that we *can* change its
-                                       --      binding to be the exported thing!
-
-    not (isExportedId local_id) &&     -- Only if this one is not itself exported,
-                                       --      since the transformation will nuke it
-
-    not (local_id `elemVarEnv` ind_env)                -- Only if not already substituted for
+    (rhs_uds, rhs') = occAnal (initOccEnv (mkVarSet tpl_vars)) rhs
+    (_, tpl_vars')  = tagBinders rhs_uds tpl_vars
 \end{code}
 
 
@@ -218,7 +93,7 @@ Bindings
 \begin{code}
 type IdWithOccInfo = Id                        -- An Id with fresh PragmaInfo attached
 
-type Node details = (details, Int, [Int])      -- The Ints are gotten from the Unique,
+type Node details = (details, Unique, [Unique])        -- The Ints are gotten from the Unique,
                                                -- which is gotten from the Id.
 type Details1    = (Id, UsageDetails, CoreExpr)
 type Details2    = (IdWithOccInfo, CoreExpr)
@@ -240,7 +115,7 @@ occAnalBind env (NonRec binder rhs) body_usage
 
   where
     (final_body_usage, tagged_binder) = tagBinder body_usage binder
-    (rhs_usage, rhs')                = occAnalRhs env binder rhs
+    (rhs_usage, rhs')                = occAnalRhs env tagged_binder rhs
 \end{code}
 
 Dropping dead code for recursive bindings is done in a very simple way:
@@ -282,8 +157,6 @@ 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_item (_, bndr, _)     = ppr bndr
-
     binders = map fst pairs
     rhs_env = env `addNewCands` binders
 
@@ -300,7 +173,7 @@ occAnalBind env (Rec pairs) body_usage
     ---- stuff for dependency analysis of binds -------------------------------
     edges :: [Node Details1]
     edges = _scc_ "occAnalBind.assoc"
-           [ (details, IBOX(u2i (idUnique id)), edges_from rhs_usage)
+           [ (details, idUnique id, edges_from rhs_usage)
            | details@(id, rhs_usage, rhs) <- analysed_pairs
            ]
 
@@ -313,7 +186,7 @@ occAnalBind env (Rec pairs) body_usage
        --               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
 
@@ -416,7 +289,7 @@ reOrderRec env (AcyclicSCC (bind, _, _)) = [bind]
 
        -- Common case of simple self-recursion
 reOrderRec env (CyclicSCC [bind])
-  = [(setInlinePragma tagged_bndr IAmALoopBreaker, rhs)]
+  = [(setIdOccInfo tagged_bndr IAmALoopBreaker, rhs)]
   where
     ((tagged_bndr, rhs), _, _) = bind
 
@@ -425,7 +298,7 @@ reOrderRec env (CyclicSCC (bind : binds))
        -- do SCC analysis on the rest, and recursively sort them out
     concat (map (reOrderRec env) (stronglyConnCompR unchosen))
     ++ 
-    [(setInlinePragma tagged_bndr IAmALoopBreaker, rhs)]
+    [(setIdOccInfo tagged_bndr IAmALoopBreaker, rhs)]
 
   where
     (chosen_pair, unchosen) = choose_loop_breaker bind (score bind) [] binds
@@ -447,21 +320,29 @@ reOrderRec env (CyclicSCC (bind : binds))
          
     score :: Node Details2 -> Int      -- Higher score => less likely to be picked as loop breaker
     score ((bndr, rhs), _, _)
-       | exprIsTrivial rhs && 
-         not (isExportedId bndr)  = 3          -- Practically certain to be inlined
-       | inlineCandidate bndr rhs = 3          -- Likely to be inlined
-       | not_fun_ty (idType bndr) = 2          -- Data types help with cases
-       | not (isEmptyCoreRules (getIdSpecialisation bndr)) = 1
+       | 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              = case getInlinePragma id of
-                                               IMustBeINLINEd          -> True
-                                               ICanSafelyBeINLINEd _ _ -> True
-                                               other               -> False
+    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
@@ -473,9 +354,7 @@ reOrderRec env (CyclicSCC (bind : binds))
        -- we didn't stupidly choose d as the loop breaker.
        -- But we won't because constructor args are marked "Many".
 
-    not_fun_ty ty = not (maybeToBool (splitFunTy_maybe rho_ty))
-                 where
-                   (_, rho_ty) = splitForAllTys ty
+    not_fun_ty ty = not (isFunTy (dropForAlls ty))
 \end{code}
 
 @occAnalRhs@ deals with the question of bindings where the Id is marked
@@ -492,12 +371,34 @@ ToDo: try using the occurrence info for the inline'd binder.
 \begin{code}
 occAnalRhs :: OccEnv
           -> Id -> CoreExpr    -- Binder and rhs
+                               -- For non-recs the binder is alrady tagged
+                               -- with occurrence info
           -> (UsageDetails, CoreExpr)
 
 occAnalRhs env id rhs
   = (final_usage, rhs')
   where
-    (rhs_usage, rhs') = occAnal (zapCtxt 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
@@ -505,8 +406,14 @@ occAnalRhs env id rhs
        -- dies (because it isn't referenced any more), then the children will
        -- die too unless they are already referenced directly.
 
-    final_usage = foldVarSet add rhs_usage (idRuleVars id)
-    add v u = addOneOcc u v noBinderInfo       -- Give a non-committal binder info
+    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}
@@ -524,7 +431,7 @@ occAnal env (Type t)  = (emptyDetails, Type t)
 occAnal env (Var v) 
   = (var_uds, Var v)
   where
-    var_uds | isCandidate env v = unitVarEnv v funOccZero
+    var_uds | isCandidate env v = unitVarEnv v oneOcc
            | otherwise         = emptyDetails
 
     -- At one stage, I gathered the idRuleVars for v here too,
@@ -552,35 +459,7 @@ If we aren't careful we duplicate the (expensive x) call!
 Constructors are rather like lambdas in this way.
 
 \begin{code}
-       -- For NoRep literals we have to report an occurrence of
-       -- the things which tidyCore will later add, so that when
-       -- we are compiling the very module in which those thin-air Ids
-       -- are defined we have them in scope!
-occAnal env expr@(Con (Literal lit) args)
-  = ASSERT( null args )
-    (mk_lit_uds lit, expr)
-  where
-    mk_lit_uds (NoRepStr _ _)     = try noRepStrIds
-    mk_lit_uds (NoRepInteger _ _) = try noRepIntegerIds
-    mk_lit_uds lit               = emptyDetails
-
-    try vs = foldr add emptyDetails vs
-    add v uds | isCandidate env v = extendVarEnv uds v funOccZero
-             | otherwise         = uds
-
-occAnal env (Con con args)
-  = case occAnalArgs env args of { (arg_uds, args') ->
-    let        
-       -- We mark the free vars of the argument of a constructor as "many"
-       -- 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.
-       final_arg_uds    = case con of
-                               DataCon _ -> mapVarEnv markMany arg_uds
-                               other     -> arg_uds
-    in
-    (final_arg_uds, Con con args')
-    }
+occAnal env expr@(Lit lit) = (emptyDetails, expr)
 \end{code}
 
 \begin{code}
@@ -602,7 +481,7 @@ occAnal env (Note note body)
 
 \begin{code}
 occAnal env app@(App fun arg)
-  = occAnalApp env (collectArgs app)
+  = occAnalApp env (collectArgs app) False
 
 -- Ignore type variables altogether
 --   (a) occurrences inside type lambdas only not marked as InsideLam
@@ -623,9 +502,13 @@ occAnal env expr@(Lam x body) | isTyVar x
 -- Then, the simplifier is careful when partially applying lambdas.
 
 occAnal env expr@(Lam _ _)
-  = case occAnal (env_body `addNewCands` binders) body of { (body_usage, body') ->
+  = 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
@@ -634,21 +517,36 @@ occAnal env expr@(Lam _ _)
     (really_final_usage,
      mkLams tagged_binders body') }
   where
-    (binders, body)    = collectBinders expr
-    (linear, env_body) = getCtxt env (count isId binders)
-
-occAnal env (Case scrut bndr alts)
-  = case mapAndUnzip (occAnalAlt alt_env) alts of { (alts_usage_s, alts')   -> 
-    case occAnal (zapCtxt env) scrut          of { (scrut_usage, scrut') ->
+    (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_usage1, tagged_bndr) = tagBinder alts_usage bndr
+       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 alts') }}
+    total_usage `seq` (total_usage, Case scrut' tagged_bndr ty alts') }}
   where
     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) ->
@@ -660,7 +558,7 @@ occAnalArgs env args
   = case mapAndUnzip (occAnal arg_env) args of { (arg_uds_s, args') ->
     (foldr combineUsageDetails emptyDetails arg_uds_s, args')}
   where
-    arg_env = zapCtxt env
+    arg_env = vanillaCtxt env
 \end{code}
 
 Applications are dealt with specially because we want
@@ -668,45 +566,75 @@ the "build hack" to work.
 
 \begin{code}
 -- Hack for build, fold, runST
-occAnalApp env (Var fun, args)
+occAnalApp env (Var fun, args) is_rhs
   = case args_stuff of { (args_uds, args') ->
     let
-       final_uds = fun_uds `combineUsageDetails` args_uds
+       -- 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
-    (final_uds, mkApps (Var fun) args') }
+    (fun_uds `combineUsageDetails` final_args_uds, mkApps (Var fun) args') }
   where
     fun_uniq = idUnique fun
 
-    fun_uds | isCandidate env fun = unitVarEnv fun funOccZero
+    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
-               | otherwise                 = occAnalArgs env args
-
-occAnalApp env (fun, args)
-  = case occAnal (zapCtxt env) fun of          { (fun_uds, fun') ->
-    case occAnalArgs env args of               { (args_uds, 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 -> [CoreExpr] -> (UsageDetails, [CoreExpr])
+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 env ctxt) arg of { (arg_uds, arg') ->
-       case occAnalArgs env args of            { (args_uds, args') ->
+      = 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 env arg of                { (arg_uds, arg') ->
+      = 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}
@@ -714,31 +642,53 @@ appSpecial env n ctxt args
     
 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}
-occAnalAlt env (con, bndrs, rhs)
+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, tagged_bndrs, rhs')) }
+    (final_usage, (con, final_bndrs, rhs')) }
 \end{code}
 
 
 %************************************************************************
 %*                                                                     *
-\subsection[OccurAnal-types]{Data types}
+\subsection[OccurAnal-types]{OccEnv}
 %*                                                                     *
 %************************************************************************
 
 \begin{code}
--- We gather inforamtion for variables that are either
---     (a) in scope or
---     (b) interesting
-
-data OccEnv =
-  OccEnv (Id -> Bool)  -- Tells whether an Id occurrence is interesting,
-        IdSet          -- In-scope Ids
-        CtxtTy         -- Tells about linearity
+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
@@ -750,49 +700,84 @@ type CtxtTy = [Bool]
        --                      be applied many times; but when it is, 
        --                      the CtxtTy inside applies
 
+initOccEnv :: VarSet -> OccEnv
+initOccEnv vars = OccEnv vars OccRhs []
+
+isRhsEnv (OccEnv _ OccRhs     _) = True
+isRhsEnv (OccEnv _ OccVanilla _) = False
+
 isCandidate :: OccEnv -> Id -> Bool
-isCandidate (OccEnv ifun cands _) id = id `elemVarSet` cands || ifun id
+isCandidate (OccEnv cands encl _) id = id `elemVarSet` cands 
 
 addNewCands :: OccEnv -> [Id] -> OccEnv
-addNewCands (OccEnv ifun cands ctxt) ids
-  = OccEnv ifun (cands `unionVarSet` mkVarSet ids) ctxt
+addNewCands (OccEnv cands encl ctxt) ids
+  = OccEnv (extendVarSetList cands ids) encl ctxt
 
 addNewCand :: OccEnv -> Id -> OccEnv
-addNewCand (OccEnv ifun cands ctxt) id
-  = OccEnv ifun (extendVarSet cands id) ctxt
+addNewCand (OccEnv cands encl ctxt) id
+  = OccEnv (extendVarSet cands id) encl ctxt
 
 setCtxt :: OccEnv -> CtxtTy -> OccEnv
-setCtxt (OccEnv ifun cands _) ctxt = OccEnv ifun cands ctxt
+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
+    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)
+
 
-getCtxt :: OccEnv -> Int -> (Bool, OccEnv)     -- True <=> this is a linear lambda
-                                               -- The Int is the number of lambdas
-getCtxt env@(OccEnv ifun cands []) n = (False, env)
-getCtxt (OccEnv ifun cands ctxt)   n = (and (take n ctxt), OccEnv ifun cands (drop n ctxt))
-               -- Only return True if *all* the lambdas are linear
+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}
 
-zapCtxt env@(OccEnv ifun cands []) = env
-zapCtxt     (OccEnv ifun cands _ ) = OccEnv ifun cands []
+%************************************************************************
+%*                                                                     *
+\subsection[OccurAnal-types]{OccEnv}
+%*                                                                     *
+%************************************************************************
 
-type UsageDetails = IdEnv BinderInfo   -- A finite map from ids to their usage
+\begin{code}
+type UsageDetails = IdEnv OccInfo      -- A finite map from ids to their usage
 
 combineUsageDetails, combineAltsUsageDetails
        :: UsageDetails -> UsageDetails -> UsageDetails
 
 combineUsageDetails usage1 usage2
-  = plusVarEnv_C addBinderInfo usage1 usage2
+  = plusVarEnv_C addOccInfo usage1 usage2
 
 combineAltsUsageDetails usage1 usage2
-  = plusVarEnv_C orBinderInfo usage1 usage2
+  = plusVarEnv_C orOccInfo usage1 usage2
 
-addOneOcc :: UsageDetails -> Id -> BinderInfo -> UsageDetails
+addOneOcc :: UsageDetails -> Id -> OccInfo -> UsageDetails
 addOneOcc usage id info
-  = plusVarEnv_C addBinderInfo usage (unitVarEnv id info)
+  = plusVarEnv_C addOccInfo usage (unitVarEnv id info)
        -- ToDo: make this more efficient
 
 emptyDetails = (emptyVarEnv :: UsageDetails)
 
-unitDetails id info = (unitVarEnv id info :: UsageDetails)
-
 usedIn :: Id -> UsageDetails -> Bool
 v `usedIn` details =  isExportedId v || v `elemVarEnv` details
 
@@ -804,7 +789,7 @@ tagBinders :: UsageDetails      -- Of scope
 tagBinders usage binders
  = let
      usage' = usage `delVarEnvList` binders
-     uss    = map (setBinderPrag usage) binders
+     uss    = map (setBinderOcc usage) binders
    in
    usage' `seq` (usage', uss)
 
@@ -816,56 +801,61 @@ tagBinder :: UsageDetails     -- Of scope
 tagBinder usage binder
  = let
      usage'  = usage `delVarEnv` binder
-     binder' = setBinderPrag usage 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
+    occ_info = lookupVarEnv usage bndr `orElse` IAmDead
+\end{code}
 
-setBinderPrag :: UsageDetails -> CoreBndr -> CoreBndr
-setBinderPrag usage bndr
-  | isTyVar bndr
-  = bndr
 
-  | otherwise
-  = case old_prag of
-       NoInlinePragInfo        -> new_bndr
-       IAmDead                 -> new_bndr     -- The next three are annotations
-       ICanSafelyBeINLINEd _ _ -> new_bndr     -- from the previous iteration of
-       IAmALoopBreaker         -> new_bndr     -- the occurrence analyser
+%************************************************************************
+%*                                                                     *
+\subsection{Operations over OccInfo}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+oneOcc :: OccInfo
+oneOcc = OneOcc False True
 
-       other | its_now_dead    -> new_bndr     -- Overwrite the others iff it's now dead
-             | otherwise       -> bndr
+markMany, markInsideLam, markInsideSCC :: OccInfo -> OccInfo
 
-  where
-    old_prag = getInlinePragma bndr 
-    new_bndr = setInlinePragma bndr new_prag
+markMany IAmDead = IAmDead
+markMany other   = NoOccInfo
 
-    its_now_dead = case new_prag of
-                       IAmDead -> True
-                       other   -> False
+markInsideSCC occ = markMany occ
 
-    new_prag = occInfoToInlinePrag occ_info
+markInsideLam (OneOcc _ one_br) = OneOcc True one_br
+markInsideLam occ              = occ
 
-    occ_info
-       | isExportedId bndr = noBinderInfo
-       -- Don't use local usage info for visible-elsewhere things
-       -- But NB that we do set NoInlinePragma for exported things
-       -- thereby nuking any IAmALoopBreaker from a previous pass.
+addOccInfo, orOccInfo :: OccInfo -> OccInfo -> OccInfo
 
-       | otherwise       = case lookupVarEnv usage bndr of
-                                   Nothing   -> deadOccurrence
-                                   Just info -> info
+addOccInfo IAmDead info2 = info2
+addOccInfo info1 IAmDead = info1
+addOccInfo info1 info2   = NoOccInfo
 
-markBinderInsideLambda :: CoreBndr -> CoreBndr
-markBinderInsideLambda bndr
-  | isTyVar bndr
-  = bndr
+-- (orOccInfo orig new) is used
+-- when combining occurrence info from branches of a case
 
-  | otherwise
-  = case getInlinePragma bndr of
-       ICanSafelyBeINLINEd not_in_lam nalts
-               -> bndr `setInlinePragma` ICanSafelyBeINLINEd InsideLam nalts
-       other   -> bndr
+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
 
-funOccZero = funOccurrence 0
+orOccInfo info1 info2 = NoOccInfo
 \end{code}