-- | This module optimizes code in the simplified-JavaScript intermediate representation. -- -- The following optimizations are supported: -- -- * Collapsing nested blocks -- -- * Tail call elimination -- -- * Inlining of (>>=) and ret for the Eff monad -- -- * Removal of unnecessary thunks -- -- * Eta conversion -- -- * Inlining variables -- -- * Inline Prelude.($), Prelude.(#), Prelude.(++), Prelude.(!!) -- -- * Inlining primitive JavaScript operators module Language.PureScript.CoreImp.Optimizer (optimize) where import Prelude import Data.Text (Text) import Control.Monad.Supply.Class (MonadSupply) import Language.PureScript.CoreImp.AST (AST(..), InitializerEffects(..)) import Language.PureScript.CoreImp.Optimizer.Blocks (collapseNestedBlocks, collapseNestedIfs) import Language.PureScript.CoreImp.Optimizer.Common (applyAll, replaceIdents) import Language.PureScript.CoreImp.Optimizer.Inliner (etaConvert, evaluateIifes, inlineCommonOperators, inlineCommonValues, inlineFnComposition, inlineFnIdentity, inlineUnsafeCoerce, inlineUnsafePartial, inlineVariables, unThunk) import Language.PureScript.CoreImp.Optimizer.MagicDo (inlineST, magicDoEff, magicDoEffect, magicDoST) import Language.PureScript.CoreImp.Optimizer.TCO (tco) import Language.PureScript.CoreImp.Optimizer.Unused (removeCodeAfterReturnStatements, removeUndefinedApp, removeUnusedEffectFreeVars) -- | Apply a series of optimizer passes to simplified JavaScript code optimize :: forall m. MonadSupply m => [Text] -> [[AST]] -> m [[AST]] optimize exps jsDecls = removeUnusedEffectFreeVars exps <$> traverse (traverse go) jsDecls where go :: AST -> m AST go js = do js' <- untilFixedPoint (inlineFnComposition expander . inlineFnIdentity expander . inlineUnsafeCoerce . inlineUnsafePartial . tidyUp . applyAll [ inlineCommonValues expander , inlineCommonOperators expander ]) js untilFixedPoint (return . tidyUp) . tco . inlineST =<< untilFixedPoint (return . magicDoST expander) =<< untilFixedPoint (return . magicDoEff expander) =<< untilFixedPoint (return . magicDoEffect expander) js' tidyUp :: AST -> AST tidyUp = applyAll [ collapseNestedBlocks , collapseNestedIfs , removeCodeAfterReturnStatements , removeUndefinedApp , unThunk , etaConvert , evaluateIifes , inlineVariables ] expander = buildExpander (concat jsDecls) untilFixedPoint :: (Monad m, Eq a) => (a -> m a) -> a -> m a untilFixedPoint f = go where go a = do a' <- f a if a' == a then return a' else go a' -- | -- Take all top-level ASTs and return a function for expanding top-level -- variables during the various inlining steps in `optimize`. -- -- Everything that gets inlined as an optimization is of a form that would -- have been lifted to a top-level binding during CSE, so for purposes of -- inlining we can save some time by only expanding variables bound at that -- level and not worrying about any inner scopes. -- buildExpander :: [AST] -> AST -> AST buildExpander = replaceIdents . foldr go [] where go = \case VariableIntroduction _ name (Just (NoEffects, e)) -> ((name, e) :) _ -> id