packages feed

futhark-0.15.4: src/Futhark/Optimise/Simplify.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TupleSections #-}
module Futhark.Optimise.Simplify
  ( simplifyProg
  , simplifySomething
  , simplifyFun
  , simplifyLambda
  , simplifyStms

  , Engine.SimpleOps (..)
  , Engine.SimpleM
  , Engine.SimplifyOp
  , Engine.bindableSimpleOps
  , Engine.noExtraHoistBlockers
  , Engine.SimplifiableLore
  , Engine.HoistBlockers
  , RuleBook
  )
  where

import Data.Bifunctor (second)
import Futhark.Representation.AST
import Futhark.MonadFreshNames
import qualified Futhark.Optimise.Simplify.Engine as Engine
import qualified Futhark.Analysis.SymbolTable as ST
import qualified Futhark.Analysis.UsageTable as UT
import Futhark.Optimise.Simplify.Rule
import Futhark.Optimise.Simplify.Lore
import Futhark.Pass

-- | Simplify the given program.  Even if the output differs from the
-- output, meaningful simplification may not have taken place - the
-- order of bindings may simply have been rearranged.
simplifyProg :: Engine.SimplifiableLore lore =>
                Engine.SimpleOps lore
             -> RuleBook (Engine.Wise lore)
             -> Engine.HoistBlockers lore
             -> Prog lore
             -> PassM (Prog lore)
simplifyProg simpl rules blockers (Prog consts funs) = do
  (consts_vtable, consts') <-
    simplifyConsts (UT.usages $ foldMap (freeIn . funDefBody) funs)
                   (mempty, consts)

  funs' <- parPass (simplifyFun' consts_vtable) funs
  let funs_uses = UT.usages $ foldMap (freeIn . funDefBody) funs'

  (_, consts'') <- simplifyConsts funs_uses (mempty, consts')

  return $ Prog consts'' funs'

  where simplifyFun' consts_vtable =
          simplifySomething
          (Engine.localVtable (consts_vtable<>) . Engine.simplifyFun)
          removeFunDefWisdom
          simpl rules blockers mempty

        simplifyConsts uses =
          simplifySomething (onConsts uses . snd)
          (second (removeStmWisdom<$>))
          simpl rules blockers mempty

        onConsts uses consts' = do
          (_, consts'') <-
            Engine.simplifyStms consts' (pure ((), mempty))
          (consts''', _) <-
            Engine.hoistStms rules (Engine.isFalse False) mempty uses consts''
          return (ST.insertStms consts''' mempty, consts''')

-- | Run a simplification operation to convergence.
simplifySomething :: (MonadFreshNames m, Engine.SimplifiableLore lore) =>
                     (a -> Engine.SimpleM lore b)
                  -> (b -> a)
                  -> Engine.SimpleOps lore
                  -> RuleBook (Wise lore)
                  -> Engine.HoistBlockers lore
                  -> ST.SymbolTable (Wise lore)
                  -> a
                  -> m a
simplifySomething f g simpl rules blockers vtable x = do
  let f' x' = Engine.localVtable (vtable<>) $ f x'
  loopUntilConvergence env simpl f' g x
  where env = Engine.emptyEnv rules blockers

-- | Simplify the given function.  Even if the output differs from the
-- output, meaningful simplification may not have taken place - the
-- order of bindings may simply have been rearranged.  Runs in a loop
-- until convergence.
simplifyFun :: (MonadFreshNames m, Engine.SimplifiableLore lore) =>
                Engine.SimpleOps lore
             -> RuleBook (Engine.Wise lore)
             -> Engine.HoistBlockers lore
             -> ST.SymbolTable (Wise lore)
             -> FunDef lore
             -> m (FunDef lore)
simplifyFun = simplifySomething Engine.simplifyFun removeFunDefWisdom

-- | Simplify just a single 'Lambda'.
simplifyLambda :: (MonadFreshNames m, HasScope lore m,
                   Engine.SimplifiableLore lore) =>
                  Engine.SimpleOps lore
               -> RuleBook (Engine.Wise lore)
               -> Engine.HoistBlockers lore
               -> Lambda lore -> [Maybe VName]
               -> m (Lambda lore)
simplifyLambda simpl rules blockers orig_lam args = do
  vtable <- ST.fromScope . addScopeWisdom <$> askScope
  simplifySomething f removeLambdaWisdom simpl rules blockers vtable orig_lam
  where f lam' = Engine.simplifyLambdaNoHoisting lam' args

-- | Simplify a list of 'Stm's.
simplifyStms :: (MonadFreshNames m, Engine.SimplifiableLore lore) =>
                Engine.SimpleOps lore
             -> RuleBook (Engine.Wise lore)
             -> Engine.HoistBlockers lore
             -> Scope lore
             -> Stms lore
             -> m (ST.SymbolTable (Wise lore), Stms lore)
simplifyStms simpl rules blockers scope =
  simplifySomething f g simpl rules blockers vtable . (mempty,)
  where vtable = ST.fromScope $ addScopeWisdom scope
        f (_, stms) =
          Engine.simplifyStms stms ((,mempty) <$> Engine.askVtable)
        g = second $ fmap removeStmWisdom

loopUntilConvergence :: (MonadFreshNames m, Engine.SimplifiableLore lore) =>
                        Engine.Env lore
                     -> Engine.SimpleOps lore
                     -> (a -> Engine.SimpleM lore b)
                     -> (b -> a)
                     -> a
                     -> m a
loopUntilConvergence env simpl f g x = do
  (x', changed) <- modifyNameSource $ Engine.runSimpleM (f x) simpl env
  if changed then loopUntilConvergence env simpl f g (g x') else return $ g x'