packages feed

lhc-0.6.20081216: src/Grin/Simplify.hs

module Grin.Simplify(simplify,renameUniqueGrin) where

import Control.Monad.Writer
import Control.Monad.State
import Control.Monad.Trans
import Control.Monad.RWS
import Data.Monoid
import List hiding (insert,union)
import Maybe
import qualified Data.Map as Map
import qualified Data.Set as Set

import StringTable.Atom
import GenUtil hiding(putErrLn,replicateM_)
import Grin.Grin
import Grin.Noodle
import Grin.Whiz
import Stats hiding(combine)
import Support.CanType
import Support.FreeVars
import Util.Graph
import Util.HasSize
import Util.Inst()
import Util.UniqueMonad
import Util.SetLike
import qualified Util.Seq as Seq
import qualified Util.Histogram as Hist
import Options (verbose)

-- perform a number of simple simplifications.
-- inline very small and builtin-wrapper functions
-- copy propagation
-- CSE / constant propagation
-- dispose of code unreachable via Error


at_OptSimplifyInline  = toAtom "Optimize.simplify.inline"
at_OptSimplifyCopyProp  = toAtom "Optimize.simplify.copy-propagate"
at_OptSimplifyCopyPropConst  = toAtom "Optimize.simplify.copy-propagate-const"
at_OptSimplifyNodeReduction  = toAtom "Optimize.simplify.node-reduction"
at_OptSimplifyDeadVar  = toAtom "Optimize.simplify.dead-var"
at_OptSimplifyConstApply  = toAtom "Optimize.simplify.const-apply"
at_OptSimplifyConstFetch  = toAtom "Optimize.simplify.const-fetch"
at_OptSimplifyConstEval  = toAtom "Optimize.simplify.const-eval"
at_OptSimplifyTrivialCase  = toAtom "Optimize.simplify.trivial-case"
at_OptSimplifyBadAssignment  = toAtom "Optimize.simplify.bad-assignment"
at_OptSimplifyHoleAssignment  = toAtom "Optimize.simplify.hole-assignment"
at_OptSimplifyConstStore  = toAtom "Optimize.simplify.const-store"
at_OptSimplifyCastLit  = toAtom "Optimize.simplify.cast-lit"
at_OptSimplifyConstUpdate  = toAtom "Optimize.simplify.const-update"
at_OptSimplifyEnumAssignment  = toAtom "Optimize.simplify.enum-assignment"

-- contains functions that should be inlined
type SimpEnv = Map.Map Atom (Atom,Lam)


simplify1 :: Stats -> SimpEnv -> (Atom,Lam) -> IO (Atom,Lam)
simplify1 stats env (n,l) = do
    (l,_) <- evalStateT (whiz fn gv f whizState l) mempty
    return (n,l)
    where
    fn _ m = do
        s <- get
        x <- m
        put s
        return x

    f (Case x [d]) = do
        (env,_) <- get
        x <- applySubst env  x
        lift $ tick stats at_OptSimplifyTrivialCase
        return $ (Return [x] :>>= d)
    f x = do
        (env,_) <- get
        x <- applySubstE env  x
        x <- gs x
        inline x
    gs (Update Const {} Var {}) = do
        lift $ tick stats at_OptSimplifyConstUpdate
        gs (Return [])
--    gs (Prim Primitive { primAPrim = APrim CCast {} _, primType = (_,nty) } [Lit i _]) = do
--        lift $ tick stats at_OptSimplifyCastLit
--        return $ Return (Lit i nty)
    gs (Store n) | valIsNF n = do
        lift $ tick stats at_OptSimplifyConstStore
        gs (Return [Const n])
