packages feed

optimusprime-0.0.1.20091114: Optimus/Inline.hs

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