feldspar-compiler-0.4.0.2: Feldspar/Compiler/Imperative/FromCore.hs
{-# LANGUAGE OverlappingInstances, PatternGuards, UndecidableInstances #-}
module Feldspar.Compiler.Imperative.FromCore
(
Compilable(..)
, numArgs
, fromCore
) where
import Control.Monad.State
import Control.Monad.Writer
import Data.List
import Data.String
import Data.Bits
import qualified Feldspar.DSL.Expression as Lang
import qualified Feldspar.DSL.Lambda as Lang
import Feldspar.DSL.Lambda hiding (Value, Variable)
import Feldspar.DSL.Network
import Feldspar.Set (universal)
import qualified Feldspar.Core.Types as Lang
import Feldspar.Core.Representation hiding (variable)
import qualified Feldspar.Core.Representation as Lang
import qualified Feldspar.Core.Functions.Array as Lang
import qualified Feldspar.Core.Functions.Tuple as Lang
import Feldspar.Core.Functions.Num ()
import Feldspar.Range (Range(..),BoundedInt)
import Data.Typeable (Typeable, typeOf)
import Feldspar.Compiler.Imperative.Representation hiding (blockProgram)
import Feldspar.Compiler.Imperative.Frontend
import Feldspar.Compiler.Backend.C.CodeGeneration
import Feldspar.Compiler.Error
import Feldspar.Compiler.Backend.C.Library
type Transformer a = StateT Integer (Writer [Definition ()]) a
-- | Path of a matching variable
type Path = [Int]
type MultiVar = [(Path, Lang.TypeRep)]
indexType :: Type
indexType = NumType Unsigned S32
-- | Where to place the result of a single-edge
data SingleLoc a
= Shifted (Expression (), FeldNetwork (Out ()) Lang.Length) (SingleLoc a)
-- ^ A shifted location
| SingleLoc
{ singleLV :: Expression ()
, feedback :: FeldNetwork (Out ()) a
}
-- ^ A location represented by a 'LeftValue ()' and a corresponding
-- expression. The expression is used for feedback.
-- | Container of a SingleLoc
getSingleLV :: SingleLoc a -> Expression ()
getSingleLV (SingleLoc lv _) = lv
getSingleLV (Shifted _ sl) = getSingleLV sl
-- | Where to place the result of a general program
data Location ra a
where
S :: { single :: SingleLoc a } -> Location (Out ()) a
M :: { multi :: Ident } -> Location ra a
-- | Append a path to an identifier
pathIdent :: Ident -> Path -> Ident
pathIdent ident path = concat $ intersperse "_" $ (ident :) $ map show path
toSingle :: Type -> Location (Out ()) a -> SingleLoc a
toSingle typ (S loc) = loc
toSingle typ (M ident) = SingleLoc expr fb
where
expr = varExpr $ variable ident typ
fb = Lang.Variable ident
getIx :: Lang.Type a
=> FeldNetwork (Out ()) [a]
-> FeldNetwork (Out ()) Lang.Index
-> FeldNetwork (Out ()) a
getIx a ix
= undoEdge
$ unData
$ Lang.getIx (fromOutEdge universal a) (fromOutEdge universal ix)
-- TODO Think about size
addExpr
:: FeldNetwork (Out ()) Lang.DefaultWord
-> FeldNetwork (Out ()) Lang.DefaultWord
-> FeldNetwork (Out ()) Lang.DefaultWord
addExpr a b = undoEdge $ unData (fromOutEdge universal a + fromOutEdge universal b)
-- TODO Think about size
add :: Expression () -> Expression () -> Expression ()
add a b = FunctionCall "(+)" indexType InfixOp [a,b] () ()
indexLocSingle
:: Lang.Type a
=> [(Expression (), FeldNetwork (Out ()) Lang.Length)] -- ^ Accumulated shifts
-> SingleLoc [a] -- ^ Original location
-> Expression () -- ^ Index variable
-> FeldNetwork (Out ()) Lang.Index -- ^ Index expression
-> SingleLoc a
indexLocSingle ls (Shifted l loc) ixVar ixExpr =
indexLocSingle (l:ls) loc ixVar ixExpr
indexLocSingle ls (SingleLoc lv fb) ixVar ixExpr = SingleLoc lv' fb'
where
lv' = arrayElem lv $ foldl add ixVar (map fst ls)
ixExpr' = foldl addExpr ixExpr (map snd ls)
fb' = getIx fb ixExpr'
-- | Sum the shifts of a shifted location
sumShifts :: SingleLoc a -> Expression ()
sumShifts (SingleLoc _ _) = createConstantExpression $ intConst 0
sumShifts (Shifted (e,_) l) = add e $ sumShifts l
-- | Indexing into a location
indexLoc
:: Lang.Type a
=> SingleLoc [a] -- ^ Original location
-> Expression () -- ^ Index variable
-> FeldNetwork (Out ()) Lang.Index -- ^ Index expression
-> SingleLoc a
indexLoc loc ixVar ixExpr = indexLocSingle [] loc ixVar ixExpr
selectFst :: (Lang.Type a, Lang.Type b)
=> SingleLoc (a,b)
-> SingleLoc a
selectFst (SingleLoc e fb) = SingleLoc (StructField e member () ()) fb'
where
member = fst $ head $ s
(StructType s) = typeof e
fb' = undoEdge $ unData $ Lang.getFst $ fromOutEdge universal fb
selectSnd :: (Lang.Type a, Lang.Type b)
=> SingleLoc (a,b)
-> SingleLoc b
selectSnd (SingleLoc e fb) = SingleLoc (StructField e member () ()) fb'
where
member = fst $ head $ tail $ s
(StructType s) = typeof e
fb' = undoEdge $ unData $ Lang.getSnd $ fromOutEdge universal fb
locToNode :: Location (Out ra) a -> FeldNetwork (Out ra) a
locToNode (S (SingleLoc _ fb)) = fb
locToNode (M ident) = Lang.Variable ident
-- TODO Ingoring shift
multiVarIn :: FeldNetwork (In ra) a -> MultiVar
multiVarIn = listEdge $ \path a -> (path, edgeType (edgeInfo a))
simpleExpr :: Expression () -> Transformer ([Program ()], Expression ())
simpleExpr expr = return ([],expr)
isComplex :: FeldNetwork (Out ra) a -> Bool
isComplex (Inject (Node Condition) :$: _ :$: _ :$: _) = True
isComplex (Inject (Node Parallel) :$: _ :$: _ :$: _) = True
isComplex (Inject (Node Sequential) :$: _ :$: _ :$: _ :$: _) = True
isComplex (Inject (Node ForLoop) :$: _ :$: _ :$: _) = True
isComplex (Inject (Node (NoInline _)) :$: _ :$: _) = True
isComplex (Inject (Node SetLength) :$: _ :$: _) = True
isComplex (Inject (Node Pair) :$: _ :$: _) = True
isComplex (Inject (Node SetIx) :$: _ :$: _ :$: _) = True
isComplex a = isArrayLit a
genLiteralExpression ::
Lang.Type a => a -> Transformer ([Program ()], Expression ())
genLiteralExpression = simpleExpr . flip ConstExpr () . compileDataRep . Lang.dataRep
genVarExpression ::
Type -> FeldNetwork (Out ()) a -> Transformer ([Program ()], Expression ())
genVarExpression typ a = simpleExpr (varExpr var)
where
Just ident = traceVar a
var = variable (pathIdent ident $ matchPath a) typ
genApplyExpression
:: Type -> String -> FeldNetwork (In ra) a
-> Transformer ([Program ()], Expression ())
genApplyExpression typ fun a = do
(progs,exprs) <- liftM unzip $ sequence $ listEdge (const genExpressionIn) a
return (concat progs, FunctionCall fun typ SimpleFun exprs () ())
-- | Generate an expression that is not a literal, function call or a variable
genComplexExpression ::
Type -> FeldNetwork (Out ()) a -> Transformer ([Program ()], Expression ())
genComplexExpression typ a = do
ident <- newName "w"
let var = variable ident typ
decl = Declaration var Nothing ()
prog <- genNode (M ident) a
return ([BlockProgram (block [decl] prog) ()], VarExpr var ())
genExpressionIn
:: FeldNetwork (In ()) a
-> Transformer ([Program ()], Expression ())
genExpressionIn a = genExpression typ (undoEdge a)
where
typ = compileTypeRep $ edgeType $ edgeInfo a
-- | Generate an expression plus support code
genExpression
:: Type
-> FeldNetwork (Out ()) a
-> Transformer ([Program ()], Expression ())
genExpression _ a@(Inject (Node (Literal lit)))
| not (isArrayLit a) = genLiteralExpression lit
genExpression typ (Inject (Node (Function fun _)) :$: a) =
genApplyExpression typ fun a
genExpression typ a
| Just _ <- traceVar a = genVarExpression typ a
genExpression typ (Inject m :$: _)
| isMatch m = localError InvariantViolation "matching on non-variable"
genExpression typ a = genComplexExpression typ a
genDeclarations :: Ident -> MultiVar -> [Declaration ()]
-- TODO Would be nice to have (Out ra)
genDeclarations ident vars =
[ Declaration var Nothing ()
| (path,typ) <- vars
, let ident' = pathIdent ident path
, let var = variable ident' $ compileTypeRep typ
]
genMultiCopy :: MultiVar -> Location ra a -> Location rb a -> [Program ()]
genMultiCopy [(_,typ)] (S (SingleLoc lv _)) (M rIdent) = [copyProg lv rhs]
where
rhs = varExpr $ variable rIdent $ compileTypeRep typ
genMultiCopy _ (S (SingleLoc lhs _)) (S (SingleLoc rhs _)) = [copyProg lhs rhs]
genMultiCopy vars (M ident) (M rIdent) =
[ copyProg (varExpr lVar) (varExpr rVar)
| (path,typ) <- vars
, let ident' = pathIdent ident path
, let rIdent' = pathIdent rIdent path
, let typ' = compileTypeRep typ
, let lVar = variable ident' typ'
, let rVar = variable rIdent' typ'
]
-- TODO Ingoring shift
genMultiCopy [(_,typ)] (M ident) (S (SingleLoc rhs _)) = [copyProg lhs rhs]
where
lhs = varExpr $ variable ident $ compileTypeRep typ
-- | Generate code for a 'Let' expression
genLet
:: ([Program ()] -> FeldNetwork ra a -> Transformer b)
-- ^ Generator for the body
-> FeldNetwork ra a
-> Transformer b
genLet gen (Let base :$: a :$: Lambda f) = do
aIdent <- newName base
aProg <- genNode (M aIdent) a
let aBlock = block (genDeclarations aIdent (resTypes a)) aProg
letProg = f (Lang.Variable aIdent)
gen [BlockProgram aBlock ()] letProg
genNodeExpression ::
SingleLoc a -> Type -> FeldNetwork (Out ()) a -> Transformer [Program ()]
genNodeExpression loc typ a = do
(prog,rhs) <- genExpression typ a
return $ prog ++ case loc of
SingleLoc l _ -> [copyProg l rhs]
l@(Shifted _ _) -> [copyProgPos (getSingleLV l) (sumShifts l) rhs]
genNodeSingle
:: SingleLoc a
-> Type
-> FeldNetwork (Out ()) a
-> Transformer [Program ()]
genNodeSingle loc _ a | isComplex a = genNode (S loc) a
genNodeSingle loc typ a = genNodeExpression loc typ a
-- | TODO network must be a 'Node' or a (nested) 'Let' resulting in a 'Node'
genNode :: forall ra a
. Location ra a
-> FeldNetwork ra a -- TODO Would be nice to have (Out ra)
-> Transformer [Program ()]
genNode loc a | isLet a = genLet genBody a
where
genBody letProg body = liftM (letProg++) $ genNode loc body
genNode loc (Inject (Node Condition) :$: cond :$: t :$: e) = do
(condProg,condExpr) <- genExpressionIn cond
thenProg <- genEdge loc t
elseProg <- genEdge loc e
let branchProg = Branch condExpr (block [] thenProg) (block [] elseProg) () ()
return (condProg ++ [branchProg])
genNode loc a@(Inject (Node Parallel) :$: len :$: Lambda ixf :$: cont) = do
(lenProg,lenExpr) <- genExpressionIn len
ixIdent <- newName "i"
let bodyExpr = ixf (Lang.Variable ixIdent)
ixVar = variable ixIdent indexType
ixExpr = Lang.Variable ixIdent
loc' = toSingle typ loc
locBody = indexLoc loc' (varExpr ixVar) ixExpr
locCont = Shifted (lenExpr, undoEdge len) loc'
setLen = case loc' of
SingleLoc _ _ -> setLength (singleLV loc') lenExpr
Shifted _ l -> increaseLength (getSingleLV l) lenExpr
bodyProg <- genEdge (S locBody) bodyExpr
contProg <- genEdge (S locCont) cont
return (lenProg ++ [setLen,ParLoop ixVar lenExpr 1 (block [] bodyProg) () ()] ++ contProg)
-- TODO Should use \"default size\" for index type
where
[(_, Lang.ArrayType _ t)] = resTypes a
typ = compileTypeRep t
genNode loc a@(Inject (Node Sequential) :$: len :$: init :$: Lambda step :$: Lambda cont) = do
(lenProg,lenExpr) <- genExpressionIn len
stepIdent <- newName "x"
let stIdent = stepIdent ++ "_2"
tempIdent <- newName "temp"
ixIdent <- newName "i"
initProg <- genEdge (M stIdent) init
let step' = step (Lang.Variable ixIdent)
stepExpr = shallowApply step' (Lang.Variable stIdent)
tempVarElem = variable (tempIdent ++ "_1") elemTyp
ixVar = variable ixIdent indexType
ixExpr = Lang.Variable ixIdent
loc' = toSingle arrTyp loc
locElem = indexLoc loc' (varExpr ixVar) ixExpr
locCont = Shifted (lenExpr, undoEdge len) loc'
tempDeclElem = genDeclarations tempIdent [([1],tElem)]
tempDeclsSt = genDeclarations (tempIdent ++ "_2") (multiVarIn init)
stDecls = genDeclarations stIdent (multiVarIn init)
elemCopy = copyProg (singleLV locElem) (varExpr tempVarElem)
stCopy = genMultiCopy (multiVarIn init) (M stIdent) (M $ tempIdent ++ "_2")
apa = cont (Lang.Variable stIdent)
setLen = setLength (singleLV loc') lenExpr
stepProg <- genEdge (M tempIdent) stepExpr
contProg <- genEdge (S locCont) apa
return $
[BlockProgram (block stDecls (initProg ++ lenProg ++ [setLen, ParLoop ixVar lenExpr 1 (block (tempDeclElem++tempDeclsSt) (stepProg ++ stCopy ++ [elemCopy])) () ()] ++ contProg)) ()]
where
[(_, tArr@(Lang.ArrayType _ tElem))] = resTypes a
arrTyp = compileTypeRep tArr
elemTyp = compileTypeRep tElem
genNode loc a@(Inject (Node ForLoop) :$: len :$: init :$: Lambda body) = do
(lenProg,lenExpr) <- genExpressionIn len
tempIdent <- newName "temp"
ixIdent <- newName "i"
initProg <- genEdge loc init
let body' = body (Lang.Variable ixIdent)
bodyExpr = shallowApply body' (locToNode loc)
ixVar = variable ixIdent indexType
tempDecls = genDeclarations tempIdent (resTypes a)
bodyProg <- genEdge (M tempIdent) bodyExpr
let copyProg = genMultiCopy (resTypes a) loc (M tempIdent)
return [BlockProgram (block tempDecls (lenProg ++ initProg ++ [ParLoop ixVar lenExpr 1 (block [] (bodyProg ++ copyProg)) () ()])) ()]
genNode (S (Shifted _ _)) a@(Inject (Node (Literal _))) | isArrayLit a && isEmpty a = return []
genNode loc a@(Inject (Node (Literal b))) | isArrayLit a = return [initialize (getSingleLV l) (sumShifts l) v]
where
l = toSingle typ loc
v = compileDataRep $ Lang.dataRep b
typ = compileTypeRep $ Lang.typeRep' b
genNode loc a@(Inject (Node (NoInline name)) :$: Lambda body :$: x) = do
param <- newName "in"
result <- newName "out"
prolog <- sequence $ listEdge (const genExpressionIn) x
let prologProgs = concatMap fst prolog
prologArgs = map (flip In () . snd) prolog
argVars = genVars param x
resVars = genVars result (body $ Lang.Variable param)
outArgs = map (flip Out ()) $ genLocExprs loc a
prog <- genEdge (M result) (body $ Lang.Variable param)
tell [procedure name argVars resVars (block [] prog)]
return $ prologProgs ++ [procedureCall name prologArgs outArgs]
where
typ = undefined
nodeType = compileTypeRep . snd . head . resTypes
genNode (S loc) (Inject (Node SetLength) :$: len :$: (Inject (Edge edge) :$: a))
| not (isComplex a), Just name <- traceVar a = do
(lenProg,lenExpr) <- genExpressionIn len
let typ = compileTypeRep $ edgeType edge
let arrProg = [copyProgLen (singleLV loc) (varExpr $ createVariable name typ) lenExpr]
return $ lenProg ++ arrProg
genNode (S loc) a@(Inject (Node SetLength) :$: len :$: arr) = do
(lenProg,lenExpr) <- genExpressionIn len
arrProg <- genEdge (S loc) arr
return $ lenProg ++ arrProg ++ [setLength (singleLV loc) lenExpr]
genNode loc a@(Inject (Node SetIx) :$: ix :$: val :$: e) = do
(ixProg,ixExpr) <- genExpressionIn ix
copy <- genEdge loc e
let
updLoc = indexLoc (toSingle elemType loc) ixExpr $ undoEdge ix
update <- genEdge (S updLoc) val
return $ copy ++ ixProg ++ update
where
[(_, (Lang.ArrayType _ tElem))] = resTypes a
elemType = compileTypeRep tElem
genNode loc a@(Inject (Node Pair) :$: x :$: y) = do
prog1 <- genEdge (S $ selectFst $ toSingle (nodeType a) loc) x
prog2 <- genEdge (S $ selectSnd $ toSingle (nodeType a) loc) y
return $ prog1 ++ prog2
where
nodeType = compileTypeRep . snd . head . resTypes
genNode loc a@(Inject (Node (Literal _))) = genNodeSingle (toSingle (nodeType a) loc) (nodeType a) a
where
nodeType = compileTypeRep . snd . head . resTypes
genNode loc a@(Inject (Node (Function _ _)) :$: _) = genNodeSingle (toSingle (nodeType a) loc) (nodeType a) a
where
nodeType = compileTypeRep . snd . head . resTypes
genLocExprs :: Location ra a -> FeldNetwork ra a -> [Expression ()]
genLocExprs (S loc) _ = [singleLV loc]
genLocExprs (M ident) a =
[ varExpr $ variable (pathIdent ident path) (compileTypeRep typ)
| (path,typ) <- resTypes a
]
viewGroup2 :: FeldNetwork (In (ra,rb)) (a,b) -> (FeldNetwork (In ra) a, FeldNetwork (In rb) b)
viewGroup2 (Inject Group2 :$: a :$: b) = (a,b)
-- TODO: Move somewhere else
genEdgeSingle
:: Ident
-> Path
-> FeldNetwork (In ()) a
-> Transformer [Program ()]
genEdgeSingle ident path a =
genNodeSingle (toSingle typ $ M ident') typ (undoEdge a)
where
ident' = pathIdent ident path
typ = compileTypeRep $ edgeType $ edgeInfo a
-- | Generate code for a multi-edge
genEdge :: Location (Out ra) a -> FeldNetwork (In ra) a -> Transformer [Program ()]
genEdge loc a | isLet a = genLet genBody a
where
genBody letProg body = liftM (letProg++) $ genEdge loc body
genEdge loc (Inject (Edge edge) :$: a) = genNodeSingle (toSingle typ loc) typ a
where
typ = compileTypeRep $ edgeType edge
genEdge (M ident) a = liftM concat $ sequence $ listEdge (genEdgeSingle ident) a
-- | Generate a variable of the same type as the given single-edge
genVar :: Ident -> Path -> FeldNetwork (In ()) a -> Variable ()
genVar ident path a = variable (pathIdent ident path) typ
where
typ = compileTypeRep $ edgeType $ edgeInfo a
genVars :: Ident -> FeldNetwork (In ra) a -> [Variable ()]
genVars ident (Let _ :$: a :$: Lambda f) =
genVars ident (f (Lang.Variable "TODO"))
genVars ident a = listEdge (genVar ident) a
class Compilable t where
toImperativeM
:: String -- ^ Name of procedure
-> [Variable ()] -- ^ Free variables
-> t -- ^ Program to compile
-> Transformer ()
-- | Returns a list containing the number of edges in each curried argument
buildInParamDescriptor :: t -> [Int]
instance Syntactic a => Compilable a where
toImperativeM procName freeVars prog = do
ident <- newName "out"
body <- genEdge (M ident) prog'
let resVars = genVars ident prog'
tell [Procedure procName freeVars resVars (block [] body) () ()]
where
prog' = feldSharing (toEdge prog)
buildInParamDescriptor _ = []
instance (Syntactic a, Compilable t) => Compilable (a -> t) where
toImperativeM procName freeVars prog = do
ident <- newName "in"
let arg = Lang.variable universal ident
let vars = genVars ident (toEdge arg)
toImperativeM procName (freeVars ++ vars) (prog arg)
buildInParamDescriptor prog =
countEdges (toEdge arg) : buildInParamDescriptor (prog arg)
where
arg = Lang.variable universal "argument"
numArgs :: Compilable a => a -> Int
numArgs = length . buildInParamDescriptor
fromCore :: Compilable t => String -> t -> Module ()
fromCore procName prog = Module (execWriter $ evalStateT (toImperativeM procName [] prog) 0) ()
initialize :: Expression () -> Expression () -> Constant () -> Program ()
initialize loc shift (ArrayConst vs _ _) = createProgramSequence $
(setLength loc $ shift `add` intConstExpr (toInteger $ length vs)) :
(map (\(v,i) -> initialize (arrayElem loc $ shift `add` i) (intConstExpr 0) v) $
zip vs $ map intConstExpr [0..])
initialize loc _ v = copyProg loc $ createConstantExpression v
-- | Compilation of a data representation to an imperative constant
compileDataRep :: Lang.DataRep -> Constant ()
compileDataRep (Lang.BoolData x) = BoolConst x () ()
compileDataRep (Lang.IntData x) = IntConst x () ()
compileDataRep (Lang.FloatData x) = FloatConst (fromRational $ toRational x) () ()
compileDataRep (Lang.ComplexData r i) = ComplexConst (compileDataRep r) (compileDataRep i) () ()
compileDataRep (Lang.ArrayData xs) = ArrayConst (map compileDataRep xs) () ()
compileDataRep (Lang.StructData sd) = localError InternalError "Struct constants not supported yet."
-- | Compilation of a type representation to an imperative type
compileTypeRep :: Lang.TypeRep -> Type
compileTypeRep typ = case typ of
Lang.BoolType -> BoolType
Lang.IntType r -> compileNumericType r
Lang.FloatType -> FloatType
Lang.ComplexType typ -> ComplexType (compileTypeRep typ)
Lang.UserType userTypeName -> UserType userTypeName
Lang.ArrayType dim elemTyp -> ArrayType (getLength dim) $ compileTypeRep elemTyp
Lang.StructType memberTypes -> StructType $ zip (map ((defaultMemberName++).show) [1..]) $ map compileTypeRep memberTypes
-- | Numeric type based on a range
compileNumericType :: (BoundedInt a, Typeable a) => Range a -> Type
compileNumericType r = NumType (intSign r) (intSize r)
-- | Sign based on a range
intSign :: BoundedInt a => Range a -> Signedness
intSign r
| isSigned (upperBound r) = Signed
| otherwise = Unsigned
-- | Size based on a range
intSize :: (BoundedInt a, Typeable a) => Range a -> Size
intSize r = case bitSize i of
8 -> S8
16 -> S16
32 -> S32
64 -> S64
_ -> localError InvariantViolation $ "unknown integer type: " ++ show (typeOf i)
where
i = upperBound r
-- | Compilation of a length
getLength :: Range Lang.Length -> Length
getLength l
| u == maxBound = UndefinedLen
| otherwise = LiteralLen (fromIntegral u)
where
u = upperBound l
-- | Customized error function
localError = handleError "Backends :: C :: ConstTransformation"