packages feed

firstify-0.1: Yhc/Core/Firstify/Paper.hs

module Yhc.Core.Firstify.Paper(paper) where

import Yhc.Core hiding (uniqueBoundVarsCore, uniqueBoundVars)
import Yhc.Core.FreeVar3
import Yhc.Core.UniqueId

import Yhc.Core.Util
import Yhc.Core.Firstify.Mitchell.Template
import Yhc.Core.Firstify.Mitchell.Terminate
import qualified Yhc.Core.Firstify.Mitchell.BiMap as BiMap

import Control.Exception
import Control.Monad
import Control.Monad.State
import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.List
import Data.Maybe
import Debug.Trace
import Safe



type SS a = State S a

type BoxesSet = Set.Set CoreFuncName

data S = S {terminate :: Terminate -- termination check
           ,special :: BiMap.BiMap CoreFuncName Template -- which special variants do we have
           ,coreRest :: Core -- the functions are not there
           ,varId :: Int -- what is the next variable id to use
           ,funcId :: Int -- what is the next function id to use

           -- used in the algorithm steps
           ,boxes :: BoxesSet
           ,core :: CoreFuncMap
           -- used for global algorithm control
           ,stack :: Map.Map CoreFuncName Bool -- True is on the stack, False is done
           ,assume :: [(CoreFuncName,Bool,Int)] -- what you assumed
           -- used for local algorithm control
           ,templated :: Bool
           }

instance UniqueId S where
    getId = varId
    putId x s = s{varId = x}


-- First lambda lift (only top-level functions).
-- Then perform the step until you have first-order.
paper :: Core -> Core
paper c = fromCoreFuncMap c2 $ coreReachableMap ["main"] res
    where
        res = evalState (liftM toCoreFuncMap (uniqueBoundVarsCore c2) >>= run) (s0 :: S)
        s0 = S (emptyTerminate True) BiMap.empty c2 0 (uniqueFuncsNext c2)
               undefined undefined undefined undefined undefined
        c2 = ensureInvariants [NoRecursiveLet,NoCorePos] c


run :: CoreFuncMap -> SS CoreFuncMap
run precore = do
    cr <- etaRaise precore
    modify $ \s -> s{core=cr, boxes=boxApprox cr}
    step
    liftM core get


-- need to return assumptions made
--      (name :: CoreFuncName, box :: Bool, arity :: Int)
-- need to track which functions are on the stack (Just True), and which
-- have been done (Just False)
step :: SS ()
step = do
    () <- trace "Iterating" $ return ()
    modify $ \s -> s{stack=Map.empty, assume=[]}
    go "main"
    s <- get
    let check (name,b,a) = sArity s name == a && sBoxed s name == b
    if all check (assume s) then return () else step
    where

    -- make sure the name has been optimised already
    go name = do
        s <- get
        case Map.lookup name (stack s) of
            Just False -> return ()
            Just True -> modify $ \s -> s{assume=(name,sBoxed s name,sArity s name):assume s}
            Nothing -> let fun = core s Map.! name in
                if isCorePrim fun then do
                    modify $ \s -> s{stack = Map.insert name False (stack s)}
                 else do
                    modify $ \s -> s{stack = Map.insert name True (stack s)}
                    fun <- func fun
                    fun <- goes fun
                    modify $ \s -> s
                        {boxes = if not (sBoxed s name) && isBox (sBoxed s) (coreFuncBody fun)
                                 then Set.insert name (boxes s) else boxes s
                        ,core = Map.insert name fun (core s)
                        ,stack = Map.insert name False (stack s)}

    goes fun = do
        mapM go [x | CoreFun x <- universe $ coreFuncBody fun]
        modify $ \s -> s{templated = False}
        fun <- func fun
        s <- get
        if templated s then goes fun else return fun




sArity s name = coreFuncArity (core s Map.! name)
sBoxed s name = name `Set.member` boxes s


-- two steps:
-- 1) etaRaise a function if you can
-- 2) ensure all CoreFun's are wrapped in CoreApp's
etaRaise :: CoreFuncMap -> SS CoreFuncMap
etaRaise core = liftM Map.fromAscList $ mapM f $ Map.toAscList core
    where
        f (nam1,CoreFunc nam2 args body) = do
            body <- g body
            return (nam1, CoreFunc nam2 args body)
        f x = return x

        g (CoreFun x) = h x []
        g (CoreApp (CoreFun x) xs) = h x =<< mapM g xs
        g x = descendM g x

        h x xs = do
            let ar = coreFuncArity $ core Map.! x
                nxs = length xs
            if ar <= nxs
                then return $ CoreApp (CoreFun x) xs
                else do
                    vs <- getVars (ar - nxs)
                    return $ CoreLam vs (CoreApp (CoreFun x) (xs ++ map CoreVar vs))


type SetBoxes = Set.Set CoreFuncName


-- for each function, store a Bool saying if you are a box or not
boxApprox :: CoreFuncMap -> SetBoxes
boxApprox core = Set.fromAscList [a | (a,True) <- Map.toAscList $ f Map.empty "main"]
    where
        f res x | x `Map.member` res = res
                | isCorePrim fun = Map.insert x False res
                | otherwise = Map.insert x (isBox (res2 Map.!) bod) res2
            where
                -- important, initially assume always not a box, then refine
                res2 = foldl f (Map.insert x False res) calls
                calls = [x | CoreFun x <- universe bod]
                bod = coreFuncBody fun
                fun = core Map.! x



