feldspar-language-0.5.0.1: Feldspar/Core/Interpretation.hs
--
-- 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.
--
{-# LANGUAGE UndecidableInstances #-}
-- | Defines different interpretations of Feldspar programs
module Feldspar.Core.Interpretation
( module Language.Syntactic.Constructs.Decoration
, targetSpecialization
, Sharable (..)
, sharableDecor
, SizeProp (..)
, sizePropDefault
, resultType
, SourceInfo
, Info (..)
, mkInfo
, mkInfoTy
, infoRange
, LatticeSize1 (..)
, viewLiteral
, literalDecor
, constFold
, SomeInfo (..)
, Env (..)
, localVar
, localSource
, Opt
, Optimize (..)
, constructFeat
, optimizeM
, optimize
, constructFeatUnOptDefaultTyp
, constructFeatUnOptDefault
, optimizeFeatDefault
) where
import Control.Monad.Reader
import Control.Monad.Writer
import Data.Map as Map
-- import Data.Set as Set
import Data.Typeable (Typeable)
import Language.Syntactic
import Language.Syntactic.Constructs.Decoration
import Language.Syntactic.Constructs.Literal
import Language.Syntactic.Constructs.Binding
import qualified Language.Syntactic.Constructs.Binding.Optimize as Synt -- For Haddock
import Feldspar.Lattice
import Feldspar.Core.Types
--------------------------------------------------------------------------------
-- * 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
--------------------------------------------------------------------------------
class Sharable dom
where
sharable :: dom a -> Bool
sharable _ = True
instance (Sharable sub1, Sharable sub2) => Sharable (sub1 :+: sub2)
where
sharable (InjL a) = sharable a
sharable (InjR a) = sharable a
sharableDecor :: Sharable dom => Decor info dom a -> Bool
sharableDecor = sharable . decorExpr
--------------------------------------------------------------------------------
-- * 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 :: (WitnessSat feature, SatContext feature ~ TypeCtx) =>
feature a -> Args (WrapFull Info) a -> Size (DenResult a)
sizePropDefault a _
| TypeWit <- fromSatWit $ witnessSat a = 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
type Bag a = Map a Integer
-- TODO Currently just used as a set. We should either switch back to
-- `Data.Set` or provide a proper bag interface. For example,
-- `Data.Map.member` doesn't do the right thing on a bag, since it should
-- return false also if the key maps to the value 0.
-- | 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)
=> { infoType :: TypeRep a
, infoSize :: Size a
, infoVars :: Bag VarId
, infoSource :: SourceInfo
}
-> Info a
instance Render Info
where
render i@(Info {}) = show (infoType i) ++ szStr ++ srcStr
where
szStr = case show (infoSize i) of
"()" -> "" -- TODO AnySize
str -> " | " ++ str
srcStr = case infoSource i of
"" -> ""
src -> " | " ++ src
instance Eq (Size a) => Eq (Info a)
where
ia == ib = infoSize ia == infoSize ib
-- TODO
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
}
-- | 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 :: (Literal TypeCtx :<: dom) => ASTF (Decor info dom) a -> Maybe a
viewLiteral (prjDecorCtx typeCtx -> Just (_,Literal a)) = Just a
viewLiteral _ = Nothing
-- | Construct a 'Literal' decorated with 'Info'
literalDecorSrc :: (Type a, Literal TypeCtx :<: dom) =>
SourceInfo -> a -> ASTF (Decor Info dom) a
literalDecorSrc src a = injDecor
((mkInfo (sizeOf a)) {infoSource = src})
(Literal a `withContext` typeCtx)
-- | Construct a 'Literal' decorated with 'Info'
literalDecor :: (Type a, Literal TypeCtx :<: dom) =>
a -> ASTF (Decor Info dom) 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 :: (MaybeWitnessSat TypeCtx dom, Literal TypeCtx :<: dom) =>
SourceInfo -> ASTF (Decor Info dom) a -> a -> ASTF (Decor Info dom) a
constFold src expr a
| Just TypeWit <- fromSatWit `fmap` maybeWitnessSat typeCtx 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
( WitnessCons feature
, MaybeWitnessSat TypeCtx dom
, AlphaEq dom dom (Decor Info dom) [(VarId, VarId)]
, EvalBind dom
, Literal TypeCtx :<: dom
, Variable TypeCtx :<: dom
, Lambda TypeCtx :<: dom
) =>
Optimize feature dom
where
-- | Top-down and bottom-up optimization of a feature
optimizeFeat
:: Optimize dom dom
=> feature a
-> Args (AST dom) a
-> Opt (ASTF (Decor Info dom) (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
:: feature a
-> Args (AST (Decor Info dom)) a
-> Opt (ASTF (Decor Info dom) (DenResult a))
constructFeatOpt = constructFeatUnOpt
-- | Unoptimized construction of an expression from a symbol and its
-- optimized arguments
constructFeatUnOpt
:: feature a
-> Args (AST (Decor Info dom)) a
-> Opt (ASTF (Decor Info dom) (DenResult a))
-- TODO Optimization should throw an error when the size of a node is
-- over-constrained. It can only happen if there's a bug in the general
-- size inference, or if the user has stated invalid size constraints. In
-- both cases it may lead to incorrect optimizations, so throwing an error
-- seems preferable.
-- | Optimized construction of an expression from a symbol and its optimized
-- arguments
constructFeat :: Optimize feature dom
=> feature a
-> Args (AST (Decor Info dom)) a
-> Opt (ASTF (Decor Info dom) (DenResult a))
constructFeat a args = do
aUnOpt <- constructFeatUnOpt a args
aOpt <- constructFeatOpt a args
return $ updateDecor (const $ getInfo aUnOpt) aOpt
-- This function uses `constructFeatOpt` for optimization and
-- `constructFeatUnOpt` for propagation of `Info`. This is because
-- `constructFeatOpt` may forget size constraints added by `SizeProp`. It is
-- reasonable to assume that `aUnOpt` has at least as accurate `Info` as
-- `aOpt`, so we just replace the `Info` with the one from `aUnOpt`. Another
-- option would have been to meet the two lattices.
instance (Optimize sub1 dom, Optimize sub2 dom) =>
Optimize (sub1 :+: sub2) dom
where
optimizeFeat (InjL a) = optimizeFeat a
optimizeFeat (InjR a) = optimizeFeat a
constructFeatOpt (InjL a) = constructFeatOpt a
constructFeatOpt (InjR a) = constructFeatOpt a
constructFeatUnOpt (InjL a) = constructFeatUnOpt a
constructFeatUnOpt (InjR a) = constructFeatUnOpt 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 :: Optimize dom dom => ASTF dom a -> Opt (ASTF (Decor Info dom) a)
optimizeM a = do
aOpt <- transformNode optimizeFeat 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
-- TODO singleton range --> literal
-- literal --> singleton range
-- | Optimization of an expression. This function runs 'optimizeM' and extracts
-- the result.
optimize :: Optimize dom dom => ASTF dom a -> ASTF (Decor Info dom) a
optimize = flip runReader initEnv . optimizeM
-- | Convenient default implementation of 'constructFeatUnOpt'. Uses 'sizeProp'
-- to propagate size.
constructFeatUnOptDefaultTyp
:: ( feature :<: dom
, WitnessCons feature
, SizeProp feature
, Show (Size (DenResult a))
)
=> TypeRep (DenResult a)
-> feature a
-> Args (AST (Decor Info dom)) a
-> Opt (ASTF (Decor Info dom) (DenResult a))
constructFeatUnOptDefaultTyp typ feat args
| ConsWit <- witnessCons feat
= do
src <- asks sourceEnv
let sz = sizeProp feat $ mapArgs (WrapFull . getInfo) args
vars = Map.unions $ listArgs (infoVars . getInfo) args
return $ appArgs (injDecor (Info typ sz vars src) feat) args
-- | Like 'constructFeatUnOptDefaultTyp' but without an explicit 'TypeRep'
constructFeatUnOptDefault
:: ( feature :<: dom
, WitnessCons feature
, WitnessSat feature
, SatContext feature ~ TypeCtx
, SizeProp feature
)
=> feature a
-> Args (AST (Decor Info dom)) a
-> Opt (ASTF (Decor Info dom) (DenResult a))
constructFeatUnOptDefault feat
| ConsWit <- witnessCons feat
, TypeWit <- fromSatWit $ witnessSat feat
= constructFeatUnOptDefaultTyp typeRep feat
-- | Convenient default implementation of 'optimizeFeat'
optimizeFeatDefault :: (Optimize feature dom, Optimize dom dom)
=> feature a
-> Args (AST dom) a
-> Opt (ASTF (Decor Info dom) (DenResult a))
optimizeFeatDefault feat args
| ConsWit <- witnessCons feat
= constructFeat feat =<< mapArgsM optimizeM args