optimusprime 0.0.1.20091113 → 0.0.1.20091114
raw patch · 11 files changed
+897/−2 lines, 11 filesdep ~flite
Dependency ranges changed: flite
Files
- Optimus/CallGraph.hs +149/−0
- Optimus/Generalise.hs +84/−0
- Optimus/Homeo.hs +42/−0
- Optimus/Inline.hs +78/−0
- Optimus/Pretty.hs +49/−0
- Optimus/Simplify.hs +134/−0
- Optimus/Strategy.hs +185/−0
- Optimus/Trace.hs +36/−0
- Optimus/Uniplate.hs +30/−0
- Optimus/Util.hs +104/−0
- optimusprime.cabal +6/−2
+ Optimus/CallGraph.hs view
@@ -0,0 +1,149 @@+module Optimus.CallGraph where+ import Flite.Pretty+ import Flite.Syntax+ import Flite.Traversals+ import Optimus.Uniplate+ import Optimus.Util+ + import Control.Monad+ import Control.Monad.Fix+ import Control.Monad.State+ import qualified Data.Map as Map+ import qualified Data.Set as Set+ import Data.Generics.Uniplate+ import Data.Graph.Inductive.Graph+ import Data.Graph.Inductive.PatriciaTree+ import Data.GraphViz hiding (Int)+ import Data.List+ import Data.Maybe+ import Debug.Trace+ + callGraph :: Prog -> Gr Id Id+ callGraph p = mkGraph (zip [1..] nodes) edges+ where+ nodes = nub $ [ f | Func f _ _ <- p ]+ lookupNodes = flip lookup (zip nodes [1..])+ edges = nub . catMaybes $ [ liftM2 (\x y -> (x, y, "")) (lookupNodes f) (lookupNodes g) | Func f _ r <- p, Fun g <- universe r ]+ + conGraph :: Prog -> Gr Id [Id]+ conGraph p = mkGraph (zip [1..] nodes) edges+ where+ nodes = nub $ [ f | Func f _ _ <- p ]+ lookupNodes = flip lookup (zip nodes [1..])+ edges = nub . catMaybes $ [ liftM2 (\x y -> (x, y, consUsed r)) (lookupNodes f) (lookupNodes g) | Func f _ r <- p, Fun g <- universe r ]+ consUsed e = nub $ [ c | Con c <- universe e ] ++ [ c | Case _ as <- universe e, (p', _) <- as, Con c <- universe p' ]+ + produceDot :: (Graph gr, Show b) => gr Id b -> String+ produceDot g = printDotGraph $ graphToDot True g [] labelNode labelEdge+ where+ labelNode (_, l) = [Label (StrLabel l), Color [ColorName $ if words l !! 0 `elem` primitives then "black" else "red"]]+ labelEdge (_, _, l) = if show l == "\"\"" then [] else [Label (StrLabel $ show l)]+ + -----------------------+ + arityGraph :: Prog -> Gr Id Id+ arityGraph p = mkGraph (zip [1..] nodes) edges+ where+ nodes = nub $ [ f | Func f _ _ <- p ]+ lookupNodes = flip lookup (zip nodes [1..])+ arities = [ (f, length a) | Func f a _ <- p ] ++ map (flip (,) 2) primitives+ arity f = fromMaybe (error "Missing function definition") $ lookup f arities+ edges = nub . catMaybes $ [ liftM2 (\x y -> (x, y, "")) (lookupNodes f) (lookupNodes g) | Func f _ r <- p, App (Fun g) ys <- universe r, arity f <= length ys ]+ + hofGraph :: Prog -> Gr Id Id+ hofGraph p = mkGraph (zip [1..] nodes) edges+ where+ nodes = nub $ [ f | Func f _ _ <- p ]+ ++ [ concat $ intersperse " " $ g : [ if hof e then showArg e else "_" | e <- ys ]+ | Func f _ r <- p, App (Fun g) ys <- universe r, arity f <= length ys, any hof ys ]+ lookupNodes = flip lookup (zip nodes [1..])+ arities = [ (f, length a) | Func f a _ <- p ] ++ map (flip (,) 2) primitives+ arity f = fromMaybe (error "Missing function definition") $ lookup f arities+ edges = nub . catMaybes $ [ if any hof ys then Nothing else Just ()+ >> liftM2 (\x y -> (x, y, "")) (lookupNodes f) (lookupNodes g)+ | Func f _ r <- p, App (Fun g) ys <- universe r, arity f <= length ys ]+ hof (App (Fun f) ys) = arity f > length ys+ hof (Fun f) = arity f > 0+ hof _ = False+ + data TravArcType = Call | Composition+ + travGraph :: Prog -> Gr Id TravArcType+ travGraph p = mkGraph (zip [1..] nodes) edges+ where+ edgesRaw = trav p+ nodes = nub . uncurry (++) . (\(_, x, y) -> (x, y)) . unzip3 $ edgesRaw+ lookupNodes = fromJust . flip lookup (zip nodes [1..])+ edges = [ (lookupNodes f, lookupNodes g, t) | (t@(Call), f, g) <- edgesRaw ]+ + produceDotTrav :: Gr Id TravArcType -> String+ produceDotTrav g = printDotGraph $ graphToDot True g [] labelNode labelEdge+ where+ labelNode (_, l) = [Label (StrLabel l)] ++ if words l !! 0 `elem` primitives then [Style [SItem Dotted []]] else []+ labelEdge (_, _, t) = case t of { Call -> [] ; Composition -> [Style [SItem Dashed []], ArrowHead emptyArr] }+ + trav :: Prog -> [(TravArcType, Id, Id)]+ trav p = fst $ execState (t [] "main" (body "main")) ([], [])+ where+ m = byFuncName (p ++ [ Func f [Var "_", Var "_"] Bottom | f <- primitives ])+ body f = fromMaybe (error "Missing function definition") $ Map.lookup f m >>= return . funcRhs+ arity = length . args+ args f = fromMaybe (error "Missing function definition") $ Map.lookup f m >>= return . concatMap patVars . funcArgs+ unsat (App (Fun f) ys) = arity f > length ys+ unsat (Fun f) = arity f > 0+ unsat _ = False+ t :: [(Id, Exp)] -> Id -> Exp -> State ([(TravArcType, Id, Id)], [Id]) Exp+ t env f (App (App x ys) zs) = t env f (App x $ ys ++ zs)+ t env f (Var v) = return $ fromMaybe (Con "") $ lookup v env+ t env f (App (Fun g) ys) | arity g <= length ys = do+ (oldEdges, oldNames) <- get+ put (oldEdges, [])+ bs <- sequence [ t env f e >>= \e' -> return $ if unsat e' then Just (v, e') else Nothing | (v, e) <- zip (args g) ys ]+ g' <- return . concat . intersperse " " $ g : (map (\x -> fromMaybe "_" (return . show . snd =<< x)) $ bs)+ (newEdges, newNames) <- get+ put ((Call, f, g'): map ((,,) Composition g') newNames ++ newEdges, [])+ e' <- if g' `notElem` [ x | (Call, _, x) <- newEdges]+ then t (catMaybes bs) g' (body g)+ else return Bottom+ (newerEdges, _) <- get+ put (newerEdges, g':oldNames)+ return e'+ t env f (App x ys) = do+ x' <- t env f x+ if x' == x || x' == Bottom then mapM (t env f) ys >>= return . App x' else t env f (App x' ys)+ t env f (Let bs x) = do+ bs' <- sequence [ liftM ((,) v) (t env f e) | (v, e) <- bs ]+ t (bs ++ env) f x+ t env f (Case x as) = do+ x <- t env f x+ ys <- sequence [ t (map (flip (,) Bottom) (patVars p) ++ env) f y | (p, y) <- as ]+ return $ head (ys ++ [x])+ t env f e = return e+ + selectFuncs n p = nub $ selectFuncs2 n p ++ ["main"]+ + selectFuncs1 :: Prog -> [Id]+ selectFuncs1 p = (evalState (traverse "main") Set.empty)+ where+ m = byFuncName p+ body f = fromMaybe Bottom (Map.lookup f m >>= return . funcRhs)+ traverse :: Id -> State (Set.Set Id) [Id]+ traverse f = let+ cs = filter (flip notElem $ f : primitives) . calls . body $ f+ csSet = Set.fromList cs in do+ visited <- get+ put $ csSet `Set.union` visited+ rest <- mapM traverse . Set.toList $ csSet `Set.difference` visited+ return $ if length cs > 1 then concat rest ++ [f] else concat rest+ + selectFuncs2 :: Int -> Prog -> [Id]+ selectFuncs2 n p = take n [ f | (f, n) <- selected, n >= limit ]+ where+ selected :: [(Id, Int)]+ selected = map (\f -> maybe (f, -1) ((,) f) $ lookup f call_count) $ selectFuncs1 p+ call_count :: [(Id, Int)]+ call_count = map (\xs@(x:_) -> (x, length xs)) . group . sort . concatMap (calls . funcRhs) $ p+ limit :: Int+ limit | length selected > n = (reverse . sort . map snd $ selected) !! n+ | otherwise = 0+
+ Optimus/Generalise.hs view
@@ -0,0 +1,84 @@+module Optimus.Generalise where+ import Flite.Fresh+ import Flite.Syntax+ import Flite.Traversals+ import Optimus.Homeo+ import Optimus.Trace+ import Optimus.Uniplate+ + import Control.Monad+ import Data.Generics.Uniplate+ import Data.List+ import Data.Maybe++ type GenBinding = (GenFlag, Id, Exp)+ data GenFlag = Safe | Unsafe+ deriving Show++ generalise :: Exp -> Exp -> Fresh Exp+ generalise s t | couple t s = do+ v <- fresh+ (x', bind, _) <- msg (Var v, [(v, s)], [(v, t)])+ return $ Let bind x'+ | otherwise = return s+ where+ msg :: (Exp, [Binding], [Binding]) -> Fresh (Exp, [Binding], [Binding])+ msg m | null vsts = return m+ | otherwise = do+ ys <- mapM (const fresh) ss + msg (subst (y_ $ map Var ys) v tg, th0' ++ zip ys ss, th1' ++ zip ys ts)+ where+ vsts = [ (v, s, t) | (v, s) <- th0, (w, t) <- th1, v == w, s =~ t ]+ th0' = [ b | b@(v', _) <- th0, v /= v' ]+ th1' = [ b | b@(w', _) <- th1, v /= w' ]+ (v, s, t) = head vsts+ (ss, y_) = uniplate s+ ts = children t+ (tg, th0, th1) = t_G (show m) m+ + generalise1 :: Exp -> Exp -> Fresh Exp+ generalise1 s t | couple t s = do+ v <- fresh+ (x', bind, _) <- msg (Var v, [(Safe, v, s)], [(v, t)])+ return $ case null [ undefined | (Safe, _, _) <- bind ] of+ True -> s+ False -> Let [ (v, y) | (Safe, v, y) <- bind ] (substMany x' [ (y, v) | (Unsafe, v, y) <- bind ])+ | otherwise = return s+ where+ msg :: (Exp, [GenBinding], [Binding]) -> Fresh (Exp, [GenBinding], [Binding])+ msg m | null vsts = return m+ | otherwise = do+ ys <- mapM (const fresh) ss + msg (subst (y_ $ map Var ys) v tg, th0' ++ zip3 sFlags ys ss, th1' ++ zip ys ts)+ where+ vsts = [ (v, s, t) | (_, v, s) <- th0, (w, t) <- th1, v == w, s =~ t ]+ th0' = [ gb | gb@(_, v', _) <- th0, v /= v' ]+ th1' = [ b | b@(w', _) <- th1, v /= w' ]+ (v, s, t) = head vsts+ (ss, y_) = uniplate s+ ts = children t+ sFlags = map flag (zipWith inaccessibleVars ss (map (\ms -> subst (y_ ms) v tg) (markers ss)))+ (tg, th0, th1) = t_G (show m) m+ flag xs | null xs = Safe+ | otherwise = Unsafe+ + markers :: [a] -> [[Exp]]+ markers [] = []+ markers xs = nub . permutations $ Var "%" : replicate (length xs - 1) (Var "")+ + inaccessibleVars :: Exp -> Exp -> [Id]+ inaccessibleVars x y = freeVars x `intersect` bindingsAtMarker y+ + bindingsAtContext :: (Exp -> Exp) -> [Id]+ bindingsAtContext ctx = bindingsAtMarker (ctx $ Var "%")+ + bindingsAtMarker :: Exp -> [Id]+ bindingsAtMarker x | isJust vs = nub . fromJust $ vs+ | otherwise = error "Never reached marker."+ where+ vs = f x+ f e@(Var "%") = Just []+ f e@(Let bs _) = (listToMaybe . catMaybes . map f) (children e) >>= return . (++) (map fst bs)+ f e@(Case _ as) = (listToMaybe . catMaybes . zipWith (\p x -> x >>= (return . (++) p)) ([] : map (patVars . fst) as) . map f) (children e)+ f e@(App _ _) = (listToMaybe . catMaybes . map f) (children e)+ f _ = Nothing
+ Optimus/Homeo.hs view
@@ -0,0 +1,42 @@+module Optimus.Homeo where+ import Flite.Syntax+ import Optimus.Uniplate+ + import Data.Generics.Uniplate+ + (<||) :: Exp -> Exp -> Bool+ x <|| y = dive x y || couple x y+ + dive :: Exp -> Exp -> Bool+ dive x y = any (x <||) (children y)+ + couple :: Exp -> Exp -> Bool+ couple x y = x =~ y && length x_ == length y_+ && and (zipWith (<||) x_ y_)+ where+ x_ = children x+ y_ = children y+ + -- Must alpha first+ (=~) :: Exp -> Exp -> Bool+ (Bottom) =~ (Bottom) = True+ (Int i) =~ (Int j) = i == j+ (Fun f) =~ (Fun h) = f == h+ (Con c) =~ (Con d) = c == d+ (Var _) =~ (Var _) = True+ (App _ xs) =~ (App _ ys) = length xs == length ys+ (Let bs_x _) =~ (Let bs_y _) = length bs_x == length bs_y+ -- && and (zipFst (==) bs_x bs_y)+ (Case _ alts_x) =~ (Case _ alts_y) = length alts_x == length alts_y+ -- && and (zipFst (==~) alts_x alts_y)+ _ =~ _ = False+ + (==~) :: Exp -> Exp -> Bool+ (App _ xs) ==~ (App _ ys) = length xs == length ys+ && and (zipWith (==~) xs ys)+ (Con c) ==~ (Con d) = c == d+ (Var v) ==~ (Var w) = v == w+ _ ==~ _ = False+ + zipFst :: (a -> b -> c) -> [(a, d)] -> [(b, e)] -> [c]+ zipFst f = zipWith (\(x, _) (y, _) -> f x y)
+ Optimus/Inline.hs view
@@ -0,0 +1,78 @@+-- |Handles the inlining of single function applications and inlining+-- residuals through a whole program+module Optimus.Inline (+ maybeInline,+ progInline+ ) where+ import Control.Monad+ import Data.List+ import qualified Data.Map as Map+ import qualified Data.Set as Set+ import Data.Maybe++ import Flite.Fresh+ import Flite.Syntax+ import Flite.Traversals+ + import Optimus.Uniplate+ import Optimus.Util+ + -- |Build an inlined form of a saturated function application given+ -- the correct function definition. Assumes that equations have+ -- been desugared. Simplify afterwards for efficiency.+ maybeInline :: [Exp] -> Decl -> Maybe (Fresh Exp)+ maybeInline ys (Func _ args rhs)+ | length_args > length_ys = fail "Unsaturated application."+ | otherwise = Just $ do+ vs <- mapM (const fresh) args+ let (bound, remaining) = splitAt (length args) ys+ let rhs' = substMany rhs (zip (map Var vs) (concatMap patVars args))+ return $ mkApp (mkLet (zip vs bound) rhs') remaining+ where+ length_ys = length ys+ length_args = length args+ + -- |Make an Application only if there are arguments.+ mkApp :: Exp -> [Exp] -> Exp+ mkApp x [] = x+ mkApp x ys = App x ys+ + -- |Make a Let only if there are bindings.+ mkLet :: [Binding] -> Exp -> Exp+ mkLet [] y = y+ mkLet bs y = Let bs y+ + -- |Inline all non-recursive function applications.+ progInline :: Prog -> Fresh Prog+ progInline p = progInline' (byFuncName p) p+ where+ callCounts = map (\xs@(x:_) -> (x, length xs)) . group . sort . concatMap (calls . funcRhs) $ p+ highlyCalled = Set.fromList [ f | (f, n) <- callCounts, n > 10 ]+ progInline' :: Map.Map Id Decl -> Prog -> Fresh Prog+ progInline' m [] = return $ Map.elems m+ progInline' m (d:ds) = do+ d'@(Func f _ _) <- declInline (`Map.lookup` m) highlyCalled d+ progInline' (Map.insert f d' m) ds+ + -- |Given a mapping of function names to declarations, inline+ -- all non-recursive function applications+ declInline :: (Id -> Maybe Decl) -> Set.Set Id -> Decl -> Fresh Decl+ declInline progmap base (Func f a r) = liftM (Func f a) (expInline (f `Set.insert` base) r)+ where+ -- |Given a trace of functions visited, inline all non-+ -- recursive function applications.+ expInline :: Set.Set Id -> Exp -> Fresh Exp+ expInline fs (Fun f) | f `Set.notMember` fs && isJust inlined+ = fromJust inlined >>= descendM (expInline $ f `Set.insert` fs)+ where+ inlined = progmap f >>= notRecursive >>= maybeInline []+ expInline fs (App (Fun f) xs) | f `Set.notMember` fs && isJust inlined+ = fromJust inlined >>= descendM (expInline $ f `Set.insert` fs)+ where+ inlined = progmap f >>= notRecursive >>= maybeInline xs+ expInline fs e = descendM (expInline fs) e+ + -- |Only return functions that are not immediately recursive.+ notRecursive :: Decl -> Maybe Decl+ notRecursive d@(Func f _ r) | f `elem` calls r = Nothing+ | otherwise = Just d
+ Optimus/Pretty.hs view
@@ -0,0 +1,49 @@+module Optimus.Pretty where+ import Flite.Syntax+ import Text.PrettyPrint.Leijen+ + putProg :: Prog -> IO ()+ putProg = putDoc . braces . enclose line line . indent 2 . vsep . punctuate (semi <> line) . map pretty+ + prettyProg :: Prog -> String+ prettyProg = show . braces . enclose line line . indent 2 . vsep . punctuate (semi <> line) . map pretty+ + instance Pretty Decl where+ pretty (Func f a r) = nest 2 $ text f+ <+> hsep (map prettyArg a)+ </> char '='+ <+> pretty r+ + instance Pretty Exp where+ pretty (App x ys) = hsep (pretty x : map prettyArg ys)+ pretty (Case x as) = nest 2 (text "case" </> prettyArg x)+ </> nest 2 (text "of" </> prettyBlock prettyAlt as)+ pretty (Let bs y) = nest 2 (text "let" </> prettyBlock prettyBind bs)+ </> nest 2 (text "in" </> prettyArg y)+ pretty (Var v) = text v+ pretty (Fun f) = text f+ pretty (Con c) = text c+ pretty (Int i) = int i+ pretty (Bottom) = text "_|_"+ + prettyBlock :: (a -> Doc) -> [a] -> Doc+ prettyBlock f = braces . enclose line line . vsep . punctuate semi . map f+ + prettyAlt :: Alt -> Doc+ prettyAlt (p, x) = nest 2 $ pretty p <+> text "->" </> pretty x+ + prettyBind :: Binding -> Doc+ prettyBind (v, x) = text v <+> text "=" <+> pretty x+ + prettyArg :: Exp -> Doc+ prettyArg (App e []) = prettyArg e+ prettyArg e@(App _ _) = parens . pretty $ e+ prettyArg e@(Case _ _) = parens . pretty $ e+ prettyArg e@(Let _ _) = parens . pretty $ e+ prettyArg e = pretty e+ + instance Show Decl where+ show = ('\n':) . show . pretty+ + instance Show Exp where+ show = ('\n':) . show . pretty
+ Optimus/Simplify.hs view
@@ -0,0 +1,134 @@+module Optimus.Simplify where -- (simplify) where+ import Flite.Syntax+ import Flite.Traversals hiding (freshen)+ import Flite.Descend+ import Flite.Fresh+ import Optimus.Util+ import Data.List+ import Data.Maybe+ import Control.Monad+ import Optimus.Trace+ + -- Issues are going to be with additional Apps in places+ + -- |Not traced as too common and simply.+ stripApp :: Exp -> Exp+ stripApp e@(App (Fun _) []) = e+ stripApp (App x []) = stripApp x+ stripApp e = descend stripApp e+ + -- |Not traced as too common and simply.+ appOfApp :: Exp -> Exp+ appOfApp (App (App x xs) ys) = appOfApp $ App x (xs ++ ys)+ appOfApp e = descend appOfApp e+ + caseOfCons :: Exp -> Fresh Exp+ caseOfCons (Case (App (Con c) xs) ps) | isJust y' = fromJust y' >>= (caseOfCons . t_S "caseOfCons")+ where+ y' = listToMaybe [ mapM (const fresh) vs' >>=+ \ws -> return $ Let (zip ws xs) (substMany y $ zipWith (\w v -> (Var w, v)) ws vs')+ | ( App (Con c') vs , y ) <- ps, c == c', length xs == length vs, let vs' = concatMap patVars vs ]+ caseOfCons (Case (Con c) ps) = caseOfCons $ Case (App (Con c) []) ps+ caseOfCons e = descendM caseOfCons e+ + appToCase :: Exp -> Fresh Exp+ appToCase (App e@(Case _ _) zs) = do+ Case x ps <- freshenMany e (concatMap freeVars zs)+ appToCase $ t_S "appToCase" $ Case x [ (p, App y zs) | (p, y) <- ps ]+ appToCase e = descendM appToCase e+ + appToLet :: Exp -> Fresh Exp+ appToLet (App e@(Let _ _) zs) = do+ Let bs y <- freshenMany e (concatMap freeVars zs)+ appToLet $ t_S "appToLet" $ Let bs (App y zs)+ appToLet e = descendM appToLet e+ + caseOfLet :: Exp -> Fresh Exp+ caseOfLet (Case e@(Let _ _) as) = do+ Let bs y <- freshenMany e $ concat [ freeVarsExcept (patVars p) x | (p, x) <- as ]+ caseOfLet $ t_S "caseOfLet" $ Let bs (Case y as)+ caseOfLet e = descendM caseOfLet e+ + caseOfCase :: Exp -> Fresh Exp+ caseOfCase (Case e@(Case _ _) as') = do+ Case x as <- freshenMany e $ concat [ freeVarsExcept (freeVars p) y | (p, y) <- as' ]+ caseOfCase $ t_S "caseOfCase" $ Case x [ (p, Case y as') | (p, y) <- as]+ caseOfCase e = descendM caseOfCase e+ + substituteVar :: Exp -> Fresh Exp+ substituteVar e@(Case (Var v) as_) | v `elem` (freeVars $ Case Bottom as_) = do+ Case _ as <- freshen e v+ as' <- sequence [ liftM ((,) p) $ freshenMD y $ patVars p | (p, y) <- as ]+ --freshenMany e $ v : concat [ patVars p | Case _ as' <- extract universe e, (p, _) <- as' ]+ substituteVar $ t_S "substituteVar" $ Case (Var v) [ (p, subst p v y) | (p, y) <- as' ]+ substituteVar e = descendM substituteVar e+ + letInCase :: Exp -> Fresh Exp+ letInCase (Let bs e@(Case y _)) | (not . null) safe_bs = do+ Case _ as <- freshenMany e (v:freeVars x)+ letInCase $ t_S "letInCase" $ Let rem_bs (Case y [ (p, Let [(v, x)] z) | (p, z) <- as ])+ where+ yFree = freeVars y+ bsFrees = [ (v, freeVars x) | (v, x) <- bs ]+ safe_bs = [ (v, x) | (v, x) <- bs, v `notElem` (yFree ++ concat [ ws | (w, ws) <- bsFrees, v /= w ]) ]+ (v, x) = head safe_bs+ rem_bs = [ (v', x') | (v', x') <- bs, v /= v' ]+ letInCase e = descendM letInCase e+ + inlineLet :: Exp -> Fresh Exp+ inlineLet (Let bs y) | (not . null) safe_bs = do+ y' <- freshenMD y $ freeVars x_i+ inlineLet $ t_S "inlineLet" $ subst x_i v_i (Let rem_bs y')+ where+ (_, xs) = unzip bs+ (v_i, x_i) = head safe_bs+ rem_bs = [ b | b@(v_j, _) <- bs, v_j /= v_i ]+ safe_bs = [ b |+ b@(v_j, x_j) <- bs+ , v_j `notElem` freeVars x_j+ , (sum . map (varRefs v_j)) (y:xs) <= 1 || isOk x_j ]+ isOk (Var _) = True+ isOk (Con _) = True+ isOk (Int _) = True+ isOk (Fun _) = True+ isOk (App e []) = isOk e+ isOk _ = False+ inlineLet e = descendM inlineLet e+ + splitLet :: Exp -> Fresh Exp+ splitLet (Let bs y) | (not . null) safe_bs+ = do+ new_bs <- mapM build args+ splitLet $ t_S "splitLet" $ subst (App (Con c) (map Var (fst . unzip $ new_bs))) v_i (Let (rem_bs ++ new_bs) y)+ where+ (v_i, App (Con c) args) = head safe_bs+ rem_bs = [ b | b@(v_j, _) <- bs, v_j /= v_i ]+ safe_bs = [ b | b@(v_j, x_j@(App (Con _) _)) <- bs, v_j `notElem` freeVars x_j ]+ build x_j = fresh >>= \vF -> return (vF, x_j)+ splitLet e = descendM splitLet e+ + removeLet :: Exp -> Exp+ removeLet (Let [] x) = descend removeLet x+ removeLet e = descend removeLet e+ + letInLet :: Exp -> Fresh Exp+ letInLet (Let bs1 e@(Let _ _)) = do+ Let bs2 x <- freshenMany e (concatMap (freeVars . snd) bs1 ++ map fst bs1)+ letInLet $ t_S "letInLet" $ Let (bs1 ++ bs2) x+ letInLet e = descendM letInLet e+ + simplify :: Exp -> Fresh Exp+ simplify p = do+ p' <- (return . stripApp . appOfApp . removeLet) =<< appToCase =<< appToLet =<< caseOfLet =<< caseOfCons+ =<< caseOfCase =<< substituteVar =<< letInCase+ =<< inlineLet =<< letInLet =<< ( splitLet . appOfApp+ . removeLet . stripApp ) p+ if p == p' then return p else simplify p'+ + simplifyInlines :: Exp -> Fresh Exp+ simplifyInlines e = do+ e' <- (return . removeLet <=< inlineLet <=< return . stripApp) e+ if e == e' then return e else simplifyInlines e'+ + simplifyProg :: Prog -> Fresh Prog+ simplifyProg p = sequence [ return . Func f a =<< (\x -> t_S' (show x) (simplify x)) =<< simplifyInlines (t_S' ("Simplifying " ++ f) r) | Func f a r <- p]
+ Optimus/Strategy.hs view
@@ -0,0 +1,185 @@+module Optimus.Strategy where+ import Control.Monad.Identity+ import Control.Monad.State+ import Data.Char+ import Data.List+ import qualified Data.Map as Map+ import qualified Data.Set as Set+ import Data.Maybe+ import Optimus.Trace+ + import Flite.Identify+ import Flite.Fresh+ --import Flite.Inline+ --import Flite.Pretty+ import Optimus.Pretty+ import Flite.Syntax+ import Flite.Traversals++ import Optimus.Generalise+ import Optimus.Homeo+ import Optimus.Inline+ import Optimus.Simplify+ import Optimus.Uniplate+ import Optimus.Util+ + import Data.Generics.Uniplate+ + --------------------------------------------------+ --------------------------------------------------+ reachable :: Prog -> Id -> Set.Set Id+ reachable p = reachable' (Set.empty)+ where+ reachable' :: Set.Set Id -> Id -> Set.Set Id+ reachable' fs f | f `Set.member` fs = fs+ | otherwise = foldl reachable' (f `Set.insert` fs) (reach f)+ reach f = [ g | d <- lookupFuncs f p, g <- (calls . funcRhs) d ]+ + onlyReachable :: Prog -> Id -> Prog+ onlyReachable p f = [ d | d <- p, funcName d `Set.member` reachable p f ]+ + supercompileFunc :: String -> Prog -> Prog+ supercompileFunc f_ p_ = onlyReachable p'' f_ ++ [ d | d@(Func f _ _) <- p, f /= f_ ]+ where+ p = t_S "Desugar program" $ freshProg (desugar . funcReuse) p_+ p'' = Func f a main : p'+ Func f a r = fromMaybe (error $ "Could not find '" ++ f_ ++ "'") $ Map.lookup (t_sc f_ f_) m+ (main, SCState _ p' m' _) = snd (runFresh (runStateT (tie r) (SCState ((+) 1 $ maximum $ -1 : [ read rest | 'f':rest <- funcs p, all isNumber rest ]) [] m [])) "v" $ (+) 1 $ maximum $ -1 : [ read rest | 'v':rest <- allNames p, (not . null) rest, all isNumber rest ])+ m = t_M $ byFuncName p+ + supercompileMany :: [String] -> Prog -> Prog+ supercompileMany fs p = flip onlyReachable (last fs) $ freshProg (finalSimplification simplifyProg . flip onlyReachable (last fs) <=< finalInlining progInline) (foldl (flip supercompileFunc) p fs)+ + supercompile :: Prog -> Prog+ supercompile = flip onlyReachable "main" . freshProg (finalSimplification simplifyProg . flip onlyReachable "main" <=< finalInlining progInline) . supercompileFunc "main"+ + type SCStateT m a = StateT SCState m a++ data SCState = SCState { scCount :: Int,+ scResidual :: Prog,+ scFuncMap :: (Map.Map Id Decl),+ scRho :: [Decl] }+ + scIncCount :: StateT SCState Fresh ()+ scIncCount = do+ SCState count prog m rho <- get+ put (SCState (count+1) prog m rho)+ + scAddDecl :: Decl -> StateT SCState Fresh Exp+ scAddDecl d@(Func fId fArgs _) = do+ SCState count prog m rho <- get+ put (SCState count (d:prog) (t_M $ Map.insert fId d m) rho)+ return $ App (Fun fId) fArgs+ + scAddRho :: Decl -> StateT SCState Fresh ()+ scAddRho d = do+ SCState count prog m rho <- get+ put (SCState count prog m (d:rho))+ + buildSig :: Id -> Exp -> Exp -> Decl+ buildSig i q r = Func i (map Var $ sort $ freeVars q) (autoAlphaExp r)+ + tie :: Exp -> SCStateT Fresh Exp+ tie x | simpleExpr x = return x+ | otherwise = do+ SCState count prog m rho <- get+ let fHead = buildSig ('f' : show count) x in+ case findExp x $ t_Rho (show rho) rho of+ Just e -> (t_T $ "Seen!" ++ show e) return e+ Nothing -> (t_T $ "Making f" ++ show count) (do+ scIncCount+ fRhs <- (drive fHead . isTerm rho) x+ scAddDecl (fHead fRhs))+ + tieOrGen :: (Exp -> Decl) -> Exp -> Exp -> SCStateT Fresh Exp+ tieOrGen s x y = do+ SCState _ _ _ rho <- get+ case findExp x rho of+ Just e -> (t_T $ "Seen!" ++ show e) return e+ Nothing -> scAddRho (s x) >> lift (generalise1 x y) >>= \x' -> scAddRho (s x') >> let (cs, gen) = uniplate $ t_D ("generalised to " ++ show x') x' in liftM gen (mapM tie cs)+ + drive :: (Exp -> Decl) -> Unfold -> SCStateT Fresh Exp+ drive s (NonTerm x) = t_D (show x) $ scAddRho (s x) >> get >>= \(SCState _ _ m rho) -> lift (unfoldT (flip Map.lookup m) rho x) >>= drive s+ drive s (SimplTerm x) = t_D ("Simple Termination" ++ show x) $ scAddRho (s x) >> let (cs, gen) = uniplate x in liftM gen (mapM tie cs)+ drive s (HomeoTerm x y) = t_D ("Homeomorphic Embedding of" ++ show x ++ "\nto" ++ show y) $ tieOrGen s x y+ drive s (NoUnfold x) = t_D ("No Unfolds Remaining" ++ show x) $ scAddRho (s x) >> return x+ + data Unfold = NonTerm { ntExp :: Exp }+ | SimplTerm { stExp :: Exp }+ | HomeoTerm { htExp :: Exp, htHomeo :: Exp }+ | NoUnfold { nuExp :: Exp }+ deriving Show+ + isTerm :: [Decl] -> Exp -> Unfold+ isTerm rho x | simpleTerm x = SimplTerm x+ | isHomeoTerm = HomeoTerm x y+ | otherwise = NonTerm x+ where+ ht = homeoTerm rho x+ isHomeoTerm = isJust ht+ Just y = ht+ + filterTerms :: Exp -> [Fresh Unfold] -> Fresh Unfold+ filterTerms def ufs = filterTerms' ufs+ where+ filterTerms' :: [Fresh Unfold] -> Fresh Unfold+ filterTerms' [] | null ufs = return (NoUnfold def)+ | otherwise = t_U ("No non-terminating expression found.") $ head ufs+ filterTerms' (x:xs) = x >>= \x' -> case x' of+ NonTerm _ -> t_U ("Found a non-terminating unfold.") (return x')+ otherwise -> t_U ("Skipped as " ++ case x' of { SimplTerm _ -> "Simple Termination."; HomeoTerm _ _ -> "Simple Termination."; otherwise -> "Unknown Problem." }) filterTerms' xs+ + -- Move simplify down here \/. And freshen those variables!+ unfoldT :: (Id -> Maybe Decl) -> [Decl] -> Exp -> Fresh Unfold+ unfoldT m rho e = t_U "\nChecking unfold of: " $ filterTerms e unfolds+ where -- Should this be holes? Should freshen anything that redefines a global variable or just block it.+ unfolds :: [Fresh Unfold]+ unfolds = [ c' >>= simplify . h >>= return . isTerm rho | (c, h) <- ((e, id) : holes e), c' <- maybeToList (unfold c) ]+ unfold :: Exp -> Maybe (Fresh Exp)+ unfold e@(App (Fun f) xs) = m f >>= maybeInline xs+ unfold (Fun f) = unfold $ App (Fun f) []+ unfold _ = Nothing+ + findExp :: Exp -> [Decl] -> Maybe Exp -- Slowdown here. Do properly+ findExp e ds = listToMaybe $ mapMaybe (matchExp (autoAlphaExp e)) ds+ + -- Must be linear and alphaed+ matchExp :: Exp -> Decl -> Maybe Exp+ matchExp e (Func f a r) | success = sequence [ join (Map.lookup v mapping) | Var v <- a ] >>= Just . App (Fun f)+ | otherwise = Nothing+ where+ (success, mapping) = runState (matchExp' e (autoAlphaExp r)) (Map.fromList $ [ (v, Nothing) | Var v <- a ])+ matchExp' :: Exp -> Exp -> State (Map.Map Id (Maybe Exp)) Bool+ matchExp' e (Var v) = get >>= \a -> case v `Map.lookup` a of+ Nothing -> case e of { Var v' -> return (v == v'); otherwise -> return False }+ Just (Nothing) -> put (Map.insert v (Just e) a) >> return True+ Just (Just e') -> return $ e == e'+ -- maybe deal with variable bindings+ matchExp' e e' = liftM ((&&) (e =~ e') . and) $ zipWithM matchExp' (children e) (children e')+ + --------------------------------------------------+ --------------------------------------------------++ simpleExpr :: Exp -> Bool+ simpleExpr (Var _) = True+ simpleExpr (Con _) = True+ simpleExpr (Int _) = True+ -- simpleExpr (Fun _) = True+ simpleExpr (Fun f) = f `elem` primitives+ simpleExpr _ = False++ simpleTerm :: Exp -> Bool+ simpleTerm (Var _) = True+ simpleTerm (App (Con _) _) = True+ simpleTerm (App (Fun f) _) = f `elem` primitives+ simpleTerm (Case (Var _) _) = True+ simpleTerm (Case (App (Con _) _) _) = True+ simpleTerm (Case (App (Fun f) _) _) = f `elem` primitives+ simpleTerm _ = False+ + homeoTerm :: [Decl] -> Exp -> Maybe Exp+ homeoTerm es y = listToMaybe (concatMap homeo es)+ where+ homeo :: Decl -> [Exp]+ homeo (Func _ _ rhs) = if rhs <|| y then [rhs] else []+
+ Optimus/Trace.hs view
@@ -0,0 +1,36 @@+module Optimus.Trace where+ import qualified Debug.Trace as Trace+ import Optimus.Pretty+ import Flite.Syntax+ import Flite.Fresh+ import Control.Monad+ + trace :: String -> String -> a -> a+ trace prefix suffix x = Trace.trace (prefix ++ suffix) x+ + dummy :: String -> a -> a+ dummy _ = id+ + finalInlining f p = trace "\nFinal inlining of:\n" (prettyProg p) (f p)+ finalSimplification f p = trace "\nFinal simplification of:\n" (prettyProg p) (f p)+ + t_sc = trace "\nSupercompile: "+ + --t_S = trace "Simplification: "+ t_S' = trace "Simplification: "+ t_S = dummy+ + t_D = trace "\n+ Drive: "+ + t_T = trace "\n}{ Tie: "+ + t_U = trace ""+ + -- t_M m = trace "\nMap:\n" (show m) m+ t_M = id+ + -- t_Rho = trace "\nrho:\n"+ t_Rho = dummy+ + -- t_G = trace "\n MSG: \n"+ t_G = dummy
+ Optimus/Uniplate.hs view
@@ -0,0 +1,30 @@+-- |This module provides a Uniplate instance for the f-lite syntax. It also+-- re-exports the Uniplate library.+module Optimus.Uniplate(+ module Data.Generics.Uniplate,+ extract+ ) where+ + import Flite.Syntax+ import Flite.Writer++ import Data.Generics.Uniplate+ + instance Uniplate Exp where+ uniplate (Case x as) = (x:ys, \(x:ys) -> Case x (zip ps ys))+ where+ (ps, ys) = unzip as+ uniplate (Let bs x) = (x:ys, \(x:ys) -> Let (zip vs ys) x)+ where+ (vs, ys) = unzip bs+ uniplate (App x ys) = (x:ys, \(x:ys) -> App x ys)+ uniplate (Var i) = ([], \[] -> Var i)+ uniplate (Con i) = ([], \[] -> Con i)+ uniplate (Fun i) = ([], \[] -> Fun i)+ uniplate (Int i) = ([], \[] -> Int i)+ uniplate (Bottom) = ([], \[] -> Bottom)+ + -- |Given a 'Uniplate' traversable tree and a function that extracts information+ -- from tree nodes, extract all the information in the tree.+ extract :: Uniplate a => (a -> [b]) -> a -> [b]+ extract f = fst . runWriter . descendM (\a -> writeMany (f a) >> return a)
+ Optimus/Util.hs view
@@ -0,0 +1,104 @@+module Optimus.Util where+ import Flite.Fresh+ import Flite.Syntax+ import Flite.Traversals hiding (freshen)+ import Optimus.Uniplate+ import Flite.Let+ import Flite.Matching+ + import Data.Char+ import Data.List+ import Control.Monad+ import Data.Generics.Uniplate+ import qualified Data.Map as Map+ import Data.Maybe+ + primitives = ["(+)", "(-)", "(==)", "(/=)", "(<=)", "emit", "emitInt"]+ + freshen :: Exp -> Id -> Fresh Exp+ freshen e v = fresh >>= return . fix e . Var+ where+ fix (Case x as) w = Case x [ if (Var v `elem` universe p) then (subst w v p, subst w v y) else (p, y) | (p, y) <- as]+ fix (Let bs x) w@(Var w_) | v `elem` map fst bs = Let [ (if v == v_ then w_ else v_, subst w v y) | (v_, y) <- bs ] (subst w v x)+ | otherwise = Let bs x+ fix _ _ = error "Not designed to freshen this."+ + freshenMany :: Exp -> [Id] -> Fresh Exp+ freshenMany = foldM freshen+ + freshenDescend :: Exp -> Id -> Fresh Exp+ freshenDescend e@(Case _ _) v = freshen e v >>= descendM (flip freshenDescend v)+ freshenDescend e@(Let _ _) v = freshen e v >>= descendM (flip freshenDescend v)+ freshenDescend e v = descendM (flip freshenDescend v) e+ + freshenMD :: Exp -> [Id] -> Fresh Exp+ freshenMD = foldM freshenDescend+ + autoFresh :: Fresh a -> a+ autoFresh x = snd $ runFresh x "v" 0+ + freshProg :: (Prog -> Fresh Prog) -> Prog -> Prog+ freshProg f x = snd $ runFresh (f x) "v" $ (+) 1 $ maximum $ -1 : [ read rest | 'v':rest <- allNames x, (not . null) rest, all isDigit rest ]+ + allNames :: Prog -> [Id]+ allNames p = nub $ concat [ [f] ++ concatMap patVars a ++ allnames r | Func f a r <- p ]+ where+ allnames (Var v) = [v]+ allnames (Fun f) = [f]+ allnames e@(Case _ as) = concatMap (patVars . fst) as ++ extract allnames e+ allnames e@(Let bs _) = map fst bs ++ extract allnames e+ allnames e = extract allnames e++ -- allNames p = concat [ [f] ++ (concatMap patVars a) ++ [ v | Var v <- universe r ] ++ (concat [ patVars p | Case _ as <- universe r, (p, _) <- as ]) | Func f a r <- p ]+ + alphaExp :: Exp -> Fresh Exp+ alphaExp e = (alpha . Func "NOVAR" []) e >>= return . funcRhs+ + autoAlphaExp :: Exp -> Exp+ autoAlphaExp e = snd $ runFresh (alphaExp e) "v" $ (+) 1 $ maximum $ -1 : [ read rest | 'v':rest <- freeVars e, (not . null) rest, all isDigit rest ]+ + insertMany :: Ord k => Map.Map k a -> [(k, a)] -> Map.Map k a+ insertMany = foldr (uncurry Map.insert)+ + alpha :: Decl -> Fresh Decl+ alpha (Func i a r) = alphaM+ where+ alphaM = do+ l <- assocFresh ([ v | Var v <- concatMap universe a ] ++ [i]) >>= return . Map.fromList+ r' <- freshSubst l r+ a' <- mapM (freshSubst l) a+ return $ Func (fromJust $ Map.lookup i l) a' r'+ assocFresh :: [Id] -> Fresh [(Id, Id)]+ assocFresh = mapM (\v -> fresh >>= \v' -> return (v, v'))+ freshSubst :: Map.Map Id Id -> Exp -> Fresh Exp+ freshSubst l (Var v) | isJust v' = return . Var . fromJust $ v'+ | otherwise = return . Var $ v+ where+ v' = Map.lookup v l+ freshSubst l (Let bs x) = let (vs, ys) = unzip bs in do+ m <- assocFresh vs+ descendM (freshSubst $ insertMany l m) (Let (zip (map snd m) ys) x)+ freshSubst l (Case x as) = do+ x' <- freshSubst l x+ as' <- sequence [ assocFresh [v | Var v <- universe p] >>= \m -> let fS' = freshSubst $ insertMany l m in liftM2 (,) (fS' p) (fS' y) | (p, y) <- as]+ return (Case x' as')+ freshSubst l e = descendM (freshSubst l) e+ + desugar :: Prog -> Fresh Prog+ desugar p = return p >>= desugarCase >>= desugarEqn >>= inlineLinearLet >>= inlineSimpleLet+ + funcReuse :: Prog -> Prog+ funcReuse p = if null reused then p else error $ "Reused the name of a function as a local variable. " ++ show reused+ where+ reused = funcs p `intersect` (nub . concatMap (\d -> (concatMap patVars . funcArgs) d ++ (boundVars . funcRhs) d) $ p)+ + byFuncName :: Prog -> Map.Map Id Decl+ byFuncName [] = Map.empty+ byFuncName (d@(Func f _ _):ds) = Map.insert f d (byFuncName ds)+ + boundVars :: Exp -> [Id]+ boundVars = nub . bv+ where+ bv e@(Case _ as) = concatMap (patVars . fst) as ++ extract bv e+ bv e@(Let bs _) = map fst bs ++ extract bv e+ bv e = extract bv e
optimusprime.cabal view
@@ -1,5 +1,5 @@ Name: optimusprime-version: 0.0.1.20091113+version: 0.0.1.20091114 Synopsis: A supercompiler for f-lite License: BSD3 License-file: LICENSE@@ -14,8 +14,12 @@ Executable optimusprime Main-is: OptimusPrime.hs+ Other-modules: Optimus.CallGraph, Optimus.Generalise, Optimus.Homeo,+ Optimus.Inline, Optimus.Pretty, Optimus.Simplify,+ Optimus.Strategy, Optimus.Trace, Optimus.Uniplate,+ Optimus.Util Build-Depends: base >= 3 && < 5, containers >= 0 && < 1,- parsec >= 2.1.0.1 && < 3, flite >= 0 && < 1,+ parsec >= 2.1.0.1 && < 3, flite >= 0.1.1 && < 1, uniplate >= 1.2.0.3 && < 2, wl-pprint >= 1 && < 2, mtl >= 1.1.0.2 && < 2, fgl >= 5.4.2.2 && < 6, graphviz >= 2999.6.0.0, haskell98 >= 1 && < 2,