--    gs (App a [n@NodeC {},v] typ) | a == funcApply = do
--        lift $ tick stats at_OptSimplifyConstApply
--        gs (doApply Return True n [v] typ)
--    gs (Store (NodeC t [Const x@NodeC {},y])) | Just 1 <- fromBap t = do --  App a [n@NodeC {},v] typ) | a == funcApply = do
--        lift $ tick stats "Optimize.simplify.const-lazy-apply"
--        gs (doApply Store False x y TyNode)
    gs (App a [Const n] typ) | a == funcEval = do
        lift $ tick stats at_OptSimplifyConstEval
        gs (Return [n])
    gs (Fetch (Const n)) = do
        lift $ tick stats at_OptSimplifyConstFetch
        gs (Return [n])
    gs x = return x
    gv (p,Case x ds) = do
        (env,_) <- get
        x <- applySubst env x
        case ds of
            [] -> error "empty case"
            [d] -> do
                lift $ tick stats at_OptSimplifyTrivialCase
                return $ Just (p,Return [x] :>>= d)
            _ -> return $ Just (p,Case x ds)
    gv ([NodeC t xs],Return [NodeC t' xs']) | t == t' = do
            lift $ tick stats at_OptSimplifyNodeReduction
            gv (xs,Return xs')
--    gv (NodeC t xs,Return (NodeC t' [])) |  t' == tagHole = do
--            lift $ tick stats at_OptSimplifyHoleAssignment
--            gv (Tup xs, Return $ Tup $ Prelude.map (properHole . getType) xs)
    gv ([NodeC t xs],Return [(NodeC t' xs')]) | t /= t' = do
            lift $ tick stats at_OptSimplifyBadAssignment
            gv (xs,Error ("Bad Assignment: " ++ show (t,t')) (map getType xs))
    gv (p,e) = do
        (env,_) <- get
        e <- (applySubstE env e)
        e <- gs e
        case e of
            Return v | all valIsNF v, Just n <- zipWithM varBind' p v -> do
                lift $ tick stats at_OptSimplifyCopyPropConst
                modify (`mappend` (Map.unions n,mempty))
                return Nothing
            Return v | Just n <- zipWithM varBind p v -> do
                lift $ tick stats at_OptSimplifyCopyProp
                modify (`mappend` (Map.unions n,mempty))
                return Nothing
            _ -> do
                e <- inline e
                mz <- getCS (p,e)
                modify (mappend (mempty,mz))
                return $ Just (p,e)
    -- funcMap = Map.fromList $ [  fn | fn <- grinFunctions grin, doInline fn]
    doInline (a,fn)
        --  | 'b':_ <- n, not ("bap" `isPrefixOf` n) = True
        --  | "fInstance@" `isPrefixOf` n = True
        | isSimple (a,fn) = True
        | otherwise = False
      --  where n = fromAtom a
    inline app@(App fn as _)
        | Just (itype,l) <- Map.lookup fn env = do
            lift $ tick stats itype
            return $ Return as :>>= l
        | otherwise = tryCSE app
    inline x = tryCSE x
    tryCSE x = do
        (_,ce) <- get
        case Map.lookup x ce of
            Just v -> do
                lift $ tick stats (cseStat x)
                return v
            Nothing -> return x
--    getCS (b,app@(App a [vr@Var {}] _)) | a == funcEval = return $ Map.fromList [(app,Return [b]), (Store b,Return [vr])]
    --getCS (b,app@App{})  = return $ Map.singleton app (Return b)
    --getCS (b@Var {},Store v@(Var _ _)) = return $ Map.singleton (App funcEval [b] TyNode) (Return v)     -- TODO - only works if node stores have always been evaluated.
--    getCS (b@Var {},Store v@(NodeC t _)) | not (isMutableNodeTag t), tagIsWHNF t, not (isHoly v) = return $ Map.fromList [(Store v,Return b),(Fetch b,Return v),(App funcEval [b] TyNode,Return v)]
--    getCS (b@Var {},Store v@(NodeC t _)) | not (isMutableNodeTag t), not (isHoly v) = return $ Map.fromList [(Store v,Return [b])]
    --getCS (b@Var {},Store v@(NodeC t as)) | Just (0,fn) <- tagUnfunction t = return $ Map.fromList [(Store v,Return b),(App funcEval [b] TyNode, App fn as TyNode :>>= n1 :-> Update b n1 :>>= unit :-> Return n1)]
--    getCS (b@Var {},Store v@(NodeC t as)) | Just (0,fn) <- tagUnfunction t = return $ Map.fromList [(Store v,Return b)]
--    getCS (b@Var {},Return (Const v)) = return $ Map.fromList [(Fetch b,Return v),(App funcEval [b] TyNode,Return v)]
--    getCS (b@Var {},Return v) = return $ Map.fromList [(Return b,Return v), (Store b, Store v), (Fetch b, Fetch v)]
    getCS _ = return mempty
    isHoly (NodeC _ as) | any isValUnknown as = True
    isHoly n = isHole n


cseStat n = toAtom $ "Optimize.simplify.cse." ++ g n where
    g App { expFunction = n } = fromAtom n
    g Fetch {} = "Fetch"
    g Store {} = "Store"
    g _ = "Misc"

doApply ret strict (NodeC t xs) ys typ | Just (n,v) <- tagUnfunction t = case n of
    1 | strict -> (App v (xs ++ ys) typ)
    _ -> ret (NodeC (partialTag v (n - 1)) (xs ++ ys))
doApply _ _ n y typ = error $ show ("doApply", n,y,typ)

doEval n@(NodeC t xs) typ
    | tagIsWHNF t = Return [n]
    | tagIsSuspFunction t = App (tagFlipFunction t) xs typ
doEval n typ = error $ show ("doEval", n,typ)

{-
fromBap :: Monad m => Atom -> m Int
fromBap t | 'B':'a':'p':'_':(n:ns) <- fromAtom t, isDigit n = return $ read (n:takeWhile isDigit ns)
fromBap t = fail "not Bap"
-}

-- This only binds variables to variables
varBind :: Monad m => Val -> Val -> m (Map.Map Var Val)
varBind (Var v t) nv@(Var v' t') | t == t' = return $ Map.singleton v nv
varBind (Lit i t) (Lit i' t') | i == i' && t == t' = return mempty
--varBind (Tup xs) (Tup ys) | length xs == length ys  = liftM mconcat $ sequence $  zipWith varBind xs ys
varBind (NodeC t vs) (NodeC t' vs') | t == t' = do
    liftM mconcat $ sequence $  zipWith varBind vs vs'
varBind v r | (getType v) == (getType r)  = fail "unvarBindable"    -- check type to be sure
varBind x y = error $ "varBind: " ++ show (x,y)

-- This binds variables to anything
varBind' :: Monad m => Val -> Val -> m (Map.Map Var Val)
varBind' (Var v t) nv | t == getType nv = return $ Map.singleton v nv
varBind' (Lit i t) (Lit i' t') | i == i' && t == t' = return mempty
--varBind' (Tup xs) (Tup ys) | length xs == length ys  = liftM mconcat $ sequence $  zipWith varBind' xs ys
varBind' (NodeC t vs) (NodeC t' vs') | t == t' = do
    liftM mconcat $ sequence $  zipWith varBind' vs vs'
varBind' v r | (getType v) == (getType r)  = fail "unvarBind'able"    -- check type to be sure
varBind' x y = error $ "varBind': " ++ show (x,y)

isSimple :: (Atom,Lam) -> Bool
isSimple (fn,x) = f (2::Int) x where
    f n _ | n <= 0 = False
    f n (p :-> a :>>= b ) = (f (n - 1) (p :-> a)) &&  (f (n - 1) b)
    f _ (_ :-> Case {}) = False
--    f _ (_ :-> Let {}) = False
--    f _ (_ :-> MkCont {}) = False
    f _ _ = True


manifestNodes as = Prelude.map (isManifestNode . lamExp) as

data UnboxingResult = UnboxTup (Atom,[Ty]) | UnboxConst Val
    deriving(Eq,Ord)

isCombinable :: Monad m => Bool -> Exp -> m UnboxingResult
isCombinable postEval e = ans where
    ans = do
        mn <- f (mempty :: Set.Set Atom) e
        equal mn
    equal [] = fail "empty isCombinable"
    equal [x] = return x
    equal (x:y:rs) = if x == y then equal (y:rs) else fail "not equal"
    f lf (Return [z]) | valIsConstant z = return [UnboxConst z]
    f lf (Return [NodeC t xs]) = return [UnboxTup (t,map getType xs)]
    f lf Error {} = return []
    f lf (Case _ ls) = do
        cs <- Prelude.mapM (f lf) [ e | _ :-> e <- ls ]
        return $ concat cs
    f lf (_ :>>= _ :-> e) = f lf e
--    f lf Let { expBody = body, expIsNormal = False } = f lf body
    f lf (App a _ _) | a `member` lf = return []
{-    f lf Let { expBody = body, expDefs = defs, expIsNormal = True } = ans where
        nlf = lf `union` Set.fromList (map funcDefName defs)
        ans = do
            xs <- mapM (f nlf . lamExp . funcDefBody) defs
            b <- f nlf body
            return (concat (b:xs))-}
    f _ _ = fail "not combinable"



--combineLam postEval nty (p :-> e) = p :-> combine postEval nty e where
combine postEval nty exp = editTail nty f exp where
    f (Return v) | all valIsConstant v  = return $ Return []
    f (Return [NodeC t xs]) = return $ Return xs
    f e = fail $ "combine: " ++ show (postEval,nty,e)

editTail :: Monad m => [Ty] -> (Exp -> m Exp) -> Exp -> m Exp
editTail nty mt te = f (mempty :: Set.Set Atom) te where
    f _ (Error s ty) = return $ Error s nty
    f lf (Case x ls) = return (Case x) `ap` mapM (g lf) ls
{-    f lf lt@Let {expIsNormal = False, expBody = body } = do
        body <- f lf body
        return $ updateLetProps lt { expBody = body }
    f lf lt@Let {expDefs = defs, expIsNormal = True } = do
        let nlf = lf `union` Set.fromList (map funcDefName defs)
        mapExpExp (f nlf) lt-}
{-    f lf lt@MkCont {expLam = lam, expCont = cont } = do
        a <- g lf lam
        b <- g lf cont
        return $ lt { expLam = a, expCont = b }-}
    f lf (e1 :>>= p :-> e2) = do
        e2 <- f lf e2
        return $ e1 :>>= p :-> e2
    f lf e@(App a as t) | a `member` lf = return $ App a as nty
    f lf e = mt e
    g lf (p :-> e) = do e <- f lf e; return $ p :-> e


isKnown NodeC {} = True
isKnown Lit {} = True
isKnown _ = False

mapExp f (b :-> e) = b :-> f e

sizeLam (b :-> exp) = sizeExp exp
sizeExp (x :>>= y) = sizeExp x + sizeLam y
sizeExp (Case e as) = 1 + sum (map sizeLam as)
--sizeExp Let { expDefs = defs, expBody = body } = sizeExp body + sum (map (sizeLam . funcDefBody) defs)
--sizeExp MkCont { expCont = l1, expLam = l2 } = 1 + sizeLam l1 + sizeLam l2
sizeExp x = 1

optimize1 ::  Grin -> Bool -> (Atom,Lam) -> StatT IO Lam
optimize1 grin postEval (n,l) = execUniqT 1 (g l) where
    mtick x = do
        lift $ lift $ putStrLn x
        Stats.mtick x
    g (b :-> e) = f e >>= return . (b :->)
--    f (Case e as :>>= lam)  | (sizeLam lam - 1) * length as <= 3 = do
--        mtick "Optimize.optimize.case-pullin"
--        return (Case e (map (mapExp (:>>= lam)) as))
--    f (Return t@NodeC {} :>>= v@Var {} :-> Update w v' :>>= lr) | v == v' = do
--        mtick "Optimize.optimize.return-update"
--        f (Return t :>>= v :-> Update w t :>>= lr)
    f (e :>>= v1 :-> Return v2) | (all isVar v1) && v1 == v2 = do
        mtick "Optimize.optimize.unit-unit"
        f e
    f (Store t :>>= [v] :-> Fetch v' :>>= lr) | v == v' = do
        mtick "Optimize.optimize.store-fetch"
        f (Store t :>>= [v] :-> Return [t] :>>= lr)
    f (Store t :>>= [v@(Var vr _)] :-> Update  v' w :>>= lr) | v == v', vr `notElem` freeVars w = do
        mtick "Optimize.optimize.store-update"
        f (Store w :>>= [v] :-> Return [] :>>= lr)
    f (Update v t :>>= [] :-> Fetch v' :>>= lr) | v == v' = do
        mtick "Optimize.optimize.update-fetch"
        f (Update v t :>>= [] :-> Return [t] :>>= lr)
--    f (Return [t@NodeC {}] :>>= v :-> App fa [v',a] typ :>>= lr) | fa == funcApply, v == v' = do
--        mtick "Optimize.optimize.return-apply"
--        f (Return [t] :>>= v :-> doApply Return True t [a] typ :>>= lr)
--    f (Return t@NodeC {} :>>= v :-> App fa [v',a] typ) | fa == funcApply, v == v' = do
--        mtick "Optimize.optimize.return-apply"
--        f (Return t :>>= v :-> doApply Return True t [a] typ)
--    f (Return t@NodeC {} :>>= v :-> App fa [v'] typ :>>= lr) | fa == funcApply, v == v' = do

--        mtick "Optimize.optimize.return-apply0"
--        f (Return t :>>= v :-> doApply Return True t [] typ :>>= lr)
--    f (Return t@NodeC {} :>>= v :-> App fa [v'] typ) | fa == funcApply, v == v' = do
--        mtick "Optimize.optimize.return-apply0"
--        f (Return t :>>= v :-> doApply Return True t [] typ)
--    f (Store t@NodeC {} :>>= v :-> App fa [v'] typ :>>= lr) | not (valIsMutable t), fa == funcEval, v == [v'] = do
--        mtick "Optimize.optimize.store-eval"
--        f (Store t :>>= v :-> doEval t typ :>>= lr)
--    f (Store t@NodeC {} :>>= v :-> App fa [v'] typ) | not (valIsMutable t), fa == funcEval, v == v' = do
--        mtick "Optimize.optimize.store-eval"
--        f (Store t :>>= v :-> doEval t typ)
--    f (Update v t@NodeC {} :>>= [] :-> App fa [v'] typ :>>= lr) | fa == funcEval, v == v' = do
--        mtick "Optimize.optimize.update-eval"
--        f (Update v t :>>= [] :-> doEval t typ :>>= lr)
--    f (Update v t@NodeC {} :>>= [] :-> App fa [v'] typ) | fa == funcEval, v == v' = do
--        mtick "Optimize.optimize.update-eval"
--        f (Update v t :>>= [] :-> doEval t typ)
    f (Case n as) | isKnown n = do
        knownCase n as
    f (Case n as :>>= lr) | isKnown n = do
        kc <- knownCase n as
        lr' <- g lr
        return (kc :>>= lr')
    f (Return [n] :>>= b :-> Case b' as :>>= lr) | isKnown n, b == [b'] = do
        c <- knownCase n as
        lr' <- g lr
        return (Return [n] :>>= b :-> c :>>= lr')
    f (Return [n] :>>= b :-> Case b' as ) | isKnown n, b == [b'] = do
        kc <- knownCase n as
        return (Return [n] :>>= b :-> kc)
        {-
    f (Case x as :>>= [] :-> (Case x' as') :>>= lr) | x == x', not $ any (isVar . lamBind) as = do
        c <- caseCombine x as as'
        f (c :>>= lr)
    f (Case x as :>>= [] :-> (Case x' as')) | x == x', not $ any (isVar . lamBind) as = do
        c <- caseCombine x as as'
        f c
    f (Case x as :>>= b :-> m) | count (/= Just []) (manifestNodes as) <= 1 = do
        mtick "Optimize.optimize.case-pullin"
        f $ Case x [ x :-> (e :>>= b :-> m) |  x :-> e <- as ]
    f (cc@Case {} :>>= v :-> Return v' :>>= [NodeC t as] :-> lr ) | v == v' = do
        mtick "Optimize.optimize.case-hoist-return"
        let (va:_) = [ v | v <- [v1..], not $ v `Set.member` fv ]
            var = Var va TyNode
            fv = freeVars as
            mc = modifyTail ( var :-> Return var :>>=  [NodeC t as] :-> Return as)
        return (mc cc :>>= as :-> Return [NodeC t as] :>>= v :-> lr)
    f (lt@Let { expIsNormal = True } :>>= v :-> Return v' :>>= [NodeC t as] :-> lr ) | v == v' = do
        mtick "Optimize.optimize.let-hoist-return"
        let (va:_) = [ v | v <- [v1..], not $ v `Set.member` fv ]
            var = Var va TyNode
            fv = freeVars as
            mc = modifyTail ( var :-> Return var :>>=  [NodeC t as] :-> Return (as))
        return (mc lt :>>= as :-> Return [NodeC t as] :>>= v :-> lr)
        -}

{-    f lt@Let { expDefs = defs, expBody = e :>>= l :-> r } | Set.null (freeVars r `Set.intersection` (Set.fromList $ map funcDefName defs)) = do
        mtick "Optimize.optimize.let-shrink-tail"
        return (updateLetProps lt { expBody = e } :>>= l :-> r)-}
--    f lt@(Let { expDefs = defs, expBody = e :>>= l :-> r } :>>= lr) | Set.null (freeVars r `Set.intersect` (Set.fromList $ map funcDefName defs)) = do
--        mtick "Optimize.optimize.let-shrink-tail"
--        f ((updateLetProps lt { expBody = e } :>>= l :-> r) :>>= lr)
{-    f lt@Let { expDefs = defs, expBody = e :>>= l :-> r } | Set.null (freeVars e `Set.intersection` (Set.fromList $ map funcDefName defs)) = do
        mtick "Optimize.optimize.let-shrink-head"
        return (e :>>= l :-> updateLetProps lt { expBody = r })-}

{-
    f (Case x as :>>= v@(Var vnum _) :-> rc@(Case v' as') :>>= lr) | v == v', count (== Nothing ) (Prelude.map (isManifestNode . lamExp) as) <= 1, not (vnum `Set.member` freeVars lr) = do
        c <- caseHoist x as v as' (getType rc)
        f (c :>>= lr)
    f (Case x as :>>= v :-> rc@(Case v' as')) | v == v', count (== Nothing ) (Prelude.map (isManifestNode . lamExp) as) <= 1 = do
        ch <- caseHoist x as v as' (getType rc)
        f ch
    -- case unboxing
    f (cs@(Case x as) :>>= lr) | Just (UnboxTup (t,ts)) <- isCombinable postEval cs = do
        mtick $ "Optimize.optimize.case-unbox-node.{" ++ show t
        let fv = freeVars cs `Set.union` freeVars [ p | p :-> _ <- as ]
            vs = [ v | v <- [v1..], not $ v `Set.member` fv ]
            vars = [ Var v t | v <- vs | t <- ts ]
        lr <- g lr
        return ((Case x (map (combineLam postEval (tuple ts)) as) :>>= tuple vars  :-> Return (NodeC t vars)) :>>= lr)
    f (cs@(Case x as) :>>= lr) | Just (UnboxConst val) <- isCombinable postEval cs = do
        mtick $ "Optimize.optimize.case-unbox-const.{" ++ show val
        lr <- g lr
        return ((Case x (map (combineLam postEval []) as) :>>= [] :-> Return val) :>>= lr)


    -- let pullin
    f (cs@Let { expIsNormal = True } :>>= lr) |  sizeLTE 1 (filter (/= ReturnError) (getReturnInfo cs)) = do
            mtick "Optimize.optimize.let-pullin"
            return $ modifyTail lr cs
    -- case pullin
    f (cs@Case {} :>>= lr) |  sizeLTE 1 (filter (/= ReturnError) (getReturnInfo cs)) = do
            mtick "Optimize.optimize.case-pullin"
            return $ modifyTail lr cs
  -}

{-
    f cs@(Case x as) | postEval && all isEnum [ p | p :-> _ <- as] = do
        mtick "Optimize.optimize.case-enum"
        let fv = freeVars cs `Set.union` freeVars [ p | p :-> _ <- as ]
            (va:vb:_vr) = [ v | v <- [v1..], not $ v `Set.member` fv ]
        return (Return x :>>= NodeV va [] :-> Case (Var va TyTag) (Prelude.map (untagPat vb) as))
        -}

    -- hoisting must come last
--    f (hexp@Case {} :>>= v@(Var vnum _) :-> rc@(Case v' as') :>>= lr) | v == v', not (vnum `Set.member` freeVars lr) = do
--        c <- caseHoist hexp v as' (getType rc)
--        lr <- g lr
--        return $ c :>>= lr
--    f (hexp@Case {} :>>= v@Var {} :-> rc@(Case v' as')) | v == v'  = do
--        caseHoist hexp v as' (getType rc)

    -- let unboxing
{-    f (cs@Let {} :>>= lr) | Just comb <- isCombinable postEval cs = do
        lr <- g lr
        case comb of
            UnboxTup (t,ts) -> do
                mtick $ "Optimize.optimize.let-unbox-node.{" ++ show t
                let vs = [ v | v <- [v1..], not $ v `Set.member` fv ]
                    vars = [ Var v t | v <- vs | t <- ts ]
                cpe <- combine postEval ts cs
                return ((cpe :>>= vars  :-> Return [NodeC t vars]) :>>= lr)
            UnboxConst val -> do
                mtick $ "Optimize.optimize.let-unbox-const.{" ++ show val
                cpe <- combine postEval [] cs
                return ((cpe :>>= [] :-> Return [val]) :>>= lr)
       where fv = freeVars cs `Set.union` freeVars [ p | p :-> _ <- map funcDefBody (expDefs cs) ]-}

--    f (hexp@Let {} :>>= v@(Var vnum _) :-> rc@(Case v' as') :>>= lr) | v == v', not (vnum `Set.member` freeVars lr) = do
--        c <- caseHoist hexp v as' (getType rc)
--        lr <- g lr
--        return $ c :>>= lr
--    f (hexp@Let {} :>>= v@Var {} :-> rc@(Case v' as')) | v == v'  = do
--        caseHoist hexp v as' (getType rc)

    f (e1 :>>= _ :-> err@Error {}) | isErrOmittable e1 = do
        mtick "Optimize.optimize.del-error"
        return err
    f (e1 :>>= l :-> e2) = do
        e1' <- f e1
        e2' <- f e2
        return (e1' :>>= l :-> e2')
    f (Case x as) = do
       as' <- sequence [ f e >>= return . (b :->)| b :-> e <- as ]
       return $ Case x as'
{-    f Let { expDefs = [fd], expBody = body } | not (funcDefName fd `Set.member` funcTags (funcDefProps fd)), sizeLTE 1 nocc = ans where
        (ne,nocc) = runWriter (c body)
        ans = case nocc of
            [] -> do
                mtick $ "Optimize.let.omitted.{" ++ show (funcDefName fd)
                return ne
            [_] -> do
                mtick $ "Optimize.let.inlined.{" ++ show (funcDefName fd)
                return ne
        c (App a xs _) | a == funcDefName fd = do
            tell [a]
            return $ Return xs :>>= funcDefBody fd
        c e@Let { expDefs = defs } | funcDefName fd `elem` map funcDefName defs = return e
        c e = mapExpExp c e
    f e@Let {} = mapExpExp f e-}
    f e = return e
    notReturnNode (ReturnNode (Just _,_)) = False
    notReturnNode _ = True
    --caseHoist hexp v as' ty | sizeLTE 1 (filter (== Nothing ) (Prelude.map (isManifestNode . lamExp) as))  = do
--    caseHoist hexp v as' ty | sizeLTE 1 (filter (\x -> x /= ReturnError && notReturnNode x ) (getReturnInfo hexp))= do
--        mtick $ "Optimize.optimize.case-hoist" -- .{" ++ show (Prelude.map (isManifestNode . lamExp) as :: [Maybe [Atom]])
--        nic <- f (Case v as')
--        --True <- return $ Set.null $ Set.intersection (freeVars nic) (freeVars (map lamBind as) :: Set.Set Var)
--        return $ modifyTail ([v] :-> nic) hexp -- Case x [ b :-> e :>>= v :-> Case v as' | b :-> e <- as ]
--    caseHoist hexp v as' ty | False && grinPhase grin >= PostDevolve  = do
--        let ufuncs = freeVars fbody
--            fbody = [v] :-> Case v as'
--            cfname = do
--                uniq <- newUniq
--                let fname = toAtom $ "fjumppoint-" ++ show n ++ "-" ++ show uniq
--                if fname `member` (ufuncs :: Set.Set Atom) then cfname else return fname
--        fname <- cfname
--        let f e@(Return NodeC {}) = e :>>= v :-> Case v as'
--            f e@(Return Lit {}) = e :>>= v :-> Case v as'
--            f e = e :>>= v :-> App fname [v] ty
--            nbody = editTail ty f hexp -- (v :-> App fname [v] (getType $ Case v as')) (Case x as)
--        mtick $ "Optimize.optimize.case-hoist-jumppoint.{" ++ show fname -- .{" ++ show (Prelude.map (isManifestNode . lamExp) as :: [Maybe [Atom]])
--        return $ grinLet [createFuncDef True fname fbody] nbody
--    caseHoist hexp v as' ty = do
--       mfc <- f hexp
--       fc <- f (Case v as')
--       return $ mfc :>>= v :-> fc
    knownCase n@(NodeC t vs) as = do
        mtick $ "Optimize.optimize.known-case-node.{" ++ show t
        --let f [] = error $ "no known case:" ++ show (n,as)
        let f [] =  Error "known-case: No known case" (getType (Case n as))
            f ((v@[Var {}] :-> b):_) = Return [n] :>>= v :-> b
            f (([NodeC t' vs'] :-> b):_) | t == t' =  Return (vs) :>>= vs' :-> b
            -- f ((NodeC t' vs' :-> b):_) | t == t' = let (xs,ys) = unzip [ (Var x t,y) | (x,y@(Var _ t)) <- Map.toList mp] in Return (Tup ys) :>>= Tup xs :-> b
            f (_:as) = f as
        return $ f as
    knownCase n@(Lit l _) as = do
        mtick $ "Optimize.optimize.known-case-lit.{" ++ show n
        let f [] =  Error "known-case: No known case" (getType (Case n as))
            f ((v@[Var {}] :-> b):_) = Return [n] :>>= v :-> b
            f (([Lit l' _] :-> b):_) | l == l' = b
            f (_:as) = f as
        return $ f as
--    caseCombine x as as' = do
--        mtick $ "Optimize.optimize.case-combine"
--        let etags = [ bd | bd@(NodeC t _ :-> _) <- as, t `notElem` [ t | NodeC t _ :-> _ <- as' ] ]
--  --          ttags = [ bd | bd@(Tag t:-> _) <- as, t `notElem` [ t | Tag t :-> _ <- as' ] ]
--            as'' = Prelude.map f as'
-- --           f ([v@Var {}] :-> b) | getType v == TyTag = v :-> Case v ttags :>>= [] :-> b
--            f ([v@Var {}] :-> b) = v :-> Case v etags :>>= [] :-> b
--            f (n@[(NodeC t _)] :-> b) = case [ a | a@(NodeC t' _ :-> _) <-  as, t == t'] of
--                [bind :-> body] -> n :-> Return n :>>= bind :-> body :>>= [] :-> b
----            f (n@[(Tag t)] :-> b) = case [ a | a@(Tag t' :-> _) <-  as, t == t'] of
----                [bind :-> body] -> n :-> Return n :>>= bind :-> body :>>= [] :-> b
--            -- f r
--        return $ Case x as''

isEnum (NodeC t []) = True
isEnum (Var t TyNode) = True
isEnum _ = False

--untagPat _ ([NodeC t []] :-> e) = [Tag t] :-> e
--untagPat vb ([v@Var{}] :-> e) = [Var vb TyTag] :-> Return [NodeV vb []] :>>= [v] :-> e


deadVars :: Stats -> (Atom,Lam) -> IO (Atom,Lam)
deadVars stats (n,l) = do
    (x,_) <- (evalStateT (fizz fn gv f whizState l) (mempty :: Set.Set Var) );
    return (n,x)
    where
    fn _ m = m
    f x = do
        uv <- get
        put $ (Set.union uv (freeVars x))
        return x
    gv w@(v, e) | isOmittable e = do
        (uv) <- get
        if  any (`Set.member` uv) (freeVars v) then
            f e >> return (Just w)
         else lift (tick stats at_OptSimplifyDeadVar) >> return Nothing
    gv w@(vs,Case x xs) = do
        uv <- get
        put $ (Set.union uv (freeVars x))
        let used v = any (`Set.member` uv) (freeVars v)
        case partition used vs of
            (_,[]) -> return $ Just w
            (nvs,unused) -> do
                replicateM_ (length unused) $ lift (tick stats "Optimize.simplify.dead-var-case-tup")
                let ml = modifyTail (vs :-> Return nvs)
                return (Just (nvs,ml (Case x xs) ))
    gv w@(_,e) = f e >> return (Just w)



{-# NOINLINE simplify #-}

simplify ::
    Stats     -- ^ stats to update
    -> Grin   -- ^ input grin
    -> IO Grin
simplify stats grin = do
    let postEval = phaseEvalInlined (grinPhase grin)
        fs = grinFuncs grin
        uf = [ ((a,l),collectUsedFuncs l) | (a,l) <- fs ]
        graph = newGraph uf (\ ((a,_),_) -> a) (\ (_,(fi,fd)) -> (if postEval then [] else fi) ++ fd)
        rf = reachable graph (grinEntryPointNames grin)
        reached = Set.fromList $ Prelude.map  (\ ((a,_),_) -> a) rf
        graph' = if postEval then graph else newGraph rf (\ ((a,_),_) -> a) (\ (_,(_,fd)) -> fd)
        (lb,os) = findLoopBreakers ( fromEnum . not . isSimple . fst) (const True) graph'
        loopBreakers = Set.fromList [ a | ((a,_),_) <- lb ]
        indirectFuncs = if postEval then Set.empty else Set.fromList (concat [ fi | (_,(fi,_)) <- rf ])
        hist =  Hist.fromList $ concat [ fd | (_,(_,fd)) <- rf ]
    let opt env a n l = do
                --(_,nl) <- deadVars stats (a,l)
                --(_,nl) <- simplify1 stats env (a,nl)
                --let Identity nl'' = whizExps return nl
                -- putDocM CharIO.putErr (prettyFun (a,nl''))
                --let (nl',stat) = runStatM (optimize1 grin postEval (a,nl''))
                (nl',stat) <- runStatT (optimize1 grin postEval (a,l))
                tickStat stats stat
                return nl'
                {-
        opt env a n l = do
            stats' <- Stats.new
            (_,nl) <- deadVars stats (a,l)  -- if the deadVars did not enable any other transformations we don't need to iterate as deadVars is idempotent
            (_,nl) <- simplify1 stats' env (a,nl)
            t <- Stats.getTicks stats'
            case t of
                0 -> return nl
                _ -> do
                    -- when (n > 2) $ Stats.print (show a) stats'
                    Stats.combine stats stats'
                    -- tick stats $ "Optimize.repeat.{" ++ show a ++ "}"
                    opt env a (n + 1 :: Int) nl
                    -}
        --procF (out,env) ((a,_),_) | False <- a `Set.member` reached = do
        --    tick stats (toAtom "Optimize.dead.function")
        --    return (out,env)
        procF (out,env) ((a,l),_) = do
            nl <- opt env a (0::Int) l
            let iname t = toAtom $ "Optimize.simplify.inline." ++ t ++ ".{" ++ fromAtom a  ++ "}"
                inline
                    | a `elem` noInline = Map.empty
                    | a `Set.member` loopBreakers = Map.empty
                    | Hist.find a hist == 1 = Map.singleton a (iname "once",nl)
                    | a `Set.member` indirectFuncs = Map.empty
                    | isSimple (a,nl) = Map.singleton a (iname "simple",nl)
                    | otherwise = Map.empty
            return ((a,nl):out , inline `Map.union` env)

    (nf,_) <- foldM procF ([],mempty) os
    return $ setGrinFunctions nf grin


noInline = [toAtom "fData.IORef.readIORef", toAtom "fData.IORef.writeIORef"]


-- TODO have this collect CAF info ignoring updates.

collectUsedFuncs :: Lam -> ([Atom],[Atom])
collectUsedFuncs (as :-> exp) = (snub $ concatMap tagToFunction (Seq.toList iu),sort $ Seq.toList du) where
    (iu,du) =  f exp
    f (e1 :>>= _ :-> e2) = f e1 `mappend` f e2
    f (App a vs _) =  (Seq.fromList (freeVars vs), Seq.singleton a)
    f (Case e alts) =  mconcat ((Seq.fromList (freeVars e) , Seq.empty):[ f e | _ :-> e <- alts])
    f e = (Seq.fromList [ v | v <- freeVars e ],Seq.empty)


-- renames all functions to unique names, grin-wide

renameUniqueGrin :: Grin -> Grin
renameUniqueGrin grin = res where
    (res,()) = evalRWS (execUniqT 1 ans) ( mempty :: Map.Map Atom Atom) (fromList [ x | (x,_) <- grinFuncs grin ] :: Set.Set Atom)
    ans = do mapGrinFuncsM f grin
    f (l :-> b) = g b >>= return . (l :->)
    g a@App  { expFunction = fn } = do
        m <- lift ask
        case mlookup fn m of
            Just fn' -> return a { expFunction = fn' }
            _ -> return a
    g a@Call { expValue = Item fn t } = do
        m <- lift ask
        case mlookup fn m of
            Just fn' -> return a { expValue = Item fn' t }
            _ -> return a
{-    g (e@Let { expDefs = defs }) = do
        (defs',rs) <- liftM unzip $ flip mapM defs $ \d -> do
            (nn,rs) <- newName (funcDefName d)
            return (d { funcDefName = nn },rs)
        local (fromList rs `mappend`) $  mapExpExp g e { expDefs = defs' }-}
    g b = mapExpExp g b
    newName a = do
        m <- lift get
        case member a m of
            False -> do lift $ modify (insert a); return (a,(a,a))
            True -> do
            let cfname = do
                uniq <- newUniq
                let fname = toAtom $ show a  ++ "-" ++ show uniq
                if fname `member` (m :: Set.Set Atom) then cfname else return fname
            nn <- cfname
            lift $ modify (insert nn)
            return (nn,(a,nn))

mapGrinFuncsM :: Monad m => (Lam -> m Lam) -> Grin -> m Grin
mapGrinFuncsM f grin = liftM (`setGrinFunctions` grin) $ mapM  (\x -> do nb <- f (funcDefBody x); return (funcDefName x, nb)) (grinFunctions grin)