summaryrefslogtreecommitdiff
path: root/ghc/compiler/simplStg
diff options
context:
space:
mode:
Diffstat (limited to 'ghc/compiler/simplStg')
-rw-r--r--ghc/compiler/simplStg/LambdaLift.hi8
-rw-r--r--ghc/compiler/simplStg/LambdaLift.lhs527
-rw-r--r--ghc/compiler/simplStg/SatStgRhs.hi8
-rw-r--r--ghc/compiler/simplStg/SatStgRhs.lhs307
-rw-r--r--ghc/compiler/simplStg/SimplStg.hi12
-rw-r--r--ghc/compiler/simplStg/SimplStg.lhs354
-rw-r--r--ghc/compiler/simplStg/StgSAT.hi18
-rw-r--r--ghc/compiler/simplStg/StgSAT.lhs186
-rw-r--r--ghc/compiler/simplStg/StgSATMonad.hi22
-rw-r--r--ghc/compiler/simplStg/StgSATMonad.lhs182
-rw-r--r--ghc/compiler/simplStg/StgStats.hi7
-rw-r--r--ghc/compiler/simplStg/StgStats.lhs188
-rw-r--r--ghc/compiler/simplStg/StgVarInfo.hi7
-rw-r--r--ghc/compiler/simplStg/StgVarInfo.lhs790
-rw-r--r--ghc/compiler/simplStg/UpdAnal.hi7
-rw-r--r--ghc/compiler/simplStg/UpdAnal.lhs510
16 files changed, 3133 insertions, 0 deletions
diff --git a/ghc/compiler/simplStg/LambdaLift.hi b/ghc/compiler/simplStg/LambdaLift.hi
new file mode 100644
index 0000000000..1ea1a64990
--- /dev/null
+++ b/ghc/compiler/simplStg/LambdaLift.hi
@@ -0,0 +1,8 @@
+{-# GHC_PRAGMA INTERFACE VERSION 5 #-}
+interface LambdaLift where
+import Id(Id)
+import SplitUniq(SplitUniqSupply)
+import StgSyn(StgBinding)
+liftProgram :: SplitUniqSupply -> [StgBinding Id Id] -> [StgBinding Id Id]
+ {-# GHC_PRAGMA _A_ 2 _U_ 21 _N_ _S_ "LS" _N_ _N_ #-}
+
diff --git a/ghc/compiler/simplStg/LambdaLift.lhs b/ghc/compiler/simplStg/LambdaLift.lhs
new file mode 100644
index 0000000000..158ce90bce
--- /dev/null
+++ b/ghc/compiler/simplStg/LambdaLift.lhs
@@ -0,0 +1,527 @@
+%
+% (c) The AQUA Project, Glasgow University, 1994-1995
+%
+\section[LambdaLift]{A STG-code lambda lifter}
+
+\begin{code}
+#include "HsVersions.h"
+
+module LambdaLift ( liftProgram ) where
+
+import StgSyn
+
+import AbsUniType ( mkForallTy, splitForalls, glueTyArgs,
+ UniType, RhoType(..), TauType(..)
+ )
+import Bag
+import Id ( mkSysLocal, getIdUniType, addIdArity, Id )
+import IdEnv
+import Maybes
+import SplitUniq
+import SrcLoc ( mkUnknownSrcLoc, SrcLoc )
+import UniqSet
+import Util
+\end{code}
+
+This is the lambda lifter. It turns lambda abstractions into
+supercombinators on a selective basis:
+
+* Let-no-escaped bindings are never lifted. That's one major reason
+ why the lambda lifter is done in STG.
+
+* Non-recursive bindings whose RHS is a lambda abstractions are lifted,
+ provided all the occurrences of the bound variable is in a function
+ postition. In this example, f will be lifted:
+
+ let
+ f = \x -> e
+ in
+ ..(f a1)...(f a2)...
+ thus
+
+ $f p q r x = e -- Supercombinator
+
+ ..($f p q r a1)...($f p q r a2)...
+
+ NOTE that the original binding is eliminated.
+
+ But in this case, f won't be lifted:
+
+ let
+ f = \x -> e
+ in
+ ..(g f)...(f a2)...
+
+ Why? Because we have to heap-allocate a closure for f thus:
+
+ $f p q r x = e -- Supercombinator
+
+ let
+ f = $f p q r
+ in
+ ..(g f)...($f p q r a2)..
+
+ so it might as well be the original lambda abstraction.
+
+ We also do not lift if the function has an occurrence with no arguments, e.g.
+
+ let
+ f = \x -> e
+ in f
+
+ as this form is more efficient than if we create a partial application
+
+ $f p q r x = e -- Supercombinator
+
+ f p q r
+
+* Recursive bindings *all* of whose RHSs are lambda abstractions are
+ lifted iff
+ - all the occurrences of all the binders are in a function position
+ - there aren't ``too many'' free variables.
+
+ Same reasoning as before for the function-position stuff. The ``too many
+ free variable'' part comes from considering the (potentially many)
+ recursive calls, which may now have lots of free vars.
+
+Recent Observations:
+* 2 might be already ``too many'' variables to abstract.
+ The problem is that the increase in the number of free variables
+ of closures refering to the lifted function (which is always # of
+ abstracted args - 1) may increase heap allocation a lot.
+ Expeiments are being done to check this...
+* We do not lambda lift if the function has at least one occurrence
+ without any arguments. This caused lots of problems. Ex:
+ h = \ x -> ... let y = ...
+ in let let f = \x -> ...y...
+ in f
+ ==>
+ f = \y x -> ...y...
+ h = \ x -> ... let y = ...
+ in f y
+
+ now f y is a partial application, so it will be updated, and this
+ is Bad.
+
+
+--- NOT RELEVANT FOR STG ----
+* All ``lone'' lambda abstractions are lifted. Notably this means lambda
+ abstractions:
+ - in a case alternative: case e of True -> (\x->b)
+ - in the body of a let: let x=e in (\y->b)
+-----------------------------
+
+%************************************************************************
+%* *
+\subsection[Lift-expressions]{The main function: liftExpr}
+%* *
+%************************************************************************
+
+\begin{code}
+liftProgram :: SplitUniqSupply -> [PlainStgBinding] -> [PlainStgBinding]
+liftProgram us prog = concat (runLM Nothing us (mapLM liftTopBind prog))
+
+
+liftTopBind :: PlainStgBinding -> LiftM [PlainStgBinding]
+liftTopBind (StgNonRec id rhs)
+ = dontLiftRhs rhs `thenLM` \ (rhs', rhs_info) ->
+ returnLM (getScBinds rhs_info ++ [StgNonRec id rhs'])
+
+liftTopBind (StgRec pairs)
+ = mapAndUnzipLM dontLiftRhs rhss `thenLM` \ (rhss', rhs_infos) ->
+ returnLM ([co_rec_ify (StgRec (ids `zip` rhss') :
+ getScBinds (unionLiftInfos rhs_infos))
+ ])
+ where
+ (ids, rhss) = unzip pairs
+\end{code}
+
+
+\begin{code}
+liftExpr :: PlainStgExpr
+ -> LiftM (PlainStgExpr, LiftInfo)
+
+
+liftExpr expr@(StgConApp con args lvs) = returnLM (expr, emptyLiftInfo)
+liftExpr expr@(StgPrimApp op args lvs) = returnLM (expr, emptyLiftInfo)
+
+liftExpr expr@(StgApp (StgLitAtom lit) args lvs) = returnLM (expr, emptyLiftInfo)
+liftExpr expr@(StgApp (StgVarAtom v) args lvs)
+ = lookup v `thenLM` \ ~(sc, sc_args) -> -- NB the ~. We don't want to
+ -- poke these bindings too early!
+ returnLM (StgApp (StgVarAtom sc) (map StgVarAtom sc_args ++ args) lvs,
+ emptyLiftInfo)
+ -- The lvs field is probably wrong, but we reconstruct it
+ -- anyway following lambda lifting
+
+liftExpr (StgCase scrut lv1 lv2 uniq alts)
+ = liftExpr scrut `thenLM` \ (scrut', scrut_info) ->
+ lift_alts alts `thenLM` \ (alts', alts_info) ->
+ returnLM (StgCase scrut' lv1 lv2 uniq alts', scrut_info `unionLiftInfo` alts_info)
+ where
+ lift_alts (StgAlgAlts ty alg_alts deflt)
+ = mapAndUnzipLM lift_alg_alt alg_alts `thenLM` \ (alg_alts', alt_infos) ->
+ lift_deflt deflt `thenLM` \ (deflt', deflt_info) ->
+ returnLM (StgAlgAlts ty alg_alts' deflt', foldr unionLiftInfo deflt_info alt_infos)
+
+ lift_alts (StgPrimAlts ty prim_alts deflt)
+ = mapAndUnzipLM lift_prim_alt prim_alts `thenLM` \ (prim_alts', alt_infos) ->
+ lift_deflt deflt `thenLM` \ (deflt', deflt_info) ->
+ returnLM (StgPrimAlts ty prim_alts' deflt', foldr unionLiftInfo deflt_info alt_infos)
+
+ lift_alg_alt (con, args, use_mask, rhs)
+ = liftExpr rhs `thenLM` \ (rhs', rhs_info) ->
+ returnLM ((con, args, use_mask, rhs'), rhs_info)
+
+ lift_prim_alt (lit, rhs)
+ = liftExpr rhs `thenLM` \ (rhs', rhs_info) ->
+ returnLM ((lit, rhs'), rhs_info)
+
+ lift_deflt StgNoDefault = returnLM (StgNoDefault, emptyLiftInfo)
+ lift_deflt (StgBindDefault var used rhs)
+ = liftExpr rhs `thenLM` \ (rhs', rhs_info) ->
+ returnLM (StgBindDefault var used rhs', rhs_info)
+\end{code}
+
+Now the interesting cases. Let no escape isn't lifted. We turn it
+back into a let, to play safe, because we have to redo that pass after
+lambda anyway.
+
+\begin{code}
+liftExpr (StgLetNoEscape _ _ (StgNonRec binder rhs) body)
+ = dontLiftRhs rhs `thenLM` \ (rhs', rhs_info) ->
+ liftExpr body `thenLM` \ (body', body_info) ->
+ returnLM (StgLet (StgNonRec binder rhs') body',
+ rhs_info `unionLiftInfo` body_info)
+
+liftExpr (StgLetNoEscape _ _ (StgRec pairs) body)
+ = liftExpr body `thenLM` \ (body', body_info) ->
+ mapAndUnzipLM dontLiftRhs rhss `thenLM` \ (rhss', rhs_infos) ->
+ returnLM (StgLet (StgRec (binders `zipEqual` rhss')) body',
+ foldr unionLiftInfo body_info rhs_infos)
+ where
+ (binders,rhss) = unzip pairs
+\end{code}
+
+\begin{code}
+liftExpr (StgLet (StgNonRec binder rhs) body)
+ | not (isLiftable rhs)
+ = dontLiftRhs rhs `thenLM` \ (rhs', rhs_info) ->
+ liftExpr body `thenLM` \ (body', body_info) ->
+ returnLM (StgLet (StgNonRec binder rhs') body',
+ rhs_info `unionLiftInfo` body_info)
+
+ | otherwise -- It's a lambda
+ = -- Do the body of the let
+ fixLM (\ ~(sc_inline, _, _) ->
+ addScInlines [binder] [sc_inline] (
+ liftExpr body
+ ) `thenLM` \ (body', body_info) ->
+
+ -- Deal with the RHS
+ dontLiftRhs rhs `thenLM` \ (rhs', rhs_info) ->
+
+ -- All occurrences in function position, so lambda lift
+ getFinalFreeVars (rhsFreeVars rhs) `thenLM` \ final_free_vars ->
+
+ mkScPieces final_free_vars (binder,rhs') `thenLM` \ (sc_inline, sc_bind) ->
+
+ returnLM (sc_inline,
+ body',
+ nonRecScBind rhs_info sc_bind `unionLiftInfo` body_info)
+
+ ) `thenLM` \ (_, expr', final_info) ->
+
+ returnLM (expr', final_info)
+
+liftExpr (StgLet (StgRec pairs) body)
+--[Andre-testing]
+ | not (all isLiftableRec rhss)
+ = liftExpr body `thenLM` \ (body', body_info) ->
+ mapAndUnzipLM dontLiftRhs rhss `thenLM` \ (rhss', rhs_infos) ->
+ returnLM (StgLet (StgRec (binders `zipEqual` rhss')) body',
+ foldr unionLiftInfo body_info rhs_infos)
+
+ | otherwise -- All rhss are liftable
+ = -- Do the body of the let
+ fixLM (\ ~(sc_inlines, _, _) ->
+ addScInlines binders sc_inlines (
+
+ liftExpr body `thenLM` \ (body', body_info) ->
+ mapAndUnzipLM dontLiftRhs rhss `thenLM` \ (rhss', rhs_infos) ->
+ let
+ -- Find the free vars of all the rhss,
+ -- excluding the binders themselves.
+ rhs_free_vars = unionManyUniqSets (map rhsFreeVars rhss)
+ `minusUniqSet`
+ mkUniqSet binders
+
+ rhs_info = unionLiftInfos rhs_infos
+ in
+ getFinalFreeVars rhs_free_vars `thenLM` \ final_free_vars ->
+
+ mapAndUnzipLM (mkScPieces final_free_vars) (binders `zip` rhss')
+ `thenLM` \ (sc_inlines, sc_pairs) ->
+ returnLM (sc_inlines,
+ body',
+ recScBind rhs_info sc_pairs `unionLiftInfo` body_info)
+
+ )) `thenLM` \ (_, expr', final_info) ->
+
+ returnLM (expr', final_info)
+ where
+ (binders,rhss) = unzip pairs
+\end{code}
+
+\begin{code}
+liftExpr (StgSCC ty cc expr)
+ = liftExpr expr `thenLM` \ (expr2, expr_info) ->
+ returnLM (StgSCC ty cc expr2, expr_info)
+\end{code}
+
+A binding is liftable if it's a *function* (args not null) and never
+occurs in an argument position.
+
+\begin{code}
+isLiftable :: PlainStgRhs -> Bool
+
+isLiftable (StgRhsClosure _ (StgBinderInfo arg_occ _ _ _ unapplied_occ) fvs _ args _)
+ -- experimental evidence suggests we should lift only if we will be abstracting up to 4 fvs.
+ = if not (null args || -- Not a function
+ unapplied_occ || -- Has an occ with no args at all
+ arg_occ || -- Occurs in arg position
+ length fvs > 4 -- Too many free variables
+ )
+ then {-trace ("LL: " ++ show (length fvs))-} True
+ else False
+isLiftable other_rhs = False
+
+isLiftableRec :: PlainStgRhs -> Bool
+-- this is just the same as for non-rec, except we only lift to abstract up to 1 argument
+-- this avoids undoing Static Argument Transformation work
+isLiftableRec (StgRhsClosure _ (StgBinderInfo arg_occ _ _ _ unapplied_occ) fvs _ args _)
+ = if not (null args || -- Not a function
+ unapplied_occ || -- Has an occ with no args at all
+ arg_occ || -- Occurs in arg position
+ length fvs > 1 -- Too many free variables
+ )
+ then {-trace ("LLRec: " ++ show (length fvs))-} True
+ else False
+isLiftableRec other_rhs = False
+
+rhsFreeVars :: PlainStgRhs -> IdSet
+rhsFreeVars (StgRhsClosure _ _ fvs _ _ _) = mkUniqSet fvs
+rhsFreeVars other = panic "rhsFreeVars"
+\end{code}
+
+dontLiftRhs is like liftExpr, except that it does not lift a top-level lambda
+abstraction. It is used for the right-hand sides of definitions where
+we've decided *not* to lift: for example, top-level ones or mutually-recursive
+ones where not all are lambdas.
+
+\begin{code}
+dontLiftRhs :: PlainStgRhs -> LiftM (PlainStgRhs, LiftInfo)
+
+dontLiftRhs rhs@(StgRhsCon cc v args) = returnLM (rhs, emptyLiftInfo)
+
+dontLiftRhs (StgRhsClosure cc bi fvs upd args body)
+ = liftExpr body `thenLM` \ (body', body_info) ->
+ returnLM (StgRhsClosure cc bi fvs upd args body', body_info)
+\end{code}
+
+
+\begin{code}
+mkScPieces :: IdSet -- Extra args for the supercombinator
+ -> (Id, PlainStgRhs) -- The processed RHS and original Id
+ -> LiftM ((Id,[Id]), -- Replace abstraction with this;
+ -- the set is its free vars
+ (Id,PlainStgRhs)) -- Binding for supercombinator
+
+mkScPieces extra_arg_set (id, StgRhsClosure cc bi _ upd args body)
+ = ASSERT( n_args > 0 )
+ -- Construct the rhs of the supercombinator, and its Id
+ -- this trace blackholes sometimes, don't use it
+ -- trace ("LL " ++ show (length (uniqSetToList extra_arg_set))) (
+ newSupercombinator sc_ty arity `thenLM` \ sc_id ->
+
+ returnLM ((sc_id, extra_args), (sc_id, sc_rhs))
+ --)
+ where
+ n_args = length args
+ extra_args = uniqSetToList extra_arg_set
+ arity = n_args + length extra_args
+
+ -- Construct the supercombinator type
+ type_of_original_id = getIdUniType id
+ extra_arg_tys = map getIdUniType extra_args
+ (tyvars, rest) = splitForalls type_of_original_id
+ sc_ty = mkForallTy tyvars (glueTyArgs extra_arg_tys rest)
+
+ sc_rhs = StgRhsClosure cc bi [] upd (extra_args ++ args) body
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection[Lift-monad]{The LiftM monad}
+%* *
+%************************************************************************
+
+The monad is used only to distribute global stuff, and the unique supply.
+
+\begin{code}
+type LiftM a = LiftFlags
+ -> SplitUniqSupply
+ -> (IdEnv -- Domain = candidates for lifting
+ (Id, -- The supercombinator
+ [Id]) -- Args to apply it to
+ )
+ -> a
+
+
+type LiftFlags = Maybe Int -- No of fvs reqd to float recursive
+ -- binding; Nothing == infinity
+
+
+runLM :: LiftFlags -> SplitUniqSupply -> LiftM a -> a
+runLM flags us m = m flags us nullIdEnv
+
+thenLM :: LiftM a -> (a -> LiftM b) -> LiftM b
+thenLM m k ci us idenv
+ = k (m ci us1 idenv) ci us2 idenv
+ where
+ (us1, us2) = splitUniqSupply us
+
+returnLM :: a -> LiftM a
+returnLM a ci us idenv = a
+
+fixLM :: (a -> LiftM a) -> LiftM a
+fixLM k ci us idenv = r
+ where
+ r = k r ci us idenv
+
+mapLM :: (a -> LiftM b) -> [a] -> LiftM [b]
+mapLM f [] = returnLM []
+mapLM f (a:as) = f a `thenLM` \ r ->
+ mapLM f as `thenLM` \ rs ->
+ returnLM (r:rs)
+
+mapAndUnzipLM :: (a -> LiftM (b,c)) -> [a] -> LiftM ([b],[c])
+mapAndUnzipLM f [] = returnLM ([],[])
+mapAndUnzipLM f (a:as) = f a `thenLM` \ (b,c) ->
+ mapAndUnzipLM f as `thenLM` \ (bs,cs) ->
+ returnLM (b:bs, c:cs)
+\end{code}
+
+\begin{code}
+newSupercombinator :: UniType
+ -> Int -- Arity
+ -> LiftM Id
+
+newSupercombinator ty arity ci us idenv
+ = (mkSysLocal SLIT("sc") uniq ty mkUnknownSrcLoc) -- ToDo: improve location
+ `addIdArity` arity
+ -- ToDo: rm the addIdArity? Just let subsequent stg-saturation pass do it?
+ where
+ uniq = getSUnique us
+
+lookup :: Id -> LiftM (Id,[Id])
+lookup v ci us idenv
+ = case lookupIdEnv idenv v of
+ Just result -> result
+ Nothing -> (v, [])
+
+addScInlines :: [Id] -> [(Id,[Id])] -> LiftM a -> LiftM a
+addScInlines ids values m ci us idenv
+ = m ci us idenv'
+ where
+ idenv' = growIdEnvList idenv (ids `zip_lazy` values)
+
+ -- zip_lazy zips two things together but matches lazily on the
+ -- second argument. This is important, because the ids are know here,
+ -- but the things they are bound to are decided only later
+ zip_lazy [] _ = []
+ zip_lazy (x:xs) ~(y:ys) = (x,y) : zip_lazy xs ys
+
+
+-- The free vars reported by the free-var analyser will include
+-- some ids, f, which are to be replaced by ($f a b c), where $f
+-- is the supercombinator. Hence instead of f being a free var,
+-- {a,b,c} are.
+--
+-- Example
+-- let
+-- f a = ...y1..y2.....
+-- in
+-- let
+-- g b = ...f...z...
+-- in
+-- ...
+--
+-- Here the free vars of g are {f,z}; but f will be lambda-lifted
+-- with free vars {y1,y2}, so the "real~ free vars of g are {y1,y2,z}.
+
+getFinalFreeVars :: IdSet -> LiftM IdSet
+
+getFinalFreeVars free_vars ci us idenv
+ = unionManyUniqSets (map munge_it (uniqSetToList free_vars))
+ where
+ munge_it :: Id -> IdSet -- Takes a free var and maps it to the "real"
+ -- free var
+ munge_it id = case lookupIdEnv idenv id of
+ Just (_, args) -> mkUniqSet args
+ Nothing -> singletonUniqSet id
+
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection[Lift-info]{The LiftInfo type}
+%* *
+%************************************************************************
+
+\begin{code}
+type LiftInfo = Bag PlainStgBinding -- Float to top
+
+emptyLiftInfo = emptyBag
+
+unionLiftInfo :: LiftInfo -> LiftInfo -> LiftInfo
+unionLiftInfo binds1 binds2 = binds1 `unionBags` binds2
+
+unionLiftInfos :: [LiftInfo] -> LiftInfo
+unionLiftInfos infos = foldr unionLiftInfo emptyLiftInfo infos
+
+mkScInfo :: PlainStgBinding -> LiftInfo
+mkScInfo bind = unitBag bind
+
+nonRecScBind :: LiftInfo -- From body of supercombinator
+ -> (Id, PlainStgRhs) -- Supercombinator and its rhs
+ -> LiftInfo
+nonRecScBind binds (sc_id,sc_rhs) = binds `snocBag` (StgNonRec sc_id sc_rhs)
+
+
+-- In the recursive case, all the SCs from the RHSs of the recursive group
+-- are dealing with might potentially mention the new, recursive SCs.
+-- So we flatten the whole lot into a single recursive group.
+
+recScBind :: LiftInfo -- From body of supercombinator
+ -> [(Id,PlainStgRhs)] -- Supercombinator rhs
+ -> LiftInfo
+
+recScBind binds pairs = unitBag (co_rec_ify (StgRec pairs : bagToList binds))
+
+co_rec_ify :: [PlainStgBinding] -> PlainStgBinding
+co_rec_ify binds = StgRec (concat (map f binds))
+ where
+ f (StgNonRec id rhs) = [(id,rhs)]
+ f (StgRec pairs) = pairs
+
+
+getScBinds :: LiftInfo -> [PlainStgBinding]
+getScBinds binds = bagToList binds
+
+looksLikeSATRhs [(f,StgRhsClosure _ _ _ _ ls _)] (StgApp (StgVarAtom f') args _)
+ = (f == f') && (length args == length ls)
+looksLikeSATRhs _ _ = False
+\end{code}
diff --git a/ghc/compiler/simplStg/SatStgRhs.hi b/ghc/compiler/simplStg/SatStgRhs.hi
new file mode 100644
index 0000000000..de10f7c424
--- /dev/null
+++ b/ghc/compiler/simplStg/SatStgRhs.hi
@@ -0,0 +1,8 @@
+{-# GHC_PRAGMA INTERFACE VERSION 5 #-}
+interface SatStgRhs where
+import Id(Id)
+import SplitUniq(SplitUniqSupply)
+import StgSyn(StgBinding)
+satStgRhs :: [StgBinding Id Id] -> SplitUniqSupply -> [StgBinding Id Id]
+ {-# GHC_PRAGMA _A_ 2 _U_ 11 _N_ _S_ "S" _N_ _N_ #-}
+
diff --git a/ghc/compiler/simplStg/SatStgRhs.lhs b/ghc/compiler/simplStg/SatStgRhs.lhs
new file mode 100644
index 0000000000..a6793d7a78
--- /dev/null
+++ b/ghc/compiler/simplStg/SatStgRhs.lhs
@@ -0,0 +1,307 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1995
+%
+\section[SatStgRhs]{Saturates RHSs when they are partial applications}
+
+
+\begin{display}
+Subject: arg satis check
+Date: Wed, 29 Apr 92 13:33:58 +0100
+From: Simon L Peyton Jones <simonpj>
+
+Andre
+
+Another transformation to consider. We'd like to avoid
+argument-satisfaction checks wherever possible. So, whenever we have an
+STG binding application
+
+ f = vs \ xs -> g e1 ... en
+
+where xs has one or more elements
+and
+where g is a known function with arity m+n,
+
+then: change it to
+
+ f = vs \ xs++{x1...xm} -> g e1 ... en x1 .. xm
+
+Now g has enough args. One arg-satisfaction check disappears;
+the one for the closure incorporates the one for g.
+
+You might like to consider variants, applying the transformation more
+widely. I concluded that this was the only instance which made
+sense, but I could be wrong.
+
+Simon
+\end{display}
+
+The algorithm proceeds as follows:
+\begin{enumerate}
+\item
+Gather the arity information of the functions defined in this module
+(as @getIdArity@ only knows about the arity of @ImportedIds@).
+
+\item
+for every definition of the form
+\begin{verbatim}
+ v = /\ts -> \vs -> f args
+\end{verbatim}
+we try to match the arity of \tr{f} with the number of arguments.
+If they do not match we insert extra lambdas to make that application
+saturated.
+\end{enumerate}
+
+This is done for local definitions as well.
+
+\begin{code}
+#include "HsVersions.h"
+
+module SatStgRhs ( satStgRhs ) where
+
+import StgSyn
+
+import AbsUniType ( splitTypeWithDictsAsArgs, Class,
+ TyVarTemplate, TauType(..)
+ )
+import CostCentre
+import IdEnv
+import Id ( mkSysLocal, getIdUniType, getIdArity, addIdArity )
+import IdInfo -- SIGH: ( arityMaybe, ArityInfo, OptIdInfo(..) )
+import SrcLoc ( mkUnknownSrcLoc, SrcLoc )
+import SplitUniq
+import Unique
+import Util
+import Maybes
+
+type Arity = Int
+type Count = Int
+
+type ExprArityInfo = Maybe Int -- Just n => This expression has a guaranteed
+ -- arity of n
+ -- Nothing => Don't know how many args it needs
+
+type Id_w_Arity = Id -- An Id with correct arity info pinned on it
+type SatEnv = IdEnv Id_w_Arity -- Binds only local, let(rec)-bound things
+\end{code}
+
+This pass
+\begin{itemize}
+\item adds extra args where necessary;
+\item pins the correct arity on everything.
+\end{itemize}
+
+%************************************************************************
+%* *
+\subsection{Top-level list of bindings (a ``program'')}
+%* *
+%************************************************************************
+
+\begin{code}
+satStgRhs :: PlainStgProgram -> SUniqSM PlainStgProgram
+
+satStgRhs p = satProgram nullIdEnv p
+
+satProgram :: SatEnv -> PlainStgProgram -> SUniqSM PlainStgProgram
+satProgram env [] = returnSUs []
+
+satProgram env (bind:binds)
+ = satBinding True{-toplevel-} env bind `thenSUs` \ (env2, bind2) ->
+ satProgram env2 binds `thenSUs` \ binds2 ->
+ returnSUs (bind2 : binds2)
+\end{code}
+
+%************************************************************************
+%* *
+\subsection{Bindings}
+%* *
+%************************************************************************
+
+\begin{code}
+satBinding :: Bool -- True <=> top-level
+ -> SatEnv
+ -> PlainStgBinding
+ -> SUniqSM (SatEnv, PlainStgBinding)
+
+satBinding top env (StgNonRec b rhs)
+ = satRhs top env (b, rhs) `thenSUs` \ (b2, rhs2) ->
+ let
+ env2 = addOneToIdEnv env b b2
+ in
+ returnSUs (env2, StgNonRec b2 rhs2)
+
+satBinding top env (StgRec pairs)
+ = -- Do it once to get the arities right...
+ mapSUs (satRhs top env) pairs `thenSUs` \ pairs2 ->
+ let
+ env2 = growIdEnvList env (map fst pairs `zip` map fst pairs2)
+ in
+ -- Do it again to *use* those arities:
+ mapSUs (satRhs top env2) pairs `thenSUs` \ pairs3 ->
+
+ returnSUs (env2, StgRec pairs3)
+
+satRhs :: Bool -> SatEnv -> (Id, PlainStgRhs) -> SUniqSM (Id_w_Arity, PlainStgRhs)
+
+satRhs top env (b, StgRhsCon cc con args) -- Nothing much to do here
+ = let
+ b2 = b `addIdArity` 0 -- bound to a saturated constructor; hence zero.
+ in
+ returnSUs (b2, StgRhsCon cc con (lookupArgs env args))
+
+satRhs top env (b, StgRhsClosure cc bi fv u args body)
+ = satExpr env body `thenSUs` \ (arity_info, body2) ->
+ let
+ num_args = length args
+ in
+ (case arity_info of
+ Nothing ->
+ returnSUs (num_args, StgRhsClosure cc bi fv u args body2)
+
+ Just needed_args ->
+ ASSERT(needed_args >= 1)
+
+ let -- the arity we're aiming for is: what we already have ("args")
+ -- plus the ones requested in "arity_info"
+ new_arity = num_args + needed_args
+
+ -- get type info for this function:
+ (_,all_arg_tys,_) = splitTypeWithDictsAsArgs (getIdUniType b)
+
+ -- now, we already have "args"; we drop that many types
+ args_we_dont_have_tys = drop num_args all_arg_tys
+
+ -- finally, we take some of those (up to maybe all of them),
+ -- depending on how many "needed_args"
+ args_to_add_tys = take needed_args args_we_dont_have_tys
+ in
+ -- make up names for them
+ mapSUs newName args_to_add_tys `thenSUs` \ nns ->
+
+ -- and do the business
+ let
+ body3 = saturate body2 (map StgVarAtom nns)
+
+ new_cc -- if we're adding args, we'd better not
+ -- keep calling something a CAF! (what about DICTs? ToDo: WDP 95/02)
+ = if not (isCafCC cc)
+ then cc -- unchanged
+ else if top then subsumedCosts else useCurrentCostCentre
+ in
+ returnSUs (new_arity, StgRhsClosure new_cc bi fv ReEntrant (args++nns) body3)
+ )
+ `thenSUs` \ (arity, rhs2) ->
+ let
+ b2 = b `addIdArity` arity
+ in
+ returnSUs (b2, rhs2)
+\end{code}
+
+%************************************************************************
+%* *
+\subsection{Expressions}
+%* *
+%************************************************************************
+
+\begin{code}
+satExpr :: SatEnv -> PlainStgExpr -> SUniqSM (ExprArityInfo, PlainStgExpr)
+
+satExpr env app@(StgApp (StgLitAtom lit) [] lvs) = returnSUs (Nothing, app)
+
+satExpr env app@(StgApp (StgVarAtom f) as lvs)
+ = returnSUs (arity_to_return, StgApp (StgVarAtom f2) as2 lvs)
+ where
+ as2 = lookupArgs env as
+ f2 = lookupVar env f
+ arity_to_return = case arityMaybe (getIdArity f2) of
+ Nothing -> Nothing
+
+ Just f_arity -> if remaining_arity > 0
+ then Just remaining_arity
+ else Nothing
+ where
+ remaining_arity = f_arity - length as
+
+satExpr env app@(StgConApp con as lvs)
+ = returnSUs (Nothing, StgConApp con (lookupArgs env as) lvs)
+
+satExpr env app@(StgPrimApp op as lvs)
+ = returnSUs (Nothing, StgPrimApp op (lookupArgs env as) lvs)
+
+satExpr env (StgSCC ty l e)
+ = satExpr env e `thenSUs` \ (_, e2) ->
+ returnSUs (Nothing, StgSCC ty l e2)
+
+{- OMITTED: Let-no-escapery should come *after* saturation
+
+satExpr (StgLetNoEscape lvs_whole lvs_rhss binds body)
+ = satBinding binds `thenSUs` \ (binds2, c) ->
+ satExpr body `thenSUs` \ (_, body2, c2) ->
+ returnSUs (Nothing, StgLetNoEscape lvs_whole lvs_rhss binds2 body2, c + c2)
+-}
+
+satExpr env (StgLet binds body)
+ = satBinding False{-not top-level-} env binds `thenSUs` \ (env2, binds2) ->
+ satExpr env2 body `thenSUs` \ (_, body2) ->
+ returnSUs (Nothing, StgLet binds2 body2)
+
+satExpr env (StgCase expr lve lva uniq alts)
+ = satExpr env expr `thenSUs` \ (_, expr2) ->
+ sat_alts alts `thenSUs` \ alts2 ->
+ returnSUs (Nothing, StgCase expr2 lve lva uniq alts2)
+ where
+ sat_alts (StgAlgAlts ty alts def)
+ = mapSUs sat_alg_alt alts `thenSUs` \ alts2 ->
+ sat_deflt def `thenSUs` \ def2 ->
+ returnSUs (StgAlgAlts ty alts2 def2)
+ where
+ sat_alg_alt (id, bs, use_mask, e)
+ = satExpr env e `thenSUs` \ (_, e2) ->
+ returnSUs (id, bs, use_mask, e2)
+
+ sat_alts (StgPrimAlts ty alts def)
+ = mapSUs sat_prim_alt alts `thenSUs` \ alts2 ->
+ sat_deflt def `thenSUs` \ def2 ->
+ returnSUs (StgPrimAlts ty alts2 def2)
+ where
+ sat_prim_alt (l, e)
+ = satExpr env e `thenSUs` \ (_, e2) ->
+ returnSUs (l, e2)
+
+ sat_deflt StgNoDefault
+ = returnSUs StgNoDefault
+
+ sat_deflt (StgBindDefault b u expr)
+ = satExpr env expr `thenSUs` \ (_,expr2) ->
+ returnSUs (StgBindDefault b u expr2)
+\end{code}
+
+%************************************************************************
+%* *
+\subsection{Utility functions}
+%* *
+%************************************************************************
+
+\begin{code}
+saturate :: PlainStgExpr -> [PlainStgAtom] -> PlainStgExpr
+
+saturate (StgApp f as lvs) ids = StgApp f (as ++ ids) lvs
+saturate other _ = panic "SatStgRhs: saturate"
+\end{code}
+
+\begin{code}
+lookupArgs :: SatEnv -> [PlainStgAtom] -> [PlainStgAtom]
+lookupArgs env args = map do args
+ where
+ do (StgVarAtom v) = StgVarAtom (lookupVar env v)
+ do a@(StgLitAtom lit) = a
+
+lookupVar :: SatEnv -> Id -> Id
+lookupVar env v = case lookupIdEnv env v of
+ Nothing -> v
+ Just v2 -> v2
+
+newName :: UniType -> SUniqSM Id
+newName ut
+ = getSUnique `thenSUs` \ uniq ->
+ returnSUs (mkSysLocal SLIT("sat") uniq ut mkUnknownSrcLoc)
+\end{code}
diff --git a/ghc/compiler/simplStg/SimplStg.hi b/ghc/compiler/simplStg/SimplStg.hi
new file mode 100644
index 0000000000..08f6c91653
--- /dev/null
+++ b/ghc/compiler/simplStg/SimplStg.hi
@@ -0,0 +1,12 @@
+{-# GHC_PRAGMA INTERFACE VERSION 5 #-}
+interface SimplStg where
+import CmdLineOpts(GlobalSwitch, StgToDo, SwitchResult)
+import CostCentre(CostCentre)
+import Id(Id)
+import PreludePS(_PackedString)
+import Pretty(PprStyle)
+import SplitUniq(SplitUniqSupply)
+import StgSyn(StgBinding)
+stg2stg :: [StgToDo] -> (GlobalSwitch -> SwitchResult) -> _PackedString -> PprStyle -> SplitUniqSupply -> [StgBinding Id Id] -> _State _RealWorld -> (([StgBinding Id Id], ([CostCentre], [CostCentre])), _State _RealWorld)
+ {-# GHC_PRAGMA _A_ 7 _U_ 1222122 _N_ _S_ "SSLLU(ALL)LL" _N_ _N_ #-}
+
diff --git a/ghc/compiler/simplStg/SimplStg.lhs b/ghc/compiler/simplStg/SimplStg.lhs
new file mode 100644
index 0000000000..6fdb44c02c
--- /dev/null
+++ b/ghc/compiler/simplStg/SimplStg.lhs
@@ -0,0 +1,354 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1993-1995
+%
+\section[SimplStg]{Driver for simplifying @STG@ programs}
+
+\begin{code}
+#include "HsVersions.h"
+
+module SimplStg ( stg2stg ) where
+
+IMPORT_Trace
+
+import StgSyn
+import StgFuns
+
+import LambdaLift ( liftProgram )
+import SCCfinal ( stgMassageForProfiling )
+import SatStgRhs ( satStgRhs )
+import StgStats ( showStgStats )
+import StgVarInfo ( setStgVarInfo )
+import UpdAnal ( updateAnalyse )
+
+import CmdLineOpts
+import Id ( unlocaliseId )
+import IdEnv
+import MainMonad
+import Maybes ( maybeToBool, Maybe(..) )
+import Outputable
+import Pretty
+import SplitUniq
+import StgLint ( lintStgBindings )
+import StgSAT ( doStaticArgs )
+import UniqSet
+import Unique
+import Util
+\end{code}
+
+\begin{code}
+stg2stg :: [StgToDo] -- spec of what stg-to-stg passes to do
+ -> (GlobalSwitch -> SwitchResult)-- access to all global cmd-line opts
+ -> FAST_STRING -- module name (profiling only)
+ -> PprStyle -- printing style (for debugging only)
+ -> SplitUniqSupply -- a name supply
+ -> [PlainStgBinding] -- input...
+ -> MainIO
+ ([PlainStgBinding], -- output program...
+ ([CostCentre], -- local cost-centres that need to be decl'd
+ [CostCentre])) -- "extern" cost-centres
+
+stg2stg stg_todos sw_chkr module_name ppr_style us binds
+ = BSCC("Stg2Stg")
+ case (splitUniqSupply us) of { (us4now, us4later) ->
+
+ (if do_verbose_stg2stg then
+ writeMn stderr "VERBOSE STG-TO-STG:\n" `thenMn_`
+ writeMn stderr (ppShow 1000
+ (ppAbove (ppStr ("*** Core2Stg:"))
+ (ppAboves (map (ppr ppr_style) (setStgVarInfo False binds)))
+ ))
+ else returnMn ()) `thenMn_`
+
+ -- Do the main business!
+ foldl_mn do_stg_pass (binds, us4now, ([],[])) stg_todos
+ `thenMn` \ (processed_binds, _, cost_centres) ->
+ -- Do essential wind-up: part (a) is SatStgRhs
+
+ -- Not optional, because correct arity information is used by
+ -- the code generator. Afterwards do setStgVarInfo; it gives
+ -- the wrong answers if arities are subsequently changed,
+ -- which stgSatRhs might do. Furthermore, setStgVarInfo
+ -- decides about let-no-escape things, which in turn do a
+ -- better job if arities are correct, which is done by
+ -- satStgRhs.
+
+ case (satStgRhs processed_binds us4later) of { saturated_binds ->
+
+ -- Essential wind-up: part (b), eliminate indirections
+
+ let no_ind_binds = elimIndirections saturated_binds in
+
+
+ -- Essential wind-up: part (c), do setStgVarInfo. It has to
+ -- happen regardless, because the code generator uses its
+ -- decorations.
+ --
+ -- Why does it have to happen last? Because earlier passes
+ -- may move things around, which would change the live-var
+ -- info. Also, setStgVarInfo decides about let-no-escape
+ -- things, which in turn do a better job if arities are
+ -- correct, which is done by satStgRhs.
+ --
+ let
+ -- ToDo: provide proper flag control!
+ binds_to_mangle
+ = if not do_unlocalising
+ then no_ind_binds
+ else snd (unlocaliseStgBinds unlocal_tag nullIdEnv no_ind_binds)
+ in
+ returnMn (setStgVarInfo do_let_no_escapes binds_to_mangle, cost_centres)
+ }}
+ ESCC
+ where
+ switch_is_on = switchIsOn sw_chkr
+
+ do_let_no_escapes = switch_is_on StgDoLetNoEscapes
+ do_verbose_stg2stg = switch_is_on D_verbose_stg2stg
+
+ (do_unlocalising, unlocal_tag)
+ = case (stringSwitchSet sw_chkr EnsureSplittableC) of
+ Nothing -> (False, panic "tag")
+ Just tag -> (True, _PK_ tag)
+
+ grp_name = case (stringSwitchSet sw_chkr SccGroup) of
+ Just xx -> _PK_ xx
+ Nothing -> module_name -- default: module name
+
+ -------------
+ stg_linter = if False -- LATER: switch_is_on DoCoreLinting -- ToDo: DoStgLinting flag
+ then lintStgBindings ppr_style
+ else ( \ whodunnit binds -> binds )
+
+ -------------------------------------------
+ do_stg_pass (binds, us, ccs) to_do
+ = let
+ (us1, us2) = splitUniqSupply us
+ in
+ case to_do of
+ StgDoStaticArgs ->
+ ASSERT(null (fst ccs) && null (snd ccs))
+ BSCC("StgStaticArgs")
+ let
+ binds3 = doStaticArgs binds us1
+ in
+ end_pass us2 "StgStaticArgs" ccs binds3
+ ESCC
+
+ StgDoUpdateAnalysis ->
+ ASSERT(null (fst ccs) && null (snd ccs))
+ BSCC("StgUpdAnal")
+ -- NB We have to do setStgVarInfo first! (There's one
+ -- place free-var info is used) But no let-no-escapes,
+ -- because update analysis doesn't care.
+ end_pass us2 "UpdAnal" ccs (updateAnalyse (setStgVarInfo False binds))
+ ESCC
+
+ D_stg_stats ->
+ trace (showStgStats binds)
+ end_pass us2 "StgStats" ccs binds
+
+ StgDoLambdaLift ->
+ BSCC("StgLambdaLift")
+ -- NB We have to do setStgVarInfo first!
+ let
+ binds3 = liftProgram us1 (setStgVarInfo do_let_no_escapes binds)
+ in
+ end_pass us2 "LambdaLift" ccs binds3
+ ESCC
+
+ StgDoMassageForProfiling ->
+ BSCC("ProfMassage")
+ let
+ (collected_CCs, binds3)
+ = stgMassageForProfiling module_name grp_name us1 switch_is_on binds
+ in
+ end_pass us2 "ProfMassage" collected_CCs binds3
+ ESCC
+
+ end_pass us2 what ccs binds2
+ = -- report verbosely, if required
+ (if do_verbose_stg2stg then
+ writeMn stderr (ppShow 1000
+ (ppAbove (ppStr ("*** "++what++":"))
+ (ppAboves (map (ppr ppr_style) binds2))
+ ))
+ else returnMn ()) `thenMn_`
+ let
+ linted_binds = stg_linter what binds2
+ in
+ returnMn (linted_binds, us2, ccs)
+ -- return: processed binds
+ -- UniqueSupply for the next guy to use
+ -- cost-centres to be declared/registered (specialised)
+ -- add to description of what's happened (reverse order)
+
+-- here so it can be inlined...
+foldl_mn f z [] = returnMn z
+foldl_mn f z (x:xs) = f z x `thenMn` \ zz ->
+ foldl_mn f zz xs
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[SimplStg-unlocalise]{Unlocalisation in STG code}
+%* *
+%************************************************************************
+
+The idea of all this ``unlocalise'' stuff is that in certain (prelude
+only) modules we split up the .hc file into lots of separate little
+files, which are separately compiled by the C compiler. That gives
+lots of little .o files. The idea is that if you happen to mention
+one of them you don't necessarily pull them all in. (Pulling in a
+piece you don't need can be v bad, because it may mention other pieces
+you don't need either, and so on.)
+
+Sadly, splitting up .hc files means that local names (like s234) are
+now globally visible, which can lead to clashes between two .hc
+files. So unlocaliseWhatnot goes through making all the local things
+into global things, essentially by giving them full names so when they
+are printed they'll have their module name too. Pretty revolting
+really.
+
+\begin{code}
+type UnlocalEnv = IdEnv Id
+
+lookup_uenv :: UnlocalEnv -> Id -> Id
+lookup_uenv env id = case lookupIdEnv env id of
+ Nothing -> id
+ Just new_id -> new_id
+
+unlocaliseStgBinds :: FAST_STRING -> UnlocalEnv -> [PlainStgBinding] -> (UnlocalEnv, [PlainStgBinding])
+
+unlocaliseStgBinds mod uenv [] = (uenv, [])
+
+unlocaliseStgBinds mod uenv (b : bs)
+ = BIND unlocal_top_bind mod uenv b _TO_ (new_uenv, new_b) ->
+ BIND unlocaliseStgBinds mod new_uenv bs _TO_ (uenv3, new_bs) ->
+ (uenv3, new_b : new_bs)
+ BEND BEND
+
+------------------
+
+unlocal_top_bind :: FAST_STRING -> UnlocalEnv -> PlainStgBinding -> (UnlocalEnv, PlainStgBinding)
+
+unlocal_top_bind mod uenv bind@(StgNonRec binder _)
+ = let new_uenv = case unlocaliseId mod binder of
+ Nothing -> uenv
+ Just new_binder -> addOneToIdEnv uenv binder new_binder
+ in
+ (new_uenv, renameTopStgBind (lookup_uenv new_uenv) bind)
+
+unlocal_top_bind mod uenv bind@(StgRec pairs)
+ = let maybe_unlocaliseds = [ (b, unlocaliseId mod b) | (b, _) <- pairs ]
+ new_uenv = growIdEnvList uenv [ (b,new_b)
+ | (b, Just new_b) <- maybe_unlocaliseds]
+ in
+ (new_uenv, renameTopStgBind (lookup_uenv new_uenv) bind)
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[SimplStg-indirections]{Eliminating indirections in STG code}
+%* *
+%************************************************************************
+
+In @elimIndirections@, we look for things at the top-level of the form...
+\begin{verbatim}
+ x_local = ....rhs...
+ ...
+ x_exported = x_local
+ ...
+\end{verbatim}
+In cases we find like this, we go {\em backwards} and replace
+\tr{x_local} with \tr{...rhs...}, to produce
+\begin{verbatim}
+ x_exported = ...rhs...
+ ...
+ ...
+\end{verbatim}
+This saves a gratuitous jump
+(from \tr{x_exported} to \tr{x_local}), and makes strictness
+information propagate better.
+
+If more than one exported thing is equal to a local thing (i.e., the
+local thing really is shared), then we eliminate only the first one. Thus:
+\begin{verbatim}
+ x_local = ....rhs...
+ ...
+ x_exported1 = x_local
+ ...
+ x_exported2 = x_local
+ ...
+\end{verbatim}
+becomes
+\begin{verbatim}
+ x_exported1 = ....rhs...
+ ...
+ ...
+ x_exported2 = x_exported1
+ ...
+\end{verbatim}
+
+We also have to watch out for
+
+ f = \xyz -> g x y z
+
+This can arise post lambda lifting; the original might have been
+
+ f = \xyz -> letrec g = [xy] \ [k] -> e
+ in
+ g z
+
+Strategy: first collect the info; then make a \tr{Id -> Id} mapping.
+Then blast the whole program (LHSs as well as RHSs) with it.
+
+\begin{code}
+elimIndirections :: [PlainStgBinding] -> [PlainStgBinding]
+
+elimIndirections binds_in
+ = if isNullIdEnv blast_env then
+ binds_in -- Nothing to do
+ else
+ [renameTopStgBind lookup_fn bind | Just bind <- reduced_binds]
+ where
+ lookup_fn id = case lookupIdEnv blast_env id of
+ Just new_id -> new_id
+ Nothing -> id
+
+ (blast_env, reduced_binds) = mapAccumL try_bind nullIdEnv binds_in
+
+ try_bind :: IdEnv Id -> PlainStgBinding -> (IdEnv Id, Maybe PlainStgBinding)
+ try_bind env_so_far
+ (StgNonRec exported_binder
+ (StgRhsClosure _ _ _ _
+ lambda_args
+ (StgApp (StgVarAtom local_binder) fun_args _)
+ ))
+ | isExported exported_binder && -- Only if this is exported
+ not (isExported local_binder) && -- Only if this one is defined in this
+ isLocallyDefined local_binder && -- module, so that we *can* change its
+ -- binding to be the exported thing!
+ not (in_dom env_so_far local_binder) && -- Only if we havn't seen it before
+ args_match lambda_args fun_args -- Just an eta-expansion
+
+ = (addOneToIdEnv env_so_far local_binder exported_binder,
+ Nothing)
+ where
+ args_match [] [] = True
+ args_match (la:las) (StgVarAtom fa:fas) = la == fa && args_match las fas
+ args_match _ _ = False
+
+ try_bind env_so_far bind
+ = (env_so_far, Just bind)
+
+ in_dom env id = maybeToBool (lookupIdEnv env id)
+\end{code}
+
+@renameTopStgBind@ renames top level binders and all occurrences thereof.
+
+\begin{code}
+renameTopStgBind :: (Id -> Id) -> PlainStgBinding -> PlainStgBinding
+
+renameTopStgBind fn (StgNonRec b rhs) = StgNonRec (fn b) (mapStgBindeesRhs fn rhs)
+renameTopStgBind fn (StgRec pairs) = StgRec [ (fn b, mapStgBindeesRhs fn r) | (b, r) <- pairs ]
+\end{code}
diff --git a/ghc/compiler/simplStg/StgSAT.hi b/ghc/compiler/simplStg/StgSAT.hi
new file mode 100644
index 0000000000..91f7a35243
--- /dev/null
+++ b/ghc/compiler/simplStg/StgSAT.hi
@@ -0,0 +1,18 @@
+{-# GHC_PRAGMA INTERFACE VERSION 5 #-}
+interface StgSAT where
+import CostCentre(CostCentre)
+import Id(Id, IdDetails)
+import IdInfo(IdInfo)
+import PrimOps(PrimOp)
+import SplitUniq(SplitUniqSupply)
+import StgSyn(PlainStgProgram(..), StgAtom, StgBinding, StgCaseAlternatives, StgExpr, StgRhs)
+import UniType(UniType)
+import UniqFM(UniqFM)
+import Unique(Unique)
+data Id {-# GHC_PRAGMA Id Unique UniType IdInfo IdDetails #-}
+type PlainStgProgram = [StgBinding Id Id]
+data StgBinding a b {-# GHC_PRAGMA StgNonRec a (StgRhs a b) | StgRec [(a, StgRhs a b)] #-}
+data StgExpr a b {-# GHC_PRAGMA StgApp (StgAtom b) [StgAtom b] (UniqFM b) | StgConApp Id [StgAtom b] (UniqFM b) | StgPrimApp PrimOp [StgAtom b] (UniqFM b) | StgCase (StgExpr a b) (UniqFM b) (UniqFM b) Unique (StgCaseAlternatives a b) | StgLet (StgBinding a b) (StgExpr a b) | StgLetNoEscape (UniqFM b) (UniqFM b) (StgBinding a b) (StgExpr a b) | StgSCC UniType CostCentre (StgExpr a b) #-}
+doStaticArgs :: [StgBinding Id Id] -> SplitUniqSupply -> [StgBinding Id Id]
+ {-# GHC_PRAGMA _A_ 1 _U_ 22 _N_ _S_ "S" _N_ _N_ #-}
+
diff --git a/ghc/compiler/simplStg/StgSAT.lhs b/ghc/compiler/simplStg/StgSAT.lhs
new file mode 100644
index 0000000000..80cdec4208
--- /dev/null
+++ b/ghc/compiler/simplStg/StgSAT.lhs
@@ -0,0 +1,186 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1995
+%
+%************************************************************************
+%* *
+\section[SAT]{Static Argument Transformation pass}
+%* *
+%************************************************************************
+
+May be seen as removing invariants from loops:
+Arguments of recursive functions that do not change in recursive
+calls are removed from the recursion, which is done locally
+and only passes the arguments which effectively change.
+
+Example:
+map = /\ ab -> \f -> \xs -> case xs of
+ [] -> []
+ (a:b) -> f a : map f b
+
+as map is recursively called with the same argument f (unmodified)
+we transform it to
+
+map = /\ ab -> \f -> \xs -> let map' ys = case ys of
+ [] -> []
+ (a:b) -> f a : map' b
+ in map' xs
+
+Notice that for a compiler that uses lambda lifting this is
+useless as map' will be transformed back to what map was.
+
+\begin{code}
+#include "HsVersions.h"
+
+module StgSAT (
+ doStaticArgs,
+
+ -- and to make the interface self-sufficient...
+ PlainStgProgram(..), StgExpr, StgBinding, Id
+ ) where
+
+import IdEnv
+import Maybes ( Maybe(..) )
+import StgSyn
+import SATMonad ( SATEnv(..), SATInfo(..), Arg(..), updSAEnv, insSAEnv,
+ SatM(..), initSAT, thenSAT, thenSAT_,
+ emptyEnvSAT, returnSAT, mapSAT )
+import StgSATMonad
+import SplitUniq
+import Util
+\end{code}
+
+\begin{code}
+doStaticArgs :: PlainStgProgram -> SplitUniqSupply -> PlainStgProgram
+
+doStaticArgs binds
+ = initSAT (mapSAT sat_bind binds)
+ where
+ sat_bind (StgNonRec binder expr)
+ = emptyEnvSAT `thenSAT_`
+ satRhs expr `thenSAT` (\ expr' ->
+ returnSAT (StgNonRec binder expr'))
+ sat_bind (StgRec [(binder,rhs)])
+ = emptyEnvSAT `thenSAT_`
+ insSAEnv binder (getArgLists rhs) `thenSAT_`
+ satRhs rhs `thenSAT` (\ rhs' ->
+ saTransform binder rhs')
+ sat_bind (StgRec pairs)
+ = emptyEnvSAT `thenSAT_`
+ mapSAT satRhs rhss `thenSAT` \ rhss' ->
+ returnSAT (StgRec (binders `zip` rhss'))
+ where
+ (binders, rhss) = unzip pairs
+\end{code}
+
+\begin{code}
+satAtom (StgVarAtom v)
+ = updSAEnv (Just (v,([],[]))) `thenSAT_`
+ returnSAT ()
+
+satAtom _ = returnSAT ()
+\end{code}
+
+\begin{code}
+satExpr :: PlainStgExpr -> SatM PlainStgExpr
+
+satExpr e@(StgConApp con args lvs)
+ = mapSAT satAtom args `thenSAT_`
+ returnSAT e
+
+satExpr e@(StgPrimApp op args lvs)
+ = mapSAT satAtom args `thenSAT_`
+ returnSAT e
+
+satExpr e@(StgApp (StgLitAtom _) _ _)
+ = returnSAT e
+
+satExpr e@(StgApp (StgVarAtom v) args _)
+ = updSAEnv (Just (v,([],map tagArg args))) `thenSAT_`
+ mapSAT satAtom args `thenSAT_`
+ returnSAT e
+ where
+ tagArg (StgVarAtom v) = Static v
+ tagArg _ = NotStatic
+
+satExpr (StgCase expr lv1 lv2 uniq alts)
+ = satExpr expr `thenSAT` \ expr' ->
+ sat_alts alts `thenSAT` \ alts' ->
+ returnSAT (StgCase expr' lv1 lv2 uniq alts')
+ where
+ sat_alts (StgAlgAlts ty alts deflt)
+ = mapSAT satAlgAlt alts `thenSAT` \ alts' ->
+ sat_default deflt `thenSAT` \ deflt' ->
+ returnSAT (StgAlgAlts ty alts' deflt')
+ where
+ satAlgAlt (con, params, use_mask, rhs)
+ = satExpr rhs `thenSAT` \ rhs' ->
+ returnSAT (con, params, use_mask, rhs')
+
+ sat_alts (StgPrimAlts ty alts deflt)
+ = mapSAT satPrimAlt alts `thenSAT` \ alts' ->
+ sat_default deflt `thenSAT` \ deflt' ->
+ returnSAT (StgPrimAlts ty alts' deflt')
+ where
+ satPrimAlt (lit, rhs)
+ = satExpr rhs `thenSAT` \ rhs' ->
+ returnSAT (lit, rhs')
+
+ sat_default StgNoDefault
+ = returnSAT StgNoDefault
+ sat_default (StgBindDefault binder used rhs)
+ = satExpr rhs `thenSAT` \ rhs' ->
+ returnSAT (StgBindDefault binder used rhs')
+
+satExpr (StgLetNoEscape lv1 lv2 (StgNonRec binder rhs) body)
+ = satExpr body `thenSAT` \ body' ->
+ satRhs rhs `thenSAT` \ rhs' ->
+ returnSAT (StgLetNoEscape lv1 lv2 (StgNonRec binder rhs') body')
+
+satExpr (StgLetNoEscape lv1 lv2 (StgRec [(binder,rhs)]) body)
+ = satExpr body `thenSAT` \ body' ->
+ insSAEnv binder (getArgLists rhs) `thenSAT_`
+ satRhs rhs `thenSAT` \ rhs' ->
+ saTransform binder rhs' `thenSAT` \ binding ->
+ returnSAT (StgLetNoEscape lv1 lv2 binding body')
+
+satExpr (StgLetNoEscape lv1 lv2 (StgRec binds) body)
+ = let (binders, rhss) = unzip binds
+ in
+ satExpr body `thenSAT` \ body' ->
+ mapSAT satRhs rhss `thenSAT` \ rhss' ->
+ returnSAT (StgLetNoEscape lv1 lv2 (StgRec (binders `zip` rhss')) body')
+
+satExpr (StgLet (StgNonRec binder rhs) body)
+ = satExpr body `thenSAT` \ body' ->
+ satRhs rhs `thenSAT` \ rhs' ->
+ returnSAT (StgLet (StgNonRec binder rhs') body')
+
+satExpr (StgLet (StgRec [(binder,rhs)]) body)
+ = satExpr body `thenSAT` \ body' ->
+ insSAEnv binder (getArgLists rhs) `thenSAT_`
+ satRhs rhs `thenSAT` \ rhs' ->
+ saTransform binder rhs' `thenSAT` \ binding ->
+ returnSAT (StgLet binding body')
+
+satExpr (StgLet (StgRec binds) body)
+ = let (binders, rhss) = unzip binds
+ in
+ satExpr body `thenSAT` \ body' ->
+ mapSAT satRhs rhss `thenSAT` \ rhss' ->
+ returnSAT (StgLet (StgRec (binders `zip` rhss')) body')
+
+satExpr (StgSCC ty cc expr)
+ = satExpr expr `thenSAT` \ expr' ->
+ returnSAT (StgSCC ty cc expr')
+
+-- ToDo: DPH stuff
+\end{code}
+
+\begin{code}
+satRhs rhs@(StgRhsCon cc v args) = returnSAT rhs
+satRhs (StgRhsClosure cc bi fvs upd args body)
+ = satExpr body `thenSAT` \ body' ->
+ returnSAT (StgRhsClosure cc bi fvs upd args body')
+
+\end{code}
+
diff --git a/ghc/compiler/simplStg/StgSATMonad.hi b/ghc/compiler/simplStg/StgSATMonad.hi
new file mode 100644
index 0000000000..a6940eb0d3
--- /dev/null
+++ b/ghc/compiler/simplStg/StgSATMonad.hi
@@ -0,0 +1,22 @@
+{-# GHC_PRAGMA INTERFACE VERSION 5 #-}
+interface StgSATMonad where
+import Class(Class)
+import Id(Id, IdDetails)
+import IdInfo(IdInfo)
+import SATMonad(Arg)
+import SplitUniq(SplitUniqSupply)
+import StgSyn(PlainStgExpr(..), StgBinding, StgExpr, StgRhs)
+import TyCon(TyCon)
+import TyVar(TyVar, TyVarTemplate)
+import UniType(UniType)
+import UniqFM(UniqFM)
+import Unique(Unique)
+data Id {-# GHC_PRAGMA Id Unique UniType IdInfo IdDetails #-}
+data SplitUniqSupply {-# GHC_PRAGMA MkSplitUniqSupply Int SplitUniqSupply SplitUniqSupply #-}
+type PlainStgExpr = StgExpr Id Id
+data UniType {-# GHC_PRAGMA UniTyVar TyVar | UniFun UniType UniType | UniData TyCon [UniType] | UniSyn TyCon [UniType] UniType | UniDict Class UniType | UniTyVarTemplate TyVarTemplate | UniForall TyVarTemplate UniType #-}
+getArgLists :: StgRhs Id Id -> ([Arg UniType], [Arg Id])
+ {-# GHC_PRAGMA _A_ 1 _U_ 1 _N_ _S_ "S" _N_ _N_ #-}
+saTransform :: Id -> StgRhs Id Id -> SplitUniqSupply -> UniqFM ([Arg UniType], [Arg Id]) -> (StgBinding Id Id, UniqFM ([Arg UniType], [Arg Id]))
+ {-# GHC_PRAGMA _A_ 4 _U_ 2212 _N_ _S_ "LLU(LLL)L" _N_ _N_ #-}
+
diff --git a/ghc/compiler/simplStg/StgSATMonad.lhs b/ghc/compiler/simplStg/StgSATMonad.lhs
new file mode 100644
index 0000000000..f0cb84d4d1
--- /dev/null
+++ b/ghc/compiler/simplStg/StgSATMonad.lhs
@@ -0,0 +1,182 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1995
+%
+%************************************************************************
+%* *
+\section[SATMonad]{The Static Argument Transformation pass Monad}
+%* *
+%************************************************************************
+
+\begin{code}
+#include "HsVersions.h"
+
+module StgSATMonad (
+ getArgLists, saTransform,
+
+ Id, UniType, SplitUniqSupply, PlainStgExpr(..)
+ ) where
+
+import AbsUniType ( mkTyVarTy, mkSigmaTy, TyVarTemplate,
+ extractTyVarsFromTy, splitType, splitTyArgs,
+ glueTyArgs, instantiateTy, TauType(..),
+ Class, ThetaType(..), SigmaType(..),
+ InstTyEnv(..)
+ )
+import IdEnv
+import Id ( mkSysLocal, getIdUniType, eqId )
+import Maybes ( Maybe(..) )
+import StgSyn
+import SATMonad ( SATEnv(..), SATInfo(..), Arg(..), updSAEnv, insSAEnv,
+ SatM(..), initSAT, thenSAT, thenSAT_,
+ emptyEnvSAT, returnSAT, mapSAT, isStatic, dropStatics,
+ getSATInfo, newSATName )
+import SrcLoc ( SrcLoc, mkUnknownSrcLoc )
+import SplitUniq
+import Unique
+import UniqSet ( UniqSet(..), emptyUniqSet )
+import Util
+
+\end{code}
+
+%************************************************************************
+%* *
+\subsection{Utility Functions}
+%* *
+%************************************************************************
+
+\begin{code}
+newSATNames :: [Id] -> SatM [Id]
+newSATNames [] = returnSAT []
+newSATNames (id:ids) = newSATName id (getIdUniType id) `thenSAT` \ id' ->
+ newSATNames ids `thenSAT` \ ids' ->
+ returnSAT (id:ids)
+
+getArgLists :: PlainStgRhs -> ([Arg UniType],[Arg Id])
+getArgLists (StgRhsCon _ _ _)
+ = ([],[])
+getArgLists (StgRhsClosure _ _ _ _ args _)
+ = ([], [Static v | v <- args])
+
+\end{code}
+
+\begin{code}
+saTransform :: Id -> PlainStgRhs -> SatM PlainStgBinding
+saTransform binder rhs
+ = getSATInfo binder `thenSAT` \ r ->
+ case r of
+ Just (_,args) | any isStatic args
+ -- [Andre] test: do it only if we have more than one static argument.
+ --Just (_,args) | length (filter isStatic args) > 1
+ -> newSATName binder (new_ty args) `thenSAT` \ binder' ->
+ let non_static_args = get_nsa args (snd (getArgLists rhs))
+ in
+ newSATNames non_static_args `thenSAT` \ non_static_args' ->
+ mkNewRhs binder binder' args rhs non_static_args' non_static_args
+ `thenSAT` \ new_rhs ->
+ trace ("SAT(STG) "++ show (length (filter isStatic args))) (
+ returnSAT (StgNonRec binder new_rhs)
+ )
+ _ -> returnSAT (StgRec [(binder, rhs)])
+
+ where
+ get_nsa :: [Arg a] -> [Arg a] -> [a]
+ get_nsa [] _ = []
+ get_nsa _ [] = []
+ get_nsa (NotStatic:args) (Static v:as) = v:get_nsa args as
+ get_nsa (_:args) (_:as) = get_nsa args as
+
+ mkNewRhs binder binder' args rhs@(StgRhsClosure cc bi fvs upd rhsargs body) non_static_args' non_static_args
+ = let
+ local_body = StgApp (StgVarAtom binder')
+ [StgVarAtom a | a <- non_static_args] emptyUniqSet
+
+ rec_body = StgRhsClosure cc bi fvs upd non_static_args'
+ (doStgSubst binder args subst_env body)
+
+ subst_env = mkIdEnv
+ ((binder,binder'):zip non_static_args non_static_args')
+ in
+ returnSAT (
+ StgRhsClosure cc bi fvs upd rhsargs
+ (StgLet (StgRec [(binder',rec_body)]) {-in-} local_body)
+ )
+
+ new_ty args
+ = instantiateTy [] (mkSigmaTy [] dict_tys' tau_ty')
+ where
+ -- get type info for the local function:
+ (tv_tmpl, dict_tys, tau_ty) = (splitType . getIdUniType) binder
+ (reg_arg_tys, res_type) = splitTyArgs tau_ty
+
+ -- now, we drop the ones that are
+ -- static, that is, the ones we will not pass to the local function
+ l = length dict_tys
+ dict_tys' = dropStatics (take l args) dict_tys
+ reg_arg_tys' = dropStatics (drop l args) reg_arg_tys
+ tau_ty' = glueTyArgs reg_arg_tys' res_type
+\end{code}
+
+NOTE: This does not keep live variable/free variable information!!
+
+\begin{code}
+doStgSubst binder orig_args subst_env body
+ = substExpr body
+ where
+ substExpr (StgConApp con args lvs)
+ = StgConApp con (map substAtom args) emptyUniqSet
+ substExpr (StgPrimApp op args lvs)
+ = StgPrimApp op (map substAtom args) emptyUniqSet
+ substExpr expr@(StgApp (StgLitAtom _) [] _)
+ = expr
+ substExpr (StgApp atom@(StgVarAtom v) args lvs)
+ | v `eqId` binder
+ = StgApp (StgVarAtom (lookupNoFailIdEnv subst_env v))
+ (remove_static_args orig_args args) emptyUniqSet
+ | otherwise
+ = StgApp (substAtom atom) (map substAtom args) lvs
+ substExpr (StgCase scrut lv1 lv2 uniq alts)
+ = StgCase (substExpr scrut) emptyUniqSet emptyUniqSet uniq (subst_alts alts)
+ where
+ subst_alts (StgAlgAlts ty alg_alts deflt)
+ = StgAlgAlts ty (map subst_alg_alt alg_alts) (subst_deflt deflt)
+ subst_alts (StgPrimAlts ty prim_alts deflt)
+ = StgPrimAlts ty (map subst_prim_alt prim_alts) (subst_deflt deflt)
+ subst_alg_alt (con, args, use_mask, rhs)
+ = (con, args, use_mask, substExpr rhs)
+ subst_prim_alt (lit, rhs)
+ = (lit, substExpr rhs)
+ subst_deflt StgNoDefault
+ = StgNoDefault
+ subst_deflt (StgBindDefault var used rhs)
+ = StgBindDefault var used (substExpr rhs)
+ substExpr (StgLetNoEscape fv1 fv2 b body)
+ = StgLetNoEscape emptyUniqSet emptyUniqSet (substBinding b) (substExpr body)
+ substExpr (StgLet b body)
+ = StgLet (substBinding b) (substExpr body)
+ substExpr (StgSCC ty cc expr)
+ = StgSCC ty cc (substExpr expr)
+ substRhs (StgRhsCon cc v args)
+ = StgRhsCon cc v (map substAtom args)
+ substRhs (StgRhsClosure cc bi fvs upd args body)
+ = StgRhsClosure cc bi [] upd args (substExpr body)
+
+ substBinding (StgNonRec binder rhs)
+ = StgNonRec binder (substRhs rhs)
+ substBinding (StgRec pairs)
+ = StgRec (zip binders (map substRhs rhss))
+ where
+ (binders,rhss) = unzip pairs
+
+ substAtom atom@(StgLitAtom lit) = atom
+ substAtom atom@(StgVarAtom v)
+ = case lookupIdEnv subst_env v of
+ Just v' -> StgVarAtom v'
+ Nothing -> atom
+
+ remove_static_args _ []
+ = []
+ remove_static_args (Static _:origs) (_:as)
+ = remove_static_args origs as
+ remove_static_args (NotStatic:origs) (a:as)
+ = substAtom a:remove_static_args origs as
+\end{code}
diff --git a/ghc/compiler/simplStg/StgStats.hi b/ghc/compiler/simplStg/StgStats.hi
new file mode 100644
index 0000000000..7dc9282ed6
--- /dev/null
+++ b/ghc/compiler/simplStg/StgStats.hi
@@ -0,0 +1,7 @@
+{-# GHC_PRAGMA INTERFACE VERSION 5 #-}
+interface StgStats where
+import Id(Id)
+import StgSyn(StgBinding)
+showStgStats :: [StgBinding Id Id] -> [Char]
+ {-# GHC_PRAGMA _A_ 1 _U_ 1 _N_ _S_ "S" _N_ _N_ #-}
+
diff --git a/ghc/compiler/simplStg/StgStats.lhs b/ghc/compiler/simplStg/StgStats.lhs
new file mode 100644
index 0000000000..2b16fc06c9
--- /dev/null
+++ b/ghc/compiler/simplStg/StgStats.lhs
@@ -0,0 +1,188 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1994
+%
+\section[StgStats]{Gathers statistical information about programs}
+
+
+The program gather statistics about
+\begin{enumerate}
+\item number of boxed cases
+\item number of unboxed cases
+\item number of let-no-escapes
+\item number of non-updatable lets
+\item number of updatable lets
+\item number of applications
+\item number of primitive applications
+\item number of closures (does not include lets bound to constructors)
+\item number of free variables in closures
+%\item number of top-level functions
+%\item number of top-level CAFs
+\item number of constructors
+\end{enumerate}
+
+\begin{code}
+#include "HsVersions.h"
+
+module StgStats ( showStgStats ) where
+
+import StgSyn
+
+import FiniteMap
+
+import Util
+\end{code}
+
+\begin{code}
+data CounterType
+ = AlgCases
+ | PrimCases
+ | LetNoEscapes
+ | NonUpdatableLets
+ | UpdatableLets
+ | Applications
+ | PrimitiveApps
+ | FreeVariables
+ | Closures -- does not include lets bound to constructors
+--| UpdatableTopLevelDefs
+--| NonUpdatableTopLevelDefs
+ | Constructors
+ deriving (Eq, Ord, Text)
+
+type Count = Int
+type StatEnv = FiniteMap CounterType Count
+\end{code}
+
+\begin{code}
+emptySE :: StatEnv
+emptySE = emptyFM
+
+combineSE :: StatEnv -> StatEnv -> StatEnv
+combineSE = plusFM_C (+)
+
+combineSEs :: [StatEnv] -> StatEnv
+combineSEs = foldr combineSE emptySE
+
+countOne :: CounterType -> StatEnv
+countOne c = singletonFM c 1
+
+countN :: CounterType -> Int -> StatEnv
+countN = singletonFM
+\end{code}
+
+%************************************************************************
+%* *
+\subsection{Top-level list of bindings (a ``program'')}
+%* *
+%************************************************************************
+
+\begin{code}
+showStgStats :: PlainStgProgram -> String
+showStgStats prog = concat (map showc (fmToList (gatherStgStats prog)))
+ where
+ showc (AlgCases,n) = "AlgCases " ++ show n ++ "\n"
+ showc (PrimCases,n) = "PrimCases " ++ show n ++ "\n"
+ showc (LetNoEscapes,n) = "LetNoEscapes " ++ show n ++ "\n"
+ showc (NonUpdatableLets,n) = "NonUpdatableLets " ++ show n ++ "\n"
+ showc (UpdatableLets,n) = "UpdatableLets " ++ show n ++ "\n"
+ showc (Applications,n) = "Applications " ++ show n ++ "\n"
+ showc (PrimitiveApps,n) = "PrimitiveApps " ++ show n ++ "\n"
+ showc (Closures,n) = "Closures " ++ show n ++ "\n"
+ showc (FreeVariables,n) = "Free Vars in Closures " ++ show n ++ "\n"
+ showc (Constructors,n) = "Constructors " ++ show n ++ "\n"
+
+gatherStgStats :: PlainStgProgram -> StatEnv
+
+gatherStgStats binds
+ = combineSEs (map statBinding binds)
+\end{code}
+
+%************************************************************************
+%* *
+\subsection{Bindings}
+%* *
+%************************************************************************
+
+\begin{code}
+statBinding :: PlainStgBinding -> StatEnv
+
+statBinding (StgNonRec b rhs)
+ = statRhs (b, rhs)
+
+statBinding (StgRec pairs)
+ = combineSEs (map statRhs pairs)
+
+statRhs :: (Id, PlainStgRhs) -> StatEnv
+
+statRhs (b, StgRhsCon cc con args)
+ = countOne Constructors `combineSE`
+ countOne NonUpdatableLets
+
+statRhs (b, StgRhsClosure cc bi fv u args body)
+ = statExpr body `combineSE`
+ countN FreeVariables (length fv) `combineSE`
+ countOne Closures `combineSE`
+ (case u of
+ Updatable -> countOne UpdatableLets
+ _ -> countOne NonUpdatableLets)
+
+\end{code}
+
+%************************************************************************
+%* *
+\subsection{Expressions}
+%* *
+%************************************************************************
+
+\begin{code}
+statExpr :: PlainStgExpr -> StatEnv
+
+statExpr (StgApp _ [] lvs)
+ = emptySE
+statExpr (StgApp _ _ lvs)
+ = countOne Applications
+
+statExpr (StgConApp con as lvs)
+ = countOne Constructors
+
+statExpr (StgPrimApp op as lvs)
+ = countOne PrimitiveApps
+
+statExpr (StgSCC ty l e)
+ = statExpr e
+
+statExpr (StgLetNoEscape lvs_whole lvs_rhss binds body)
+ = statBinding binds `combineSE`
+ statExpr body `combineSE`
+ countOne LetNoEscapes
+
+statExpr (StgLet binds body)
+ = statBinding binds `combineSE`
+ statExpr body
+
+statExpr (StgCase expr lve lva uniq alts)
+ = statExpr expr `combineSE`
+ stat_alts alts
+ where
+ stat_alts (StgAlgAlts ty alts def)
+ = combineSEs (map stat_alg_alt alts) `combineSE`
+ stat_deflt def `combineSE`
+ countOne AlgCases
+ where
+ stat_alg_alt (id, bs, use_mask, e)
+ = statExpr e
+
+ stat_alts (StgPrimAlts ty alts def)
+ = combineSEs (map stat_prim_alt alts) `combineSE`
+ stat_deflt def `combineSE`
+ countOne PrimCases
+ where
+ stat_prim_alt (l, e)
+ = statExpr e
+
+ stat_deflt StgNoDefault
+ = emptySE
+
+ stat_deflt (StgBindDefault b u expr)
+ = statExpr expr
+\end{code}
+
diff --git a/ghc/compiler/simplStg/StgVarInfo.hi b/ghc/compiler/simplStg/StgVarInfo.hi
new file mode 100644
index 0000000000..52f36e0ffd
--- /dev/null
+++ b/ghc/compiler/simplStg/StgVarInfo.hi
@@ -0,0 +1,7 @@
+{-# GHC_PRAGMA INTERFACE VERSION 5 #-}
+interface StgVarInfo where
+import Id(Id)
+import StgSyn(StgBinding)
+setStgVarInfo :: Bool -> [StgBinding Id Id] -> [StgBinding Id Id]
+ {-# GHC_PRAGMA _A_ 2 _U_ 22 _N_ _S_ "LS" _N_ _N_ #-}
+
diff --git a/ghc/compiler/simplStg/StgVarInfo.lhs b/ghc/compiler/simplStg/StgVarInfo.lhs
new file mode 100644
index 0000000000..10d618c4a7
--- /dev/null
+++ b/ghc/compiler/simplStg/StgVarInfo.lhs
@@ -0,0 +1,790 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1993-1995
+%
+\section[StgVarInfo]{Sets free/live variable info in STG syntax}
+
+And, as we have the info in hand, we may convert some lets to
+let-no-escapes.
+
+\begin{code}
+#include "HsVersions.h"
+
+module StgVarInfo ( setStgVarInfo ) where
+
+IMPORT_Trace -- ToDo: rm (debugging only)
+import Pretty
+import Outputable
+
+import StgSyn
+
+import Id ( getIdArity, externallyVisibleId )
+import IdInfo -- ( arityMaybe, ArityInfo )
+
+import IdEnv
+import Maybes ( maybeToBool, Maybe(..) )
+import UniqSet
+import Util
+
+infixr 9 `thenLne`, `thenLne_`
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[live-vs-free-doc]{Documentation}
+%* *
+%************************************************************************
+
+(There is other relevant documentation in codeGen/CgLetNoEscape.)
+
+The actual Stg datatype is decorated with {\em live variable}
+information, as well as {\em free variable} information. The two are
+{\em not} the same. Liveness is an operational property rather than a
+semantic one. A variable is live at a particular execution point if
+it can be referred to {\em directly} again. In particular, a dead
+variable's stack slot (if it has one):
+\begin{enumerate}
+\item
+should be stubbed to avoid space leaks, and
+\item
+may be reused for something else.
+\end{enumerate}
+
+There ought to be a better way to say this. Here are some examples:
+\begin{verbatim}
+ let v = [q] \[x] -> e
+ in
+ ...v... (but no q's)
+\end{verbatim}
+
+Just after the `in', v is live, but q is dead. If the whole of that
+let expression was enclosed in a case expression, thus:
+\begin{verbatim}
+ case (let v = [q] \[x] -> e in ...v...) of
+ alts[...q...]
+\end{verbatim}
+(ie @alts@ mention @q@), then @q@ is live even after the `in'; because
+we'll return later to the @alts@ and need it.
+
+Let-no-escapes make this a bit more interesting:
+\begin{verbatim}
+ let-no-escape v = [q] \ [x] -> e
+ in
+ ...v...
+\end{verbatim}
+Here, @q@ is still live at the `in', because @v@ is represented not by
+a closure but by the current stack state. In other words, if @v@ is
+live then so is @q@. Furthermore, if @e@ mentions an enclosing
+let-no-escaped variable, then {\em its} free variables are also live
+if @v@ is.
+
+%************************************************************************
+%* *
+\subsection[binds-StgVarInfo]{Setting variable info: top-level, binds, RHSs}
+%* *
+%************************************************************************
+
+Top-level:
+\begin{code}
+setStgVarInfo :: Bool -- True <=> do let-no-escapes
+ -> [PlainStgBinding] -- input
+ -> [PlainStgBinding] -- result
+
+setStgVarInfo want_LNEs pgm
+ = pgm'
+ where
+ (pgm', _) = initLne want_LNEs (varsTopBinds pgm)
+
+\end{code}
+
+For top-level guys, we basically aren't worried about this
+live-variable stuff; we do need to keep adding to the environment
+as we step through the bindings (using @extendVarEnv@).
+
+\begin{code}
+varsTopBinds :: [PlainStgBinding] -> LneM ([PlainStgBinding], FreeVarsInfo)
+
+varsTopBinds [] = returnLne ([], emptyFVInfo)
+varsTopBinds (bind:binds)
+ = extendVarEnv env_extension (
+ varsTopBinds binds `thenLne` \ (binds', fv_binds) ->
+ varsTopBind fv_binds bind `thenLne` \ (bind', fv_bind) ->
+ returnLne ((bind' : binds'),
+ (fv_binds `unionFVInfo` fv_bind) `minusFVBinders` binders
+ )
+
+ )
+ where
+ env_extension = [(b, LetrecBound
+ True {- top level -}
+ (rhsArity rhs)
+ emptyUniqSet)
+ | (b,rhs) <- pairs]
+
+ pairs = case bind of
+ StgNonRec binder rhs -> [(binder,rhs)]
+ StgRec pairs -> pairs
+
+ binders = [b | (b,_) <- pairs]
+
+
+varsTopBind :: FreeVarsInfo -- Info about the body
+ -> PlainStgBinding
+ -> LneM (PlainStgBinding, FreeVarsInfo)
+
+varsTopBind body_fvs (StgNonRec binder rhs)
+ = varsRhs body_fvs (binder,rhs) `thenLne` \ (rhs2, fvs, _) ->
+ returnLne (StgNonRec binder rhs2, fvs)
+
+varsTopBind body_fvs (StgRec pairs)
+ = let
+ (binders, rhss) = unzip pairs
+ in
+ fixLne (\ ~(_, rec_rhs_fvs) ->
+ let
+ scope_fvs = unionFVInfo body_fvs rec_rhs_fvs
+ in
+ mapAndUnzip3Lne (varsRhs scope_fvs) pairs `thenLne` \ (rhss2, fvss, _) ->
+ let
+ fvs = unionFVInfos fvss
+ in
+ returnLne (StgRec (binders `zip` rhss2), fvs)
+ )
+
+\end{code}
+
+\begin{code}
+varsRhs :: FreeVarsInfo -- Free var info for the scope of the binding
+ -> (Id,PlainStgRhs)
+ -> LneM (PlainStgRhs, FreeVarsInfo, EscVarsSet)
+
+varsRhs scope_fv_info (binder, StgRhsCon cc con args)
+ = varsAtoms args `thenLne` \ fvs ->
+ returnLne (StgRhsCon cc con args, fvs, getFVSet fvs)
+
+varsRhs scope_fv_info (binder, StgRhsClosure cc _ _ upd args body)
+ = extendVarEnv [ (a, LambdaBound) | a <- args ] (
+ do_body args body `thenLne` \ (body2, body_fvs, body_escs) ->
+ let
+ set_of_args = mkUniqSet args
+ rhs_fvs = body_fvs `minusFVBinders` args
+ rhs_escs = body_escs `minusUniqSet` set_of_args
+ binder_info = lookupFVInfo scope_fv_info binder
+ in
+ returnLne (StgRhsClosure cc binder_info (getFVs rhs_fvs) upd args body2,
+ rhs_fvs, rhs_escs)
+ )
+ where
+ -- Pick out special case of application in body of thunk
+ do_body [] (StgApp (StgVarAtom f) args _) = varsApp (Just upd) f args
+ do_body _ other_body = varsExpr other_body
+\end{code}
+
+\begin{code}
+varsAtoms :: [PlainStgAtom]
+ -> LneM FreeVarsInfo
+
+varsAtoms atoms
+ = mapLne var_atom atoms `thenLne` \ fvs_lists ->
+ returnLne (unionFVInfos fvs_lists)
+ where
+ var_atom a@(StgLitAtom _) = returnLne emptyFVInfo
+ var_atom a@(StgVarAtom v)
+ = lookupVarEnv v `thenLne` \ how_bound ->
+ returnLne (singletonFVInfo v how_bound stgArgOcc)
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[expr-StgVarInfo]{Setting variable info on expressions}
+%* *
+%************************************************************************
+
+@varsExpr@ carries in a monad-ised environment, which binds each
+let(rec) variable (ie non top level, not imported, not lambda bound,
+not case-alternative bound) to:
+ - its STG arity, and
+ - its set of live vars.
+For normal variables the set of live vars is just the variable
+itself. For let-no-escaped variables, the set of live vars is the set
+live at the moment the variable is entered. The set is guaranteed to
+have no further let-no-escaped vars in it.
+
+\begin{code}
+varsExpr :: PlainStgExpr
+ -> LneM (PlainStgExpr, -- Decorated expr
+ FreeVarsInfo, -- Its free vars (NB free, not live)
+ EscVarsSet) -- Its escapees, a subset of its free vars;
+ -- also a subset of the domain of the envt
+ -- because we are only interested in the escapees
+ -- for vars which might be turned into
+ -- let-no-escaped ones.
+\end{code}
+
+The second and third components can be derived in a simple bottom up pass, not
+dependent on any decisions about which variables will be let-no-escaped or
+not. The first component, that is, the decorated expression, may then depend
+on these components, but it in turn is not scrutinised as the basis for any
+decisions. Hence no black holes.
+
+\begin{code}
+varsExpr (StgApp lit@(StgLitAtom _) args _)
+ = --(if null args then id else (trace (ppShow 80 (ppr PprShowAll args)))) (
+ returnLne (StgApp lit [] emptyUniqSet, emptyFVInfo, emptyUniqSet)
+ --)
+
+varsExpr (StgApp fun@(StgVarAtom f) args _) = varsApp Nothing f args
+
+varsExpr (StgConApp con args _)
+ = getVarsLiveInCont `thenLne` \ live_in_cont ->
+ varsAtoms args `thenLne` \ args_fvs ->
+
+ returnLne (StgConApp con args live_in_cont, args_fvs, getFVSet args_fvs)
+
+varsExpr (StgPrimApp op args _)
+ = getVarsLiveInCont `thenLne` \ live_in_cont ->
+ varsAtoms args `thenLne` \ args_fvs ->
+
+ returnLne (StgPrimApp op args live_in_cont, args_fvs, getFVSet args_fvs)
+
+varsExpr (StgSCC ty label expr)
+ = varsExpr expr `thenLne` ( \ (expr2, fvs, escs) ->
+ returnLne (StgSCC ty label expr2, fvs, escs) )
+\end{code}
+
+Cases require a little more real work.
+\begin{code}
+varsExpr (StgCase scrut _ _ uniq alts)
+ = getVarsLiveInCont `thenLne` \ live_in_cont ->
+ vars_alts alts `thenLne` \ (alts2, alts_fvs, alts_escs) ->
+ lookupLiveVarsForSet alts_fvs `thenLne` \ alts_lvs ->
+ let
+ live_in_alts = live_in_cont `unionUniqSets` alts_lvs
+ in
+ -- we tell the scrutinee that everything live in the alts
+ -- is live in it, too.
+ setVarsLiveInCont live_in_alts (
+ varsExpr scrut
+ ) `thenLne` \ (scrut2, scrut_fvs, scrut_escs) ->
+ lookupLiveVarsForSet scrut_fvs `thenLne` \ scrut_lvs ->
+ let
+ live_in_whole_case = live_in_alts `unionUniqSets` scrut_lvs
+ in
+ returnLne (
+ StgCase scrut2 live_in_whole_case live_in_alts uniq alts2,
+ scrut_fvs `unionFVInfo` alts_fvs,
+ alts_escs `unionUniqSets` (getFVSet scrut_fvs) -- All free vars in the scrutinee escape
+ )
+ where
+ vars_alts (StgAlgAlts ty alts deflt)
+ = mapAndUnzip3Lne vars_alg_alt alts
+ `thenLne` \ (alts2, alts_fvs_list, alts_escs_list) ->
+ let
+ alts_fvs = unionFVInfos alts_fvs_list
+ alts_escs = unionManyUniqSets alts_escs_list
+ in
+ vars_deflt deflt `thenLne` \ (deflt2, deflt_fvs, deflt_escs) ->
+ returnLne (
+ StgAlgAlts ty alts2 deflt2,
+ alts_fvs `unionFVInfo` deflt_fvs,
+ alts_escs `unionUniqSets` deflt_escs
+ )
+ where
+ vars_alg_alt (con, binders, worthless_use_mask, rhs)
+ = extendVarEnv [(b, CaseBound) | b <- binders] (
+ varsExpr rhs `thenLne` \ (rhs2, rhs_fvs, rhs_escs) ->
+ let
+ good_use_mask = [ b `elementOfFVInfo` rhs_fvs | b <- binders ]
+ -- records whether each param is used in the RHS
+ in
+ returnLne (
+ (con, binders, good_use_mask, rhs2),
+ rhs_fvs `minusFVBinders` binders,
+ rhs_escs `minusUniqSet` mkUniqSet binders -- ToDo: remove the minusUniqSet;
+ -- since escs won't include
+ -- any of these binders
+ ))
+
+ vars_alts (StgPrimAlts ty alts deflt)
+ = mapAndUnzip3Lne vars_prim_alt alts
+ `thenLne` \ (alts2, alts_fvs_list, alts_escs_list) ->
+ let
+ alts_fvs = unionFVInfos alts_fvs_list
+ alts_escs = unionManyUniqSets alts_escs_list
+ in
+ vars_deflt deflt `thenLne` \ (deflt2, deflt_fvs, deflt_escs) ->
+ returnLne (
+ StgPrimAlts ty alts2 deflt2,
+ alts_fvs `unionFVInfo` deflt_fvs,
+ alts_escs `unionUniqSets` deflt_escs
+ )
+ where
+ vars_prim_alt (lit, rhs)
+ = varsExpr rhs `thenLne` \ (rhs2, rhs_fvs, rhs_escs) ->
+ returnLne ((lit, rhs2), rhs_fvs, rhs_escs)
+
+ vars_deflt StgNoDefault
+ = returnLne (StgNoDefault, emptyFVInfo, emptyUniqSet)
+
+ vars_deflt (StgBindDefault binder _ rhs)
+ = extendVarEnv [(binder, CaseBound)] (
+ varsExpr rhs `thenLne` \ (rhs2, rhs_fvs, rhs_escs) ->
+ let
+ used_in_rhs = binder `elementOfFVInfo` rhs_fvs
+ in
+ returnLne (
+ StgBindDefault binder used_in_rhs rhs2,
+ rhs_fvs `minusFVBinders` [binder],
+ rhs_escs `minusUniqSet` singletonUniqSet binder
+ ))
+\end{code}
+
+Lets not only take quite a bit of work, but this is where we convert
+then to let-no-escapes, if we wish.
+
+(Meanwhile, we don't expect to see let-no-escapes...)
+\begin{code}
+varsExpr (StgLetNoEscape _ _ _ _) = panic "varsExpr: unexpected StgLetNoEscape"
+
+varsExpr (StgLet bind body)
+ = isSwitchSetLne {-StgDoLetNoEscapes-} `thenLne` \ want_LNEs ->
+
+ (fixLne (\ ~(_, _, _, no_binder_escapes) ->
+ let
+ non_escaping_let = want_LNEs && no_binder_escapes
+ in
+ vars_let non_escaping_let bind body
+ )) `thenLne` \ (new_let, fvs, escs, _) ->
+
+ returnLne (new_let, fvs, escs)
+\end{code}
+
+\begin{code}
+#ifdef DPH
+-- rest of varsExpr goes here
+
+#endif {- Data Parallel Haskell -}
+\end{code}
+
+Applications:
+\begin{code}
+varsApp :: Maybe UpdateFlag -- Just upd <=> this application is
+ -- the rhs of a thunk binding
+ -- x = [...] \upd [] -> the_app
+ -- with specified update flag
+ -> Id -- Function
+ -> [PlainStgAtom] -- Arguments
+ -> LneM (PlainStgExpr, FreeVarsInfo, EscVarsSet)
+
+varsApp maybe_thunk_body f args
+ = getVarsLiveInCont `thenLne` \ live_in_cont ->
+
+ varsAtoms args `thenLne` \ args_fvs ->
+
+ lookupVarEnv f `thenLne` \ how_bound ->
+
+ let
+ n_args = length args
+
+ fun_fvs = singletonFVInfo f how_bound fun_occ
+
+ fun_occ =
+ case how_bound of
+ LetrecBound _ arity _
+ | n_args == 0 -> stgFakeFunAppOcc -- Function Application
+ -- with no arguments.
+ -- used by the lambda lifter.
+ | arity > n_args -> stgUnsatOcc -- Unsaturated
+
+
+ | arity == n_args &&
+ maybeToBool maybe_thunk_body -> -- Exactly saturated,
+ -- and rhs of thunk
+ case maybe_thunk_body of
+ Just Updatable -> stgStdHeapOcc
+ Just SingleEntry -> stgNoUpdHeapOcc
+ other -> panic "varsApp"
+
+ | otherwise -> stgNormalOcc
+ -- record only that it occurs free
+
+ other -> NoStgBinderInfo
+ -- uninteresting variable
+
+ myself = singletonUniqSet f
+
+ fun_escs = case how_bound of
+
+ LetrecBound _ arity lvs ->
+ if arity == n_args then
+ emptyUniqSet -- Function doesn't escape
+ else
+ myself -- Inexact application; it does escape
+
+ other -> emptyUniqSet -- Only letrec-bound escapees
+ -- are interesting
+
+ -- At the moment of the call:
+
+ -- either the function is *not* let-no-escaped, in which case
+ -- nothing is live except live_in_cont
+ -- or the function *is* let-no-escaped in which case the
+ -- variables it uses are live, but still the function
+ -- itself is not. PS. In this case, the function's
+ -- live vars should already include those of the
+ -- continuation, but it does no harm to just union the
+ -- two regardless.
+
+ live_at_call
+ = live_in_cont `unionUniqSets` case how_bound of
+ LetrecBound _ _ lvs -> lvs `minusUniqSet` myself
+ other -> emptyUniqSet
+ in
+ returnLne (
+ StgApp (StgVarAtom f) args live_at_call,
+ fun_fvs `unionFVInfo` args_fvs,
+ fun_escs `unionUniqSets` (getFVSet args_fvs)
+ -- All the free vars of the args are disqualified
+ -- from being let-no-escaped.
+ )
+\end{code}
+
+The magic for lets:
+\begin{code}
+vars_let :: Bool -- True <=> yes, we are let-no-escaping this let
+ -> PlainStgBinding -- bindings
+ -> PlainStgExpr -- body
+ -> LneM (PlainStgExpr, -- new let
+ FreeVarsInfo, -- variables free in the whole let
+ EscVarsSet, -- variables that escape from the whole let
+ Bool) -- True <=> none of the binders in the bindings
+ -- is among the escaping vars
+
+vars_let let_no_escape bind body
+ = fixLne (\ ~(_, _, _, rec_bind_lvs, _, rec_body_fvs, _, _) ->
+
+ -- Do the bindings, setting live_in_cont to empty if
+ -- we ain't in a let-no-escape world
+ getVarsLiveInCont `thenLne` \ live_in_cont ->
+ setVarsLiveInCont
+ (if let_no_escape then live_in_cont else emptyUniqSet)
+ (vars_bind rec_bind_lvs rec_body_fvs bind)
+ `thenLne` \ (bind2, bind_fvs, bind_escs, env_ext) ->
+
+ -- The live variables of this binding are the ones which are live
+ -- by virtue of being accessible via the free vars of the binding (lvs_from_fvs)
+ -- together with the live_in_cont ones
+ lookupLiveVarsForSet (bind_fvs `minusFVBinders` binders) `thenLne` \ lvs_from_fvs ->
+ let
+ bind_lvs = lvs_from_fvs `unionUniqSets` live_in_cont
+ in
+
+ -- bind_fvs and bind_escs still include the binders of the let(rec)
+ -- but bind_lvs does not
+
+ -- Do the body
+ extendVarEnv env_ext (
+ varsExpr body `thenLne` \ (body2, body_fvs, body_escs) ->
+ lookupLiveVarsForSet body_fvs `thenLne` \ body_lvs ->
+
+ returnLne (bind2, bind_fvs, bind_escs, bind_lvs,
+ body2, body_fvs, body_escs, body_lvs)
+
+ )) `thenLne` (\ (bind2, bind_fvs, bind_escs, bind_lvs,
+ body2, body_fvs, body_escs, body_lvs) ->
+
+
+ -- Compute the new let-expression
+ let
+ new_let = if let_no_escape then
+ -- trace "StgLetNoEscape!" (
+ StgLetNoEscape live_in_whole_let bind_lvs bind2 body2
+ -- )
+ else
+ StgLet bind2 body2
+
+ free_in_whole_let
+ = (bind_fvs `unionFVInfo` body_fvs) `minusFVBinders` binders
+
+ live_in_whole_let
+ = bind_lvs `unionUniqSets` (body_lvs `minusUniqSet` set_of_binders)
+
+ real_bind_escs = if let_no_escape then
+ bind_escs
+ else
+ getFVSet bind_fvs
+ -- Everything escapes which is free in the bindings
+
+ let_escs = (real_bind_escs `unionUniqSets` body_escs) `minusUniqSet` set_of_binders
+
+ all_escs = bind_escs `unionUniqSets` body_escs -- Still includes binders of
+ -- this let(rec)
+
+ no_binder_escapes = isEmptyUniqSet (set_of_binders `intersectUniqSets` all_escs)
+ -- Mustn't depend on the passed-in let_no_escape flag, since
+ -- no_binder_escapes is used by the caller to derive the flag!
+ in
+ returnLne (
+ new_let,
+ free_in_whole_let,
+ let_escs,
+ no_binder_escapes
+ ))
+ where
+ binders = case bind of
+ StgNonRec binder rhs -> [binder]
+ StgRec pairs -> map fst pairs
+ set_of_binders = mkUniqSet binders
+
+ mk_binding bind_lvs (binder,rhs)
+ = (binder,
+ LetrecBound False -- Not top level
+ (stgArity rhs)
+ live_vars
+ )
+ where
+ live_vars = if let_no_escape then
+ bind_lvs `unionUniqSets` singletonUniqSet binder
+ else
+ singletonUniqSet binder
+
+ vars_bind :: PlainStgLiveVars
+ -> FreeVarsInfo -- Free var info for body of binding
+ -> PlainStgBinding
+ -> LneM (PlainStgBinding,
+ FreeVarsInfo, EscVarsSet, -- free vars; escapee vars
+ [(Id, HowBound)])
+ -- extension to environment
+
+ vars_bind rec_bind_lvs rec_body_fvs (StgNonRec binder rhs)
+ = varsRhs rec_body_fvs (binder,rhs) `thenLne` \ (rhs2, fvs, escs) ->
+ let
+ env_ext = [mk_binding rec_bind_lvs (binder,rhs)]
+ in
+ returnLne (StgNonRec binder rhs2, fvs, escs, env_ext)
+
+ vars_bind rec_bind_lvs rec_body_fvs (StgRec pairs)
+ = let
+ (binders, rhss) = unzip pairs
+ env_ext = map (mk_binding rec_bind_lvs) pairs
+ in
+ extendVarEnv env_ext (
+ fixLne (\ ~(_, rec_rhs_fvs, _, _) ->
+ let
+ rec_scope_fvs = unionFVInfo rec_body_fvs rec_rhs_fvs
+ in
+ mapAndUnzip3Lne (varsRhs rec_scope_fvs) pairs `thenLne` \ (rhss2, fvss, escss) ->
+ let
+ fvs = unionFVInfos fvss
+ escs = unionManyUniqSets escss
+ in
+ returnLne (StgRec (binders `zip` rhss2), fvs, escs, env_ext)
+ ))
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[LNE-monad]{A little monad for this let-no-escaping pass}
+%* *
+%************************************************************************
+
+There's a lot of stuff to pass around, so we use this @LneM@ monad to
+help. All the stuff here is only passed {\em down}.
+
+\begin{code}
+type LneM a = Bool -- True <=> do let-no-escapes
+ -> IdEnv HowBound
+ -> PlainStgLiveVars -- vars live in continuation
+ -> a
+
+type Arity = Int
+
+data HowBound
+ = ImportBound
+ | CaseBound
+ | LambdaBound
+ | LetrecBound
+ Bool -- True <=> bound at top level
+ Arity -- Arity
+ PlainStgLiveVars -- Live vars... see notes below
+\end{code}
+
+For a let(rec)-bound variable, x, we record what varibles are live if
+x is live. For "normal" variables that is just x alone. If x is
+a let-no-escaped variable then x is represented by a code pointer and
+a stack pointer (well, one for each stack). So all of the variables
+needed in the execution of x are live if x is, and are therefore recorded
+in the LetrecBound constructor; x itself *is* included.
+
+The std monad functions:
+\begin{code}
+initLne :: Bool -> LneM a -> a
+initLne want_LNEs m = m want_LNEs nullIdEnv emptyUniqSet
+
+#ifdef __GLASGOW_HASKELL__
+{-# INLINE thenLne #-}
+{-# INLINE thenLne_ #-}
+{-# INLINE returnLne #-}
+#endif
+
+returnLne :: a -> LneM a
+returnLne e sw env lvs_cont = e
+
+thenLne :: LneM a -> (a -> LneM b) -> LneM b
+(m `thenLne` k) sw env lvs_cont
+ = case (m sw env lvs_cont) of
+ m_result -> k m_result sw env lvs_cont
+
+thenLne_ :: LneM a -> LneM b -> LneM b
+(m `thenLne_` k) sw env lvs_cont
+ = case (m sw env lvs_cont) of
+ _ -> k sw env lvs_cont
+
+mapLne :: (a -> LneM b) -> [a] -> LneM [b]
+mapLne f [] = returnLne []
+mapLne f (x:xs)
+ = f x `thenLne` \ r ->
+ mapLne f xs `thenLne` \ rs ->
+ returnLne (r:rs)
+
+mapAndUnzipLne :: (a -> LneM (b,c)) -> [a] -> LneM ([b],[c])
+
+mapAndUnzipLne f [] = returnLne ([],[])
+mapAndUnzipLne f (x:xs)
+ = f x `thenLne` \ (r1, r2) ->
+ mapAndUnzipLne f xs `thenLne` \ (rs1, rs2) ->
+ returnLne (r1:rs1, r2:rs2)
+
+mapAndUnzip3Lne :: (a -> LneM (b,c,d)) -> [a] -> LneM ([b],[c],[d])
+
+mapAndUnzip3Lne f [] = returnLne ([],[],[])
+mapAndUnzip3Lne f (x:xs)
+ = f x `thenLne` \ (r1, r2, r3) ->
+ mapAndUnzip3Lne f xs `thenLne` \ (rs1, rs2, rs3) ->
+ returnLne (r1:rs1, r2:rs2, r3:rs3)
+
+fixLne :: (a -> LneM a) -> LneM a
+fixLne expr sw env lvs_cont = result
+ where
+ result = expr result sw env lvs_cont
+-- ^^^^^^ ------ ^^^^^^
+\end{code}
+
+Functions specific to this monad:
+\begin{code}
+{- NOT USED:
+ifSwitchSetLne :: GlobalSwitch -> LneM a -> LneM a -> LneM a
+ifSwitchSetLne switch then_ else_ switch_checker env lvs_cont
+ = (if switch_checker switch then then_ else else_) switch_checker env lvs_cont
+-}
+
+isSwitchSetLne :: LneM Bool
+isSwitchSetLne want_LNEs env lvs_cont
+ = want_LNEs
+
+getVarsLiveInCont :: LneM PlainStgLiveVars
+getVarsLiveInCont sw env lvs_cont = lvs_cont
+
+setVarsLiveInCont :: PlainStgLiveVars -> LneM a -> LneM a
+setVarsLiveInCont new_lvs_cont expr sw env lvs_cont
+ = expr sw env new_lvs_cont
+
+extendVarEnv :: [(Id, HowBound)] -> LneM a -> LneM a
+extendVarEnv extension expr sw env lvs_cont
+ = expr sw (growIdEnvList env extension) lvs_cont
+
+lookupVarEnv :: Id -> LneM HowBound
+lookupVarEnv v sw env lvs_cont
+ = returnLne (
+ case (lookupIdEnv env v) of
+ Just xx -> xx
+ Nothing -> --false:ASSERT(not (isLocallyDefined v))
+ ImportBound
+ ) sw env lvs_cont
+
+-- The result of lookupLiveVarsForSet, a set of live variables, is
+-- only ever tacked onto a decorated expression. It is never used as
+-- the basis of a control decision, which might give a black hole.
+
+lookupLiveVarsForSet :: FreeVarsInfo -> LneM PlainStgLiveVars
+
+lookupLiveVarsForSet fvs sw env lvs_cont
+ = returnLne (unionManyUniqSets (map do_one (getFVs fvs)))
+ sw env lvs_cont
+ where
+ do_one v
+ = if isLocallyDefined v then
+ case (lookupIdEnv env v) of
+ Just (LetrecBound _ _ lvs) -> lvs `unionUniqSets` singletonUniqSet v
+ Just _ -> singletonUniqSet v
+ Nothing -> pprPanic "lookupVarEnv/do_one:" (ppr PprShowAll v)
+ else
+ emptyUniqSet
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection[Free-var info]{Free variable information}
+%* *
+%************************************************************************
+
+\begin{code}
+type FreeVarsInfo = IdEnv (Id, Bool, StgBinderInfo)
+ -- If f is mapped to NoStgBinderInfo, that means
+ -- that f *is* mentioned (else it wouldn't be in the
+ -- IdEnv at all), but only in a saturated applications.
+ --
+ -- All case/lambda-bound things are also mapped to
+ -- NoStgBinderInfo, since we aren't interested in their
+ -- occurence info.
+ --
+ -- The Bool is True <=> the Id is top level letrec bound
+
+type EscVarsSet = UniqSet Id
+\end{code}
+
+\begin{code}
+emptyFVInfo :: FreeVarsInfo
+emptyFVInfo = nullIdEnv
+
+singletonFVInfo :: Id -> HowBound -> StgBinderInfo -> FreeVarsInfo
+singletonFVInfo id ImportBound info = nullIdEnv
+singletonFVInfo id (LetrecBound top_level _ _) info = unitIdEnv id (id, top_level, info)
+singletonFVInfo id other info = unitIdEnv id (id, False, info)
+
+unionFVInfo :: FreeVarsInfo -> FreeVarsInfo -> FreeVarsInfo
+unionFVInfo fv1 fv2 = combineIdEnvs plusFVInfo fv1 fv2
+
+unionFVInfos :: [FreeVarsInfo] -> FreeVarsInfo
+unionFVInfos fvs = foldr unionFVInfo emptyFVInfo fvs
+
+minusFVBinders :: FreeVarsInfo -> [Id] -> FreeVarsInfo
+minusFVBinders fv ids = fv `delManyFromIdEnv` ids
+
+elementOfFVInfo :: Id -> FreeVarsInfo -> Bool
+elementOfFVInfo id fvs = maybeToBool (lookupIdEnv fvs id)
+
+lookupFVInfo :: FreeVarsInfo -> Id -> StgBinderInfo
+lookupFVInfo fvs id = case lookupIdEnv fvs id of
+ Nothing -> NoStgBinderInfo
+ Just (_,_,info) -> info
+
+getFVs :: FreeVarsInfo -> [Id] -- Non-top-level things only
+getFVs fvs = [id | (id,False,_) <- rngIdEnv fvs]
+
+getFVSet :: FreeVarsInfo -> UniqSet Id
+getFVSet fvs = mkUniqSet (getFVs fvs)
+
+plusFVInfo (id1,top1,info1) (id2,top2,info2)
+ = ASSERT (id1 == id2 && top1 == top2)
+ (id1, top1, combineStgBinderInfo info1 info2)
+\end{code}
+
+\begin{code}
+rhsArity :: PlainStgRhs -> Arity
+rhsArity (StgRhsCon _ _ _) = 0
+rhsArity (StgRhsClosure _ _ _ _ args _) = length args
+\end{code}
+
+
+
diff --git a/ghc/compiler/simplStg/UpdAnal.hi b/ghc/compiler/simplStg/UpdAnal.hi
new file mode 100644
index 0000000000..c45043ea8e
--- /dev/null
+++ b/ghc/compiler/simplStg/UpdAnal.hi
@@ -0,0 +1,7 @@
+{-# GHC_PRAGMA INTERFACE VERSION 5 #-}
+interface UpdAnal where
+import Id(Id)
+import StgSyn(StgBinding)
+updateAnalyse :: [StgBinding Id Id] -> [StgBinding Id Id]
+ {-# GHC_PRAGMA _A_ 1 _U_ 1 _N_ _S_ "S" _N_ _N_ #-}
+
diff --git a/ghc/compiler/simplStg/UpdAnal.lhs b/ghc/compiler/simplStg/UpdAnal.lhs
new file mode 100644
index 0000000000..a50e672f65
--- /dev/null
+++ b/ghc/compiler/simplStg/UpdAnal.lhs
@@ -0,0 +1,510 @@
+\section{Update Avoidance Analyser} -*-haskell-literate-*-
+
+(c) Simon Marlow, Andre Santos 1992-1993
+(c) The AQUA Project, Glasgow University, 1995
+
+%-----------------------------------------------------------------------------
+\subsection{Module Interface}
+
+\begin{code}
+#include "HsVersions.h"
+\end{code}
+
+> module UpdAnal ( updateAnalyse ) where
+>
+> IMPORT_Trace
+
+> import AbsUniType ( splitTyArgs, splitType, Class, TyVarTemplate,
+> TauType(..)
+> )
+> import Id
+> import IdEnv
+> import IdInfo
+> import Outputable ( isExported )
+> import Pretty
+> import SrcLoc ( mkUnknownSrcLoc )
+> import StgSyn
+> import UniqSet
+> import Unique ( getBuiltinUniques )
+> import Util
+
+%-----------------------------------------------------------------------------
+\subsection{Reverse application}
+
+This is used instead of lazy pattern bindings to avoid space leaks.
+
+> infixr 3 =:
+> a =: k = k a
+
+%-----------------------------------------------------------------------------
+\subsection{Types}
+
+List of closure references
+
+> type Refs = IdSet
+> x `notInRefs` y = not (x `elementOfUniqSet` y)
+
+A closure value: environment of closures that are evaluated on entry,
+a list of closures that are referenced from the result, and an
+abstract value for the evaluated closure.
+
+An IdEnv is used for the reference counts, as these environments are
+combined often. A generic environment is used for the main environment
+mapping closure names to values; as a common operation is extension of
+this environment, this representation should be efficient.
+
+> -- partain: funny synonyms to cope w/ the fact
+> -- that IdEnvs know longer know what their keys are
+> -- (94/05) ToDo: improve
+> type IdEnvInt = IdEnv (Id, Int)
+> type IdEnvClosure = IdEnv (Id, Closure)
+
+> -- backward-compat functions
+> null_IdEnv :: IdEnv (Id, a)
+> null_IdEnv = nullIdEnv
+>
+> unit_IdEnv :: Id -> a -> IdEnv (Id, a)
+> unit_IdEnv k v = unitIdEnv k (k, v)
+>
+> mk_IdEnv :: [(Id, a)] -> IdEnv (Id, a)
+> mk_IdEnv pairs = mkIdEnv [ (k, (k,v)) | (k,v) <- pairs ]
+>
+> grow_IdEnv :: IdEnv (Id, a) -> IdEnv (Id, a) -> IdEnv (Id, a)
+> grow_IdEnv env1 env2 = growIdEnv env1 env2
+>
+> addOneTo_IdEnv :: IdEnv (Id, a) -> Id -> a -> IdEnv (Id, a)
+> addOneTo_IdEnv env k v = addOneToIdEnv env k (k, v)
+>
+> combine_IdEnvs :: (a->a->a) -> IdEnv (Id, a) -> IdEnv (Id, a) -> IdEnv (Id, a)
+> combine_IdEnvs combiner env1 env2 = combineIdEnvs new_combiner env1 env2
+> where
+> new_combiner (id, x) (_, y) = (id, combiner x y)
+>
+> dom_IdEnv :: IdEnv (Id, a) -> Refs
+> dom_IdEnv env = mkUniqSet [ i | (i,_) <- rngIdEnv env ]
+>
+> lookup_IdEnv :: IdEnv (Id, a) -> Id -> Maybe a
+> lookup_IdEnv env key = case lookupIdEnv env key of
+> Nothing -> Nothing
+> Just (_,a) -> Just a
+> -- end backward compat stuff
+
+> type Closure = (IdEnvInt, Refs, AbFun)
+
+> type AbVal = IdEnvClosure -> Closure
+> data AbFun = Fun (Closure -> Closure)
+
+> -- partain: speeding-up stuff
+>
+> type CaseBoundVars = IdSet
+> noCaseBound = emptyUniqSet
+> isCaseBound = elementOfUniqSet
+> x `notCaseBound` y = not (isCaseBound x y)
+> moreCaseBound :: CaseBoundVars -> [Id] -> CaseBoundVars
+> moreCaseBound old new = old `unionUniqSets` mkUniqSet new
+>
+> -- end speeding-up
+
+%----------------------------------------------------------------------------
+\subsection{Environment lookup}
+
+If the requested value is not in the environment, we return an unknown
+value. Lookup is designed to be partially applied to a variable, and
+repeatedly applied to different environments after that.
+
+> lookup v
+> | isImportedId v
+> = const (case updateInfoMaybe (getIdUpdateInfo v) of
+> Nothing -> unknownClosure
+> Just spec -> convertUpdateSpec spec)
+> | otherwise
+> = \p -> case lookup_IdEnv p v of
+> Just b -> b
+> Nothing -> unknownClosure
+
+%-----------------------------------------------------------------------------
+Represent a list of references as an ordered list.
+
+> mkRefs :: [Id] -> Refs
+> mkRefs = mkUniqSet
+
+> noRefs :: Refs
+> noRefs = emptyUniqSet
+
+> elemRefs = elementOfUniqSet
+
+> merge :: [Refs] -> Refs
+> merge xs = foldr merge2 emptyUniqSet xs
+
+> merge2 :: Refs -> Refs -> Refs
+> merge2 = unionUniqSets
+
+%-----------------------------------------------------------------------------
+\subsection{Some non-interesting values}
+
+bottom will be used for abstract values that are not functions.
+Hopefully its value will never be required!
+
+> bottom :: AbFun
+> bottom = panic "Internal: (Update Analyser) bottom"
+
+noClosure is a value that is definitely not a function (i.e. primitive
+values and constructor applications). unknownClosure is a value about
+which we have no information at all. This should occur rarely, but
+could happen when an id is imported and the exporting module was not
+compiled with the update analyser.
+
+> noClosure, unknownClosure :: Closure
+> noClosure = (null_IdEnv, noRefs, bottom)
+> unknownClosure = (null_IdEnv, noRefs, dont_know noRefs)
+
+dont_know is a black hole: it is something we know nothing about.
+Applying dont_know to anything will generate a new dont_know that simply
+contains more buried references.
+
+> dont_know :: Refs -> AbFun
+> dont_know b'
+> = Fun (\(c,b,f) -> let b'' = dom_IdEnv c `merge2` b `merge2` b'
+> in (null_IdEnv, b'', dont_know b''))
+
+%-----------------------------------------------------------------------------
+
+> getrefs :: IdEnvClosure -> [AbVal] -> Refs -> Refs
+> getrefs p vs rest = foldr merge2 rest (getrefs' (map ($ p) vs))
+> where
+> getrefs' [] = []
+> getrefs' ((c,b,_):rs) = dom_IdEnv c : b : getrefs' rs
+
+%-----------------------------------------------------------------------------
+
+udData is used when we are putting a list of closure references into a
+data structure, or something else that we know nothing about.
+
+> udData :: [PlainStgAtom] -> CaseBoundVars -> AbVal
+> udData vs cvs
+> = \p -> (null_IdEnv, getrefs p local_ids noRefs, bottom)
+> where local_ids = [ lookup v | (StgVarAtom v) <- vs, v `notCaseBound` cvs ]
+
+%-----------------------------------------------------------------------------
+\subsection{Analysing an atom}
+
+> udAtom :: CaseBoundVars -> PlainStgAtom -> AbVal
+> udAtom cvs (StgVarAtom v)
+> | v `isCaseBound` cvs = const unknownClosure
+> | otherwise = lookup v
+>
+> udAtom cvs _ = const noClosure
+
+%-----------------------------------------------------------------------------
+\subsection{Analysing an STG expression}
+
+> ud :: PlainStgExpr -- Expression to be analysed
+> -> CaseBoundVars -- List of case-bound vars
+> -> IdEnvClosure -- Current environment
+> -> (PlainStgExpr, AbVal) -- (New expression, abstract value)
+>
+> ud e@(StgPrimApp _ vs _) cvs p = (e, udData vs cvs)
+> ud e@(StgConApp _ vs _) cvs p = (e, udData vs cvs)
+> ud e@(StgSCC ty lab a) cvs p = ud a cvs p =: \(a', abval_a) ->
+> (StgSCC ty lab a', abval_a)
+
+Here is application. The first thing to do is analyse the head, and
+get an abstract function. Multiple applications are performed by using
+a foldl with the function doApp. Closures are actually passed to the
+abstract function iff the atom is a local variable.
+
+I've left the type signature for doApp in to make things a bit clearer.
+
+> ud e@(StgApp a atoms lvs) cvs p
+> = (e, abval_app)
+> where
+> abval_atoms = map (udAtom cvs) atoms
+> abval_a = udAtom cvs a
+> abval_app = \p ->
+> let doApp :: Closure -> AbVal -> Closure
+> doApp (c, b, Fun f) abval_atom =
+> abval_atom p =: \e@(_,_,_) ->
+> f e =: \(c', b', f') ->
+> (combine_IdEnvs (+) c' c, b', f')
+> in foldl doApp (abval_a p) abval_atoms
+
+> ud (StgCase expr lve lva uniq alts) cvs p
+> = ud expr cvs p =: \(expr', abval_selector) ->
+> udAlt alts p =: \(alts', abval_alts) ->
+> let
+> abval_case = \p ->
+> abval_selector p =: \(c, b, abfun_selector) ->
+> abval_alts p =: \(cs, bs, abfun_alts) ->
+> let bs' = b `merge2` bs in
+> (combine_IdEnvs (+) c cs, bs', dont_know bs')
+> in
+> (StgCase expr' lve lva uniq alts', abval_case)
+> where
+>
+> udAlt :: PlainStgCaseAlternatives
+> -> IdEnvClosure
+> -> (PlainStgCaseAlternatives, AbVal)
+>
+> udAlt (StgAlgAlts ty [alt] StgNoDefault) p
+> = udAlgAlt p alt =: \(alt', abval) ->
+> (StgAlgAlts ty [alt'] StgNoDefault, abval)
+> udAlt (StgAlgAlts ty [] def) p
+> = udDef def p =: \(def', abval) ->
+> (StgAlgAlts ty [] def', abval)
+> udAlt (StgAlgAlts ty alts def) p
+> = udManyAlts alts def udAlgAlt (StgAlgAlts ty) p
+> udAlt (StgPrimAlts ty [alt] StgNoDefault) p
+> = udPrimAlt p alt =: \(alt', abval) ->
+> (StgPrimAlts ty [alt'] StgNoDefault, abval)
+> udAlt (StgPrimAlts ty [] def) p
+> = udDef def p =: \(def', abval) ->
+> (StgPrimAlts ty [] def', abval)
+> udAlt (StgPrimAlts ty alts def) p
+> = udManyAlts alts def udPrimAlt (StgPrimAlts ty) p
+>
+> udPrimAlt p (l, e)
+> = ud e cvs p =: \(e', v) -> ((l, e'), v)
+>
+> udAlgAlt p (id, vs, use_mask, e)
+> = ud e (moreCaseBound cvs vs) p =: \(e', v) -> ((id, vs, use_mask, e'), v)
+>
+> udDef :: PlainStgCaseDefault
+> -> IdEnvClosure
+> -> (PlainStgCaseDefault, AbVal)
+>
+> udDef StgNoDefault p
+> = (StgNoDefault, \p -> (null_IdEnv, noRefs, dont_know noRefs))
+> udDef (StgBindDefault v is_used expr) p
+> = ud expr (moreCaseBound cvs [v]) p =: \(expr', abval) ->
+> (StgBindDefault v is_used expr', abval)
+>
+> udManyAlts alts def udalt stgalts p
+> = udDef def p =: \(def', abval_def) ->
+> unzip (map (udalt p) alts) =: \(alts', abvals_alts) ->
+> let
+> abval_alts = \p ->
+> abval_def p =: \(cd, bd, _) ->
+> unzip3 (map ($ p) abvals_alts) =: \(cs, bs, _) ->
+> let bs' = merge (bd:bs) in
+> (foldr (combine_IdEnvs max) cd cs, bs', dont_know bs')
+> in (stgalts alts' def', abval_alts)
+
+The heart of the analysis: here we decide whether to make a specific
+closure updatable or not, based on the results of analysing the body.
+
+> ud (StgLet binds body) cvs p
+> = udBinding binds cvs p =: \(binds', vs, abval1, abval2) ->
+> abval1 p =: \(cs, p') ->
+> grow_IdEnv p p' =: \p ->
+> ud body cvs p =: \(body', abval_body) ->
+> abval_body p =: \(c, b, abfun) ->
+> tag b (combine_IdEnvs (+) cs c) binds' =: \tagged_binds ->
+> let
+> abval p
+> = abval2 p =: \(c1, p') ->
+> abval_body (grow_IdEnv p p') =: \(c2, b, abfun) ->
+> (combine_IdEnvs (+) c1 c2, b, abfun)
+> in
+> (StgLet tagged_binds body', abval)
+
+%-----------------------------------------------------------------------------
+\subsection{Analysing bindings}
+
+For recursive sets of bindings we perform one iteration of a fixed
+point algorithm, using (dont_know fv) as a safe approximation to the
+real fixed point, where fv are the (mappings in the environment of
+the) free variables of the function.
+
+We'll return two new environments, one with the new closures in and
+one without. There's no point in carrying around closures when their
+respective bindings have already been analysed.
+
+We don't need to find anything out about closures with arguments,
+constructor closures etc.
+
+> udBinding :: PlainStgBinding
+> -> CaseBoundVars
+> -> IdEnvClosure
+> -> (PlainStgBinding,
+> [Id],
+> IdEnvClosure -> (IdEnvInt, IdEnvClosure),
+> IdEnvClosure -> (IdEnvInt, IdEnvClosure))
+>
+> udBinding (StgNonRec v rhs) cvs p
+> = udRhs rhs cvs p =: \(rhs', abval) ->
+> abval p =: \(c, b, abfun) ->
+> let
+> abval_rhs a = \p ->
+> abval p =: \(c, b, abfun) ->
+> (c, unit_IdEnv v (a, b, abfun))
+> a = case rhs of
+> StgRhsClosure _ _ _ Updatable [] _ -> unit_IdEnv v 1
+> _ -> null_IdEnv
+> in (StgNonRec v rhs', [v], abval_rhs a, abval_rhs null_IdEnv)
+>
+> udBinding (StgRec ve) cvs p
+> = (StgRec ve', [], abval_rhs, abval_rhs)
+> where
+> (vs, ve', abvals) = unzip3 (map udBind ve)
+> fv = (map lookup . filter (`notCaseBound` cvs) . concat . map collectfv) ve
+> vs' = mkRefs vs
+> abval_rhs = \p ->
+> let
+> p' = grow_IdEnv (mk_IdEnv (vs `zip` (repeat closure))) p
+> closure = (null_IdEnv, fv', dont_know fv')
+> fv' = getrefs p fv vs'
+> (cs, ps) = unzip (doRec vs abvals)
+>
+> doRec [] _ = []
+> doRec (v:vs) (abval:as)
+> = abval p' =: \(c,b,abfun) ->
+> (c, (v,(null_IdEnv, b, abfun))) : doRec vs as
+>
+> in
+> (foldr (combine_IdEnvs (+)) null_IdEnv cs, mk_IdEnv ps)
+>
+> udBind (v,rhs)
+> = udRhs rhs cvs p =: \(rhs', abval) ->
+> (v,(v,rhs'), abval)
+>
+> collectfv (_, StgRhsClosure _ _ fv _ _ _) = fv
+> collectfv (_, StgRhsCon _ con args) = [ v | (StgVarAtom v) <- args ]
+
+%-----------------------------------------------------------------------------
+\subsection{Analysing Right-Hand Sides}
+
+> udRhs e@(StgRhsCon _ _ vs) cvs p = (e, udData vs cvs)
+>
+> udRhs (StgRhsClosure cc bi fv u [] body) cvs p
+> = ud body cvs p =: \(body', abval_body) ->
+> (StgRhsClosure cc bi fv u [] body', abval_body)
+
+Here is the code for closures with arguments. A closure has a number
+of arguments, which correspond to a set of nested lambda expressions.
+We build up the analysis using foldr with the function doLam to
+analyse each lambda expression.
+
+> udRhs (StgRhsClosure cc bi fv u args body) cvs p
+> = ud body cvs p =: \(body', abval_body) ->
+> let
+> fv' = map lookup (filter (`notCaseBound` cvs) fv)
+> abval_rhs = \p ->
+> foldr doLam (\b -> abval_body) args (getrefs p fv' noRefs) p
+> in
+> (StgRhsClosure cc bi fv u args body', abval_rhs)
+> where
+>
+> doLam :: Id -> (Refs -> AbVal) -> Refs -> AbVal
+> doLam i f b p
+> = (null_IdEnv, b,
+> Fun (\x@(c',b',_) ->
+> let b'' = dom_IdEnv c' `merge2` b' `merge2` b in
+> f b'' (addOneTo_IdEnv p i x)))
+
+%-----------------------------------------------------------------------------
+\subsection{Adjusting Update flags}
+
+The closure is tagged single entry iff it is used at most once, it is
+not referenced from inside a data structure or function, and it has no
+arguments (closures with arguments are re-entrant).
+
+> tag :: Refs -> IdEnvInt -> PlainStgBinding -> PlainStgBinding
+>
+> tag b c r@(StgNonRec v (StgRhsClosure cc bi fv Updatable [] body))
+> = if (v `notInRefs` b) && (lookupc c v <= 1)
+> then -- trace "One!" (
+> StgNonRec v (StgRhsClosure cc bi fv SingleEntry [] body)
+> -- )
+> else r
+> tag b c other = other
+>
+> lookupc c v = case lookup_IdEnv c v of
+> Just n -> n
+> Nothing -> 0
+
+%-----------------------------------------------------------------------------
+\subsection{Top Level analysis}
+
+Should we tag top level closures? This could have good implications
+for CAFs (i.e. they could be made non-updateable if only used once,
+thus preventing a space leak).
+
+> updateAnalyse :: PlainStgProgram -> PlainStgProgram {- Exported -}
+> updateAnalyse bs
+> = udProgram bs null_IdEnv
+
+> udProgram :: PlainStgProgram -> IdEnvClosure -> PlainStgProgram
+> udProgram [] p = []
+> udProgram (d:ds) p
+> = udBinding d noCaseBound p =: \(d', vs, _, abval_bind) ->
+> abval_bind p =: \(_, p') ->
+> grow_IdEnv p p' =: \p'' ->
+> attachUpdateInfoToBinds d' p'' =: \d'' ->
+> d'' : udProgram ds p''
+
+%-----------------------------------------------------------------------------
+\subsection{Exporting Update Information}
+
+Convert the exported representation of a function's update function
+into a real Closure value.
+
+> convertUpdateSpec :: UpdateSpec -> Closure
+> convertUpdateSpec = mkClosure null_IdEnv noRefs noRefs
+
+> mkClosure :: IdEnvInt -> Refs -> Refs -> UpdateSpec -> Closure
+>
+> mkClosure c b b' [] = (c, b', dont_know b')
+> mkClosure c b b' (0 : ns) = (null_IdEnv, b, Fun (\ _ -> mkClosure c b b' ns))
+> mkClosure c b b' (1 : ns) = (null_IdEnv, b, Fun (\ (c',b'',f) ->
+> mkClosure
+> (combine_IdEnvs (+) c c')
+> (dom_IdEnv c' `merge2` b'' `merge2` b)
+> (b'' `merge2` b')
+> ns ))
+> mkClosure c b b' (n : ns) = (null_IdEnv, b, Fun (\ (c',b'',f) ->
+> mkClosure c
+> (dom_IdEnv c' `merge2` b'' `merge2` b)
+> (dom_IdEnv c' `merge2` b'' `merge2` b')
+> ns ))
+
+Convert a Closure into a representation that can be placed in a .hi file.
+
+> mkUpdateSpec :: Id -> Closure -> UpdateSpec
+> mkUpdateSpec v f = {- removeSuperfluous2s -} (map countUses ids)
+> where
+> (c,b,_) = foldl doApp f ids
+> ids = map mkid (getBuiltinUniques arity)
+> mkid u = mkSysLocal SLIT("upd") u noType mkUnknownSrcLoc
+> countUses u = if u `elemRefs` b then 2 else min (lookupc c u) 2
+> noType = panic "UpdAnal: no type!"
+>
+> doApp (c,b,Fun f) i
+> = f (unit_IdEnv i 1, noRefs, dont_know noRefs) =: \(c',b',f') ->
+> (combine_IdEnvs (+) c' c, b', f')
+>
+> (_,dict_tys,tau_ty) = (splitType . getIdUniType) v
+> (reg_arg_tys, _) = splitTyArgs tau_ty
+> arity = length dict_tys + length reg_arg_tys
+
+ removeSuperfluous2s = reverse . dropWhile (> 1) . reverse
+
+%-----------------------------------------------------------------------------
+\subsection{Attaching the update information to top-level bindings}
+
+This is so that the information can later be retrieved for printing
+out in the .hi file. This is not an ideal solution, however it will
+suffice for now.
+
+> attachUpdateInfoToBinds b p
+> = case b of
+> StgNonRec v rhs -> StgNonRec (attachOne v) rhs
+> StgRec bs -> StgRec [ (attachOne v, rhs) | (v, rhs) <- bs ]
+>
+> where attachOne v
+> | isExported v
+> = let c = lookup v p in
+> addIdUpdateInfo v
+> (mkUpdateInfo (mkUpdateSpec v c))
+> | otherwise = v
+
+%-----------------------------------------------------------------------------