{-# LANGUAGE GADTs #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} -- -- Copyright (c) 2009-2011, ERICSSON AB -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions are met: -- -- * Redistributions of source code must retain the above copyright notice, -- this list of conditions and the following disclaimer. -- * Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- * Neither the name of the ERICSSON AB nor the names of its contributors -- may be used to endorse or promote products derived from this software -- without specific prior written permission. -- -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- -- | Defines different interpretations of Feldspar programs module Feldspar.Core.Interpretation ( module Language.Syntactic.Constructs.Decoration , module Feldspar.Core.Interpretation.Typed , targetSpecialization , Sharable (..) , SizeProp (..) , sizePropDefault , resultType , SourceInfo , Info (..) , mkInfo , mkInfoTy , infoRange , LatticeSize1 (..) , viewLiteral , literal , literalDecor , constFold , SomeInfo (..) , SomeType (..) , Env (..) , FeldOpts (..) , Target (..) , inTarget , defaultFeldOpts , localVar , localSource , Opt , Optimize (..) , OptimizeSuper , constructFeat , optimizeM , optimize , constructFeatUnOptDefaultTyp , constructFeatUnOptDefault , optimizeFeatDefault , prjF , c' , Monotonic (..) , viewMonotonicInc , viewMonotonicDec ) where import Control.Monad.Reader import Data.Map as Map import Data.Typeable (Typeable) import Language.Syntactic import Language.Syntactic.Constructs.Decoration import Language.Syntactic.Constructs.Literal import Language.Syntactic.Constructs.Binding import Feldspar.Lattice import Feldspar.Core.Types import Feldspar.Core.Interpretation.Typed -------------------------------------------------------------------------------- -- * Target specialization -------------------------------------------------------------------------------- -- | Specialize the program for a target platform with the given native bit -- width targetSpecialization :: BitWidth n -> ASTF dom a -> ASTF dom a -- TODO targetSpecialization :: BitWidth n -> ASTF dom a -> ASTF dom (TargetType n a) targetSpecialization _ = id -------------------------------------------------------------------------------- -- * Code motion -------------------------------------------------------------------------------- -- | Indication whether a symbol is sharable or not class Sharable dom where sharable :: dom a -> Bool sharable _ = True hoistOver :: dom a -> Bool hoistOver _ = True instance (Sharable sub1, Sharable sub2) => Sharable (sub1 :+: sub2) where sharable (InjL a) = sharable a sharable (InjR a) = sharable a hoistOver (InjL a) = hoistOver a hoistOver (InjR a) = hoistOver a instance Sharable sym => Sharable (sym :|| pred) where sharable (C' s) = sharable s hoistOver (C' s) = hoistOver s instance Sharable sym => Sharable (SubConstr2 c sym p1 p2) where sharable (SubConstr2 s) = sharable s hoistOver (SubConstr2 s) = hoistOver s instance Sharable dom => Sharable (Decor Info dom) where sharable = sharable . decorExpr hoistOver = hoistOver . decorExpr instance Sharable Empty -------------------------------------------------------------------------------- -- * Size propagation -------------------------------------------------------------------------------- -- | Forwards size propagation class SizeProp feature where -- | Size propagation for a symbol given a list of argument sizes sizeProp :: feature a -> Args (WrapFull Info) a -> Size (DenResult a) -- | Convenient default implementation of 'sizeProp' sizePropDefault :: (Type (DenResult a)) => feature a -> Args (WrapFull Info) a -> Size (DenResult a) sizePropDefault _ _ = universal -------------------------------------------------------------------------------- -- * Optimization and type/size inference -------------------------------------------------------------------------------- -- | Compute a type representation of a symbol's result type resultType :: Type (DenResult a) => c a -> TypeRep (DenResult a) resultType _ = typeRep data SomeType where SomeType :: TypeRep a -> SomeType type VarInfo = Map VarId SomeType -- | Information about the source code of an expression type SourceInfo = String -- | Type and size information of a Feldspar program data Info a where Info :: (Show (Size a), Lattice (Size a)) => { infoType :: TypeRep a , infoSize :: Size a , infoVars :: VarInfo , infoSource :: SourceInfo } -> Info a instance Show (Info a) where show i@(Info {}) = show (infoType i) ++ szStr ++ srcStr where szStr = case show (infoSize i) of "AnySize" -> "" str -> " | " ++ str srcStr = case infoSource i of "" -> "" src -> " | " ++ src mkInfo :: Type a => Size a -> Info a mkInfo sz = Info typeRep sz Map.empty "" mkInfoTy :: (Show (Size a), Lattice (Size a)) => TypeRep a -> Info a mkInfoTy t = Info t universal Map.empty "" infoRange :: Type a => Info a -> RangeSet a infoRange = sizeToRange . infoSize -- | This class is used to allow constructs to be abstract in the monad. Its -- purpose is similar to that of 'MonadType'. class LatticeSize1 m where mergeSize :: Lattice (Size a) => Info (m a) -> Size (m a) -> Size (m a) -> Size (m a) -- TODO Is this class needed? See comment to `MonadType`. instance LatticeSize1 Mut where mergeSize _ = (\/) -- | 'Info' with hidden result type data SomeInfo where SomeInfo :: Typeable a => Info a -> SomeInfo data Env = Env { varEnv :: [(VarId, SomeInfo)] , sourceEnv :: SourceInfo } -- | Possible compilation targets. data Target = RegionInf | SICS deriving Eq -- | A record with options for explicit passing in rewrite rules. data FeldOpts = FeldOpts { targets :: [Target] } -- | Default options. defaultFeldOpts :: FeldOpts defaultFeldOpts = FeldOpts { targets = [] } -- | Decide whether a Target is enabled in FeldOpts. inTarget :: Target -> FeldOpts -> Bool inTarget t opts = t `elem` (targets opts) -- | Initial environment initEnv :: Env initEnv = Env [] "" -- | Insert a variable into the environment localVar :: Typeable b => VarId -> Info b -> Opt a -> Opt a localVar v info = local $ \env -> env {varEnv = (v, SomeInfo info):varEnv env} -- | Change the 'SourceInfo' environment localSource :: SourceInfo -> Opt a -> Opt a localSource src = local $ \env -> env {sourceEnv = src} -- | It the expression is a literal, its value is returned, otherwise 'Nothing' viewLiteral :: forall info dom a. ((Literal :|| Type) :<: dom) => ASTF (Decor info (dom :|| Typeable)) a -> Maybe a viewLiteral (prjF -> Just (C' (Literal a))) = Just a viewLiteral _ = Nothing prjF :: Project (sub :|| Type) sup => sup sig -> Maybe ((sub :|| Type) sig) prjF = prj -- | Construct a 'Literal' literal :: (Type a, (Literal :|| Type) :<: dom) => a -> ASTF (dom :|| Typeable) a literal a = Sym $ C' $ inj $ c' $ Literal a -- | Construct a 'Literal' decorated with 'Info' literalDecorSrc :: (Type a, (Literal :|| Type) :<: dom) => SourceInfo -> a -> ASTF (Decor Info (dom :|| Typeable)) a literalDecorSrc src a = Sym $ Decor ((mkInfo (sizeOf a)) {infoSource = src}) (C' $ inj $ c' $ Literal a) c' :: (Type (DenResult sig)) => feature sig -> (feature :|| Type) sig c' = C' -- | Construct a 'Literal' decorated with 'Info' literalDecor :: (Type a, (Literal :|| Type) :<: dom) => a -> ASTF (Decor Info (dom :|| Typeable)) a literalDecor = literalDecorSrc "" -- Note: This function could get the 'SourceInfo' from the environment and -- insert it in the 'infoSource' field. But then it needs to be monadic which -- makes optimizations uglier. -- | Replaces an expression with a literal if the type permits, otherwise -- returns the expression unchanged. constFold :: (Typed dom, (Literal :|| Type) :<: dom) => SourceInfo -> ASTF (Decor Info (dom :|| Typeable)) a -> a -> ASTF (Decor Info (dom :|| Typeable)) a constFold src expr a | Just Dict <- typeDict expr = literalDecorSrc src a constFold _ expr _ = expr -- | Environment for optimization type Opt = Reader Env -- | Basic optimization of a feature -- -- This optimization is similar to 'Synt.Optimize', but it also performs size -- inference. Size inference has to be done simultaneously with other -- optimizations in order to avoid iterating the phases. (Size information may -- help optimization and optimization may help size inference.) class Optimize feature dom where -- | Top-down and bottom-up optimization of a feature optimizeFeat :: ( Typeable (DenResult a) , OptimizeSuper dom ) => FeldOpts -> feature a -> Args (AST (dom :|| Typeable)) a -> Opt (ASTF (Decor Info (dom :|| Typeable)) (DenResult a)) optimizeFeat = optimizeFeatDefault -- | Optimized construction of an expression from a symbol and its optimized -- arguments -- -- Note: This function should normally not be called directly. Instead, use -- 'constructFeat' which has more accurate propagation of 'Info'. constructFeatOpt :: ( Typeable (DenResult a)) => FeldOpts -> feature a -> Args (AST (Decor Info (dom :|| Typeable))) a -> Opt (ASTF (Decor Info (dom :|| Typeable)) (DenResult a)) constructFeatOpt = constructFeatUnOpt -- | Unoptimized construction of an expression from a symbol and its -- optimized arguments constructFeatUnOpt :: ( Typeable (DenResult a)) => FeldOpts -> feature a -> Args (AST (Decor Info (dom :|| Typeable))) a -> Opt (ASTF (Decor Info (dom :|| Typeable)) (DenResult a)) instance Optimize Empty dom where constructFeatUnOpt = error "Not implemented: constructFeatUnOpt for Empty" -- These classes used to be super-classes of `Optimize`, but after switching to -- GHC 7.4, that lead to looping dictionaries (at run time). The problem arises -- when you make instances like -- -- instance Optimize dom dom => Optimize MyConstruct dom -- -- Since the second parameter does not change, this seems to create a loop -- whenever you want to access super-class methods through a -- `Optimize MyConstruct dom` constraint. -- -- This may or may not be related to the following (unconfirmed) bug: -- -- http://hackage.haskell.org/trac/ghc/ticket/5913 -- -- To revert the class hierarchy: -- -- * Make `OptimizeSuper` (expanded) a super-class of `Optimize` -- * Make `WitnessCons feature` a super-class of `Optimize` -- * Replace the context of `optimizeFeat` with `Optimize dom dom` -- * Replace all references to `OptimizeSuper dom` with `Optimize dom dom` -- * Remove `OptimizeSuper` class ( AlphaEq dom dom (dom :|| Typeable) [(VarId, VarId)] , AlphaEq dom dom (Decor Info (dom :|| Typeable)) [(VarId, VarId)] , EvalBind dom , (Literal :|| Type) :<: dom , Typed dom , Render dom -- For debug , Constrained dom , Optimize dom dom ) => OptimizeSuper dom instance ( AlphaEq dom dom (dom :|| Typeable) [(VarId, VarId)] , AlphaEq dom dom (Decor Info (dom :|| Typeable)) [(VarId, VarId)] , EvalBind dom , (Literal :|| Type) :<: dom , Typed dom , Render dom , Constrained dom , Optimize dom dom ) => OptimizeSuper dom -- | Optimized construction of an expression from a symbol and its optimized -- arguments constructFeat :: ( Typeable (DenResult a) , Optimize feature dom) => FeldOpts -> feature a -> Args (AST (Decor Info (dom :|| Typeable))) a -> Opt (ASTF (Decor Info (dom :|| Typeable)) (DenResult a)) constructFeat opts a args = do aUnOpt <- constructFeatUnOpt opts a args aOpt <- constructFeatOpt opts a args return $ updateDecor (\info -> info {infoSize = infoSize (getInfo aUnOpt)}) aOpt -- This function uses `constructFeatOpt` for optimization and -- `constructFeatUnOpt` for size propagation. This is because -- `constructFeatOpt` may produce less accurate size information than -- `constructFeatUnOpt`. instance ( Optimize sub1 dom , Optimize sub2 dom ) => Optimize (sub1 :+: sub2) dom where optimizeFeat opts (InjL a) = optimizeFeat opts a optimizeFeat opts (InjR a) = optimizeFeat opts a constructFeatOpt opts (InjL a) = constructFeatOpt opts a constructFeatOpt opts (InjR a) = constructFeatOpt opts a constructFeatUnOpt opts (InjL a) = constructFeatUnOpt opts a constructFeatUnOpt opts (InjR a) = constructFeatUnOpt opts a -- | Optimization of an expression -- -- In addition to running 'optimizeFeat', this function performs constant -- folding on all closed expressions, provided that the type permits making a -- literal. optimizeM :: (OptimizeSuper dom) => FeldOpts -> ASTF (dom :|| Typeable) a -> Opt (ASTF (Decor Info (dom :|| Typeable)) a) optimizeM opts a | Dict <- exprDict a = do aOpt <- matchTrans (\(C' x) -> optimizeFeat opts x) a let vars = infoVars $ getInfo aOpt value = evalBind aOpt src = infoSource $ getInfo aOpt if Map.null vars then return $ constFold src aOpt value else return aOpt -- | Optimization of an expression. This function runs 'optimizeM' and extracts -- the result. optimize :: ( Typeable a , OptimizeSuper dom ) => FeldOpts -> ASTF (dom :|| Typeable) a -> ASTF (Decor Info (dom :|| Typeable)) a optimize opts = flip runReader initEnv . optimizeM opts -- | Convenient default implementation of 'constructFeatUnOpt'. Uses 'sizeProp' -- to propagate size. constructFeatUnOptDefaultTyp :: ( feature :<: dom , SizeProp feature , Typeable (DenResult a) , Show (Size (DenResult a)) , Lattice (Size (DenResult a)) ) => FeldOpts -> TypeRep (DenResult a) -> feature a -> Args (AST (Decor Info (dom :|| Typeable))) a -> Opt (ASTF (Decor Info (dom :|| Typeable)) (DenResult a)) constructFeatUnOptDefaultTyp _ typ feat args = do src <- asks sourceEnv let sz = sizeProp feat $ mapArgs (WrapFull . getInfo) args vars = Map.unions $ listArgs (infoVars . getInfo) args return $ appArgs (Sym $ Decor (Info typ sz vars src) $ C' $ inj feat) args -- | Like 'constructFeatUnOptDefaultTyp' but without an explicit 'TypeRep' constructFeatUnOptDefault :: ( feature :<: dom , SizeProp feature , Type (DenResult a) ) => FeldOpts -> feature a -> Args (AST (Decor Info (dom :|| Typeable))) a -> Opt (ASTF (Decor Info (dom :|| Typeable)) (DenResult a)) constructFeatUnOptDefault _ feat args = do src <- asks sourceEnv let sz = sizeProp feat $ mapArgs (WrapFull . getInfo) args vars = Map.unions $ listArgs (infoVars . getInfo) args return $ appArgs (Sym $ Decor (Info typeRep sz vars src) $ C' $ inj feat) args -- | Convenient default implementation of 'optimizeFeat' optimizeFeatDefault :: ( Optimize feature dom , Typeable (DenResult a) , OptimizeSuper dom ) => FeldOpts -> feature a -> Args (AST (dom :|| Typeable)) a -> Opt (ASTF (Decor Info (dom :|| Typeable)) (DenResult a)) optimizeFeatDefault opts feat args = constructFeat opts feat =<< mapArgsM (optimizeM opts) args -- | The 'Monotonic' class represents (weak) monotonicity class Monotonic feature where -- | Return the arguments for which the symbol is monotonic increasing -- -- prop> forAll a. [a] = monotonicInc (f a) ==> f a >= a -- monotonicInc :: feature a -> Args (AST (Decor Info (dom :|| Typeable))) a -> [ASTF (Decor Info (dom :|| Typeable)) (DenResult a)] monotonicInc _ _ = [] -- | Return the arguments for which the symbol is monotonic decreasing -- -- prop> forAll a. [a] = monotonicDec (f a) ==> f a <= a -- monotonicDec :: feature a -> Args (AST (Decor Info (dom :|| Typeable))) a -> [ASTF (Decor Info (dom :|| Typeable)) (DenResult a)] monotonicDec _ _ = [] instance (Monotonic sub1, Monotonic sub2) => Monotonic (sub1 :+: sub2) where monotonicInc (InjL a) = monotonicInc a monotonicInc (InjR a) = monotonicInc a monotonicDec (InjL a) = monotonicDec a monotonicDec (InjR a) = monotonicDec a instance (Monotonic sym) => Monotonic (sym :|| pred) where monotonicInc (C' s) = monotonicInc s monotonicDec (C' s) = monotonicDec s instance (Monotonic sym) => Monotonic (SubConstr2 c sym p1 p2) where monotonicInc (SubConstr2 s) = monotonicInc s monotonicDec (SubConstr2 s) = monotonicDec s instance (Monotonic dom) => Monotonic (Decor Info dom) where monotonicInc = monotonicInc . decorExpr monotonicDec = monotonicDec . decorExpr instance Monotonic Empty -- | Extract sub-expressions for which the expression is (weak) monotonic -- increasing viewMonotonicInc :: (Monotonic dom) => ASTF (Decor Info (dom :|| Typeable)) a -> [ASTF (Decor Info (dom :|| Typeable)) a] viewMonotonicInc = simpleMatch monotonicInc -- | Extract sub-expressions for which the expression is (weak) monotonic -- decreasing viewMonotonicDec :: (Monotonic dom) => ASTF (Decor Info (dom :|| Typeable)) a -> [ASTF (Decor Info (dom :|| Typeable)) a] viewMonotonicDec = simpleMatch monotonicDec