isBox :: (CoreFuncName -> Bool) -> CoreExpr -> Bool
isBox f (CoreApp (CoreCon _) xs) = any isCoreLam xs || any (isBox f) xs
isBox f (CoreLet _ x) = isBox f x
isBox f (CoreApp (CoreFun x) _) = f x
isBox f (CoreCase _ xs) = any (isBox f . snd) xs
isBox f _ = False



-- run over a function
func :: CoreFunc -> SS CoreFunc
func (CoreFunc name args body) = do
    (args2,body2) <- liftM fromCoreLam $ transformM f body
    return $ CoreFunc name (args++args2) body2
    where
        -- ARITY RAISING RULE
        -- SPECIALISE RULE
        f (CoreApp (CoreFun x) xs) = do
            s <- get
            let a = sArity s x
                extra = a - length xs
            if extra <= 0
                then template x xs
                else do
                    vs <- getVars extra
                    let xs2 = xs ++ map CoreVar vs
                    f . CoreLam vs =<< template x xs2

        -- must go before the inline rule, or gets overlapped
        f (CoreCase on alts) | not $ null ar = do
                vs <- getVars $ maximum ar
                let vs2 = map CoreVar vs
                alts <- sequence [liftM ((,) a) $ f $ CoreApp b vs2 | (a,b) <- alts]
                f . CoreLam vs =<< f (CoreCase on alts)
            where
                ar = [length vs | (_, CoreLam vs x) <- alts]

        -- INLINE RULE
        f o@(CoreCase (CoreApp (CoreFun x) xs) alts) = do
            s <- get
            let b = sBoxed s x
            if not b then return o else do
                x2 <- inline x
                on <- f $ CoreApp x2 xs
                f $ CoreCase on alts

        f (CoreCase (CoreFun x) _) = error "unwrapped fun"

        -- SIMPLIFY RULES
        f (CoreApp (CoreLam vs x) ys) = do
                transformM f $ coreApp (coreLam vs2 x2) ys2
            where
                i = min (length vs) (length ys)
                (vs1,vs2) = splitAt i vs
                (ys1,ys2) = splitAt i ys
                (rep,bind) = partition (\(a,b) -> isCoreVar b || countFreeVar a x <= 1) (zip vs1 ys1)
                x2 = coreLet bind $ replaceFreeVars rep x

        f (CoreCase (CoreLet bind on) alts) = do
            cas <- f $ CoreCase on alts
            f $ CoreLet bind cas

        f (CoreCase on@(CoreApp (CoreCon x) xs) alts) =
                (if null xs then return else f) $ head $ concatMap g alts
            where
                g (PatDefault, y) = [y]
                g (PatCon c vs, y) = [coreLet (zip vs xs) y | c == x]
                g _ = []

        f (CoreCase (CoreCase on alts1) alts2) =
                f =<< liftM (CoreCase on) (mapM g alts1)
            where
                g (lhs,rhs) = do
                    CoreCase _ alts22 <- duplicateExpr $ CoreCase (CoreLit $ CoreInt 0) alts2
                    rhs <- f $ CoreCase rhs alts22
                    return (lhs, rhs)

        f (CoreLam vs1 (CoreLam vs2 x)) = return $ CoreLam (vs1++vs2) x
        f (CoreLet bind (CoreLam vs x)) = f . CoreLam vs =<< f (CoreLet bind x)
        f (CoreApp (CoreApp x y) z) = return $ CoreApp x (y++z)

        f (CoreLet bind x) = do
                s <- get
                let (bad,good) = partition (\(a,b) -> isCoreLam b || isBox (sBoxed s) b) bind
                if null bad
                    then return $ CoreLet bind x
                    else transformM f =<< liftM (coreLet good) (transformM (g bad) x)
            where
                g bad (CoreVar x) = case lookup x bad of
                                    Nothing -> return $ CoreVar x
                                    Just y -> duplicateExpr y
                g bad x = return x

        f x = return x


inline :: CoreFuncName -> SS CoreExpr
inline name = do
    c <- liftM core get
    let CoreFunc _ args body = c Map.! name
    duplicateExpr $ coreLam args body


template :: CoreFuncName -> [CoreExpr] -> SS CoreExpr
template x xs = do
    s <- get
    let o = CoreApp (CoreFun x) xs
        t = templateNorm $ templateCheck (sBoxed s) o
    if isCorePrim (core s Map.! x) || t == templateNone then return o else do
        let holes = templateHoles o t
        case BiMap.lookupRev t (special s) of
            -- OPTION 2: Previously done
            Just name -> do
                return $ CoreApp (CoreFun name) holes
            -- OPTION 3: New todo
            _ -> do
                let name = uniqueJoin (templateName t) (funcId s)
                fun <- templateGenerate (core s Map.!) name t
                modify $ \s -> s{funcId = funcId s + 1
                                ,special = BiMap.insert name t (special s)
                                ,core = Map.insert name fun (core s)
                                ,templated = True
                                }
                return $ CoreApp (CoreFun name) holes