packages feed

optimusprime-0.0.1.20091114: Optimus/Strategy.hs

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 []