{- CAO Compiler
Copyright (C) 2014 Cryptography and Information Security Group, HASLab - INESC TEC and Universidade do Minho
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . -}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
{- |
Module : $Header$
Description : CAO to C translation.
Copyright : (C) 2014 Cryptography and Information Security Group, HASLab - INESC TEC and Universidade do Minho
License : GPL
Maintainer : Paulo Silva
Stability : experimental
Portability : non-portable
CAO to C translation.
Flexible contexts may be dropped.
-}
module Language.CAO.Translation.C (
cao2c
) where
import Control.Monad
import Data.List (intercalate, genericLength, partition)
import qualified Data.Set as Set
import Language.C.Syntax
import Language.C
import Text.PrettyPrint
import Language.CAO.Common.Error
import Language.CAO.Common.Fresh
import Language.CAO.Common.Literal
import Language.CAO.Common.Monad
import Language.CAO.Common.Outputable
import Language.CAO.Common.Polynomial
import Language.CAO.Common.SrcLoc
import Language.CAO.Common.State
import Language.CAO.Common.Utils
import Language.CAO.Common.Var
import Language.CAO.Index
import Language.CAO.Platform.Naming
import Language.CAO.Platform.Query
import Language.CAO.Platform.Specification
import Language.CAO.Semantics.Bits
import Language.CAO.Syntax
import Language.CAO.Syntax.Utils
import Language.CAO.Syntax.Tidy
import Language.CAO.Translation.Names
import Language.CAO.Translation.C.Wrappers
import Language.CAO.Type
import Language.CAO.Type.Utils
{-
- The representation of Boolean must be compatible with C integers, or, if a
pointer is used, this must be castable to integers.
-}
--------------------------------------------------------------------------------
------------------------------------- Prog -------------------------------------
-- Top level translation function
cao2c
:: CaoMonad m
=> TranslationSpec -> Prog Var
-> m String
cao2c tspec ast = withCST $ do
cprog <- mapProg tspec $ tidyCaoAST ast
fnm <- getFileName
let header = moduleHeader fnm tspec
return $ header ++ render (pretty cprog)
-- Precondition:
-- The list of definitions is not empty.
mapProg
:: CaoMonad m
=> TranslationSpec -> Prog Var
-> m CTranslUnit'
mapProg tspec (Prog defs (Just ip)) = do
let gvars = filter isGlobalVar $ Set.toList $ bvs defs
defs' <- concatMapM (mapDefinition tspec) defs
ip' <- initProc tspec ip gvars
disp <- disposeProc tspec gvars
return $ CTranslUnit' (defs' ++ ip' : disp : []) undefNode
mapProg _ _ = internalError "mapProg" "Init procedure not found"
--------------------------------------------------------------------------------
-- Init precedure
initProc
:: CaoMonad m
=> TranslationSpec -> Fun Var -> [Var]
-> m CExtDecl'
initProc tspec f = liftM (CED . CFDefExt) . mapInit tspec f
-- Init procedure generation:
-- Gets the body of the init procedure and the list of global variables
mapInit
:: CaoMonad m
=> TranslationSpec -> Fun Var -> [Var]
-> m CFunDef
mapInit tspec (Fun fn _ _ body) gvs = do
resetCST
let (gc, gvs') = partition indVar gvs
gvs'' = map varDecl gvs'
body''' = mergeDecls gc gvs'' body
body' <- mapStatements tspec body'''
disp <- disposeAlloc tspec
let body'' = body' ++ disp
return $ cProc tspec (getSymbol $ unLoc fn) body''
where
varDecl v = genLoc $ VDecl $ VarD (genLoc v) undefined Nothing
mergeDecls gc vs b = insertConstDecls gc (insertVarDecls vs b)
-- XXX: This is not very efficient...
insertConstDecls [] b = b
insertConstDecls (v:vs) b = insertConstDecls vs (insertConst v b)
insertConst v (L l (Nop EndConsts):slst) = varDecl v : L l (Nop EndConsts) : slst
insertConst v (s@(L _ (Assign [LVVar (L _ a)] [L _ (TyE _ (FunCall _ _))])):slst)
| v == a = varDecl v : s : slst
insertConst v (s:slst) = s : insertConst v slst
insertConst _ _ = internalError "insertConst" "Not expected case"
insertVarDecls vs [] = vs
insertVarDecls vs (L l (Nop EndAux): slst) = (L l $ Nop EndAux) : vs ++ slst
insertVarDecls vs (s:slst) = s : insertVarDecls vs slst
--------------------------------------------------------------------------------
-- Dispose procedure
disposeProc
:: CaoMonad m
=> TranslationSpec -> [Var]
-> m CExtDecl'
disposeProc tspec =
liftM (CED . CFDefExt . cProc tspec (disposeName tspec))
. disposeVars tspec
-- Gets the allocated variables and disposes them
disposeAlloc
:: CaoMonad m
=> TranslationSpec
-> m [CBlockItem]
disposeAlloc tspec = getAllocVars >>= disposeVars tspec
disposeAllAlloc
:: CaoMonad m
=> TranslationSpec
-> m [CBlockItem]
disposeAllAlloc tspec = getAllAllocVars >>= disposeVars tspec
disposeVars
:: CaoMonad m
=> TranslationSpec -> [Var]
-> m [CBlockItem]
disposeVars tspec = concatMapM $ \ v -> let
typ = typeOf v
in autoOrAlloc tspec typ
(return [])
(do tname <- typeName tspec typ
let fcall = fCall tspec tname code_dispose
return $ cFuncCallStmt fcall (cVar' v : []) : []
)
--------------------------------------------------------------------------------
-- Definitions
mapDefinition
:: CaoMonad m
=> TranslationSpec -> LDef Var
-> m [CExtDecl']
mapDefinition tspec d = case unLoc d of
VarDef vd -> liftM (singleton . declOrMacro) $ mapVarDefinition tspec vd
ConstDef cd -> liftM (singleton . declOrMacro) $ mapConstDefinition tspec cd
FunDef fd -> liftM (singleton . CED . CFDefExt) $ mapFunc tspec fd
TyDef td -> mapTypeDef tspec td
--------------------------------------------------------------------------------
-- Constants
mapConstDefinition
:: CaoMonad m
=> TranslationSpec -> ConstDecl Var
-> m (Either CDecl CBlockItem)
mapConstDefinition tspec (ConstD (unLoc -> n) _ _) = mapVar tspec n
mapConstDefinition _ _ = internalError "" "Not expected case."
--------------------------------------------------------------------------------
-- Variables
mapVarDefinition
:: CaoMonad m
=> TranslationSpec -> VarDecl Var
-> m (Either CDecl CBlockItem)
mapVarDefinition tspec (VarD (unLoc -> n) _ Nothing) = mapVar tspec n
mapVarDefinition _ _ =
internalError "mapVarDefinition" "Not expected case."
-- Preconditions:
-- 1 - Simplification removes all initializations and multiple declarations
-- in the global setting, so they are not expected as arguments of this
-- function.
-- 2 - Variable/constant initialization is provided elsewhere, namely in the
-- 'init' function.
mapVar :: CaoMonad m
=> TranslationSpec -> Var
-> m (Either CDecl CBlockItem)
mapVar tspec n =
varOrMacroDecl tspec tn auxVar auxMacro
where
tn = varType n
auxVar = liftM (Left . cVarDecl (getSymbol n) . tPrefix tspec) $ typeName tspec tn
auxMacro = valOrRefOpMacroReturn tspec tn code_decl
(caoError defSrcLoc $ mkUnknownErr $ ".:\
\ Not expecting macro variable declaration returning a value")
(do typ <- typeName tspec tn
(p, _) <- extractParams' tspec tn -- TODO: Verify what happens with global mod variables
let fdcall = fCall tspec typ code_decl
return $ Right $ cFuncCallStmt fdcall (cVar' n : p))
--------------------------------------------------------------------------------
----------------------------------------- Func ---------------------------------
mapFunc
:: CaoMonad m
=> TranslationSpec -> Fun Var
-> m CFunDef
mapFunc tspec (Fun (L _ fn) args _ body) = do
let FuncSig _ rtype _ = varType fn
rtype' = fromTuple rtype
resetCST
body' <- mapBlocks tspec body
tmpvs <- getTmpVars
decls <- concatMapM (mapVarDecl tspec . varDecl) tmpvs
args' <- mapM (mapArg tspec) args
(cr, rargs) <- mapReturnType tspec rtype'
let exitFunc = if null rtype' then cReturn caoOk : [] else []
body'' = decls <<+> (body' <+>> exitFunc)
return $ cFuncDefinition (getSymbol fn) (rargs ++ args') (tPrefix tspec cr) body''
where
varDecl v = VarD (genLoc v) (type2TyDecl (varType v)) Nothing
mapReturnType
:: CaoMonad m
=> TranslationSpec -> [Type Var]
-> m (String, [CDecl])
mapReturnType _ [] = return (caoRes, [])
mapReturnType tspec tps@(t:tl) = do
(t', tl') <- if isStruct t && aux t
then return (getSymbol $ getStructName t, tl)
else valOrRefFuncReturn tspec t (liftM (split id (const tl)) $ typeName tspec t) (return (caoRes, tps))
tl'' <- zipWithSeqM byReference tl'
return (t', tl'')
where
-- [See note]
byReference n ty = do
let retArg = retArgId ++ show n
(_, ty') <- mapType tspec ty
valOrRef tspec ty (return $ cPointerDecl retArg) (return $ cParamDecl retArg) `apM` ty'
aux (Struct sname _) = varType sname == Bullet -- HACK
aux _ = False
--------------------------------------------------------------------------------
mapArg
:: CaoMonad m
=> TranslationSpec -> Arg Var
-> m CDecl
mapArg tspec (Arg (L _ an) td) = cArgs tspec (getSymbol an) td $ varType an
mapArg tspec (ArgConst (L _ an) td _) = cArgs tspec (getSymbol an) td $ varType an
--------------------------------------------------------------------------------
--------------------------------------- TypeDef --------------------------------
mapTypeDef
:: CaoMonad m
=> TranslationSpec -> TyDef Var
-> m [CExtDecl']
mapTypeDef tspec td = case td of
TySynDef sn _ -> do
let nm = getSymbol $ unLoc sn
(tname, typ) <- mapType tspec $ synType $ varType $ unLoc sn -- TODO: HACK: get a more elegante way to deal with type synonyms
if nm == tname then return []
else return [CED $ CDeclExt $ cTypedefDecl (tPrefix tspec nm) typ]
StructDecl sname lFields -> do
let tname = tPrefix tspec $ getSymbol (unLoc sname)
lFields' <- mapM (aux . fst) lFields
return [ CStructExt tname (tname ++ "_struct") lFields' ]
where
aux :: CaoMonad m => Located Var -> m CDecl'
aux v = do
let typ = sfType $ varType $ unLoc v
fldName = getSymbol $ unLoc v
tname <- typeName tspec typ
varOrMacroDecl tspec typ
(declD fldName tname)
(declM fldName tname typ)
declD fn tn = return $ cParamDecl' fn (cType (tPrefix tspec tn))
declM fn tn typ = valOrRefOpMacroReturn tspec typ code_decl
(caoError defSrcLoc $ mkUnknownErr $ ".:\
\ Not expecting macro variable declaration returning a value")
(do (p, _) <- extractParams' tspec typ
let ffldcall = fCall tspec tn code_decl
return $ CFld $ cFuncCallStmt ffldcall (cVar fn : p))
--------------------------------------------------------------------------------
--------------------------------------- TypeDecl -------------------------------
mapType
:: CaoMonad m
=> TranslationSpec -> Type Var
-> m (String, CDeclSpec)
mapType tspec t = case t of
TySyn v _ -> return (getSymbol v, cType (tPrefix tspec $ getSymbol v))
_ -> do
nm <- typeName tspec t
return (nm, cType (tPrefix tspec nm))
--------------------------------------------------------------------------------
-------------------------------------- Statement -------------------------------
{-
The copy of values to ensure safeness may require types which are dependent
on the sequence index (this only happens with sequences which were translated
to while loops). Thus, they cannot be handled like ordinary variables and
taken outside the loop (done in PreC module), so they are initialized and
dealocked in the body of the loop to ensure dependencies.
Since PreC declares those variables 'in place', this means that C scope rules
applies and we have to dealock them in the exit of any kind of block
(while, if, function)
-}
-- Maps a block of CAO statements into a block of C statements
mapBlocks
:: CaoMonad m
=> TranslationSpec -> [LStmt Var]
-> m CStat
mapBlocks tspec stmt = allocScope $ do
stmt' <- mapStatements tspec stmt
iDisp <- if isReturn $ unLoc $ last stmt then return [] else disposeAlloc tspec
return $ CCompound [] (stmt' ++ iDisp) undefNode
mapStatements
:: CaoMonad m
=> TranslationSpec -> [LStmt Var]
-> m [CBlockItem]
mapStatements tspec = concatMapM (mapStatement tspec . unLoc)
mapStatement
:: CaoMonad m
=> TranslationSpec -> Stmt Var
-> m [CBlockItem]
mapStatement tspec (VDecl vd) = mapVarDecl tspec vd
mapStatement tspec (Language.CAO.Syntax.CDecl cd) = mapConstDecl tspec cd
mapStatement tspec (Assign lv [unLoc -> unTyp -> FunCall fn args]) =
mapFunCall tspec lv fn args
mapStatement _ (Assign _ _) =
internalError "mapStatement" "Unexpected assignment case"
mapStatement tspec (FCallS pn ex) = do
liftM singleton $ mapFCallS tspec pn ex
mapStatement tspec (Ret re) = mapReturn tspec re
mapStatement tspec (Ite ex ifBlock elseBlock) = do
cond <- mapExp tspec ex
let ex' = typeOf ex
cond' <- valOrRef tspec ex' (return id) (return cPointedExpr) `apM` cond
ifBlock' <- mapBlocks tspec ifBlock
elseBlock' <- mapMaybeM (mapBlocks tspec) elseBlock
return [ CBlockStmt (CIf cond' ifBlock' elseBlock' undefNode) ]
mapStatement tspec (While ex whileBlock) = do
cond <- mapExp tspec ex
let ex' = typeOf ex
cond' <- valOrRef tspec ex' (return id) (return cPointedExpr) `apM` cond
whileBlock' <- mapBlocks tspec whileBlock
return [ CBlockStmt (CWhile cond' whileBlock' False undefNode) ]
mapStatement _ (Nop _) = return []
mapStatement _ _ = internalError "mapStatement" "Not expected!"
--------------------------------------------------------------------------------
{-
Note
If the type is declared as a reference there is nothing left to do
If the type is declared as a value, then a pointer is being used,
thus we have to deference it
-}
{-
Return can be of the form:
* struct, ref1, ref2, ...
* constant, ref1, ref2, ...
* variable, ref1, ref2, ...
* ref1, ref2, ...
-}
mapReturn
:: CaoMonad m
=> TranslationSpec -> [TLExpr Var]
-> m [CBlockItem]
mapReturn tspec [] = disposeAllAlloc tspec
mapReturn tspec exps@(expr:el) = do
e' <- mapExp tspec expr
(e'', el') <- if isCStructExpr $ unTyp $ unLoc expr
then return (e', el)
else valOrRefFuncReturn tspec (typeOf expr) (return (e', el)) (return (cVar caoOk, exps))
assign <- zipWithSeqM byReference el'
disp <- disposeAllAlloc tspec
return $ assign ++ disp ++ [ cReturnExpr e'' ]
where
byReference n ex = do
let op = if isLit (unTyp $ unLoc ex) then code_init else code_assign
typ = typeOf ex
tname <- typeName tspec typ
let fop = fCall tspec tname op
ex' <- mapExp tspec ex
-- [See note]
retArg <- valOrRef tspec typ (return cIndirection) (return id) `apM` cVar (retArgId ++ show n)
opReturnKind' tspec typ op
(return $ cAssignStmt retArg (cFuncCall fop [ex']))
(return $ cFuncCallStmt fop [retArg, ex'])
(do (p, _) <- composedCase typ
return $ cAssignStmt retArg (cFuncCall fop (ex' : p)))
(do (p, _) <- composedCase typ
return $ cFuncCallStmt fop (retArg : ex' : p))
composedCase typ = if isComposed typ then extractParams' tspec typ else return ([], [])
--------------------------------------------------------------------------------
-- Procedure calls
mapFCallS
:: CaoMonad m
=> TranslationSpec -> Var -> [TLExpr Var]
-> m CBlockItem
mapFCallS tspec pn = liftM (cFuncCallStmt (getSymbol pn)) . mapExps tspec
--------------------------------------------------------------------------------
-- Function calls
mapFunCall
:: CaoMonad m
=> TranslationSpec -> [LVal Var] -> Located Var -> [TLExpr Var]
-> m [CBlockItem]
-- Particular case when a global refrence extraction has to be called from the
-- static library
mapFunCall tspec lv (unLoc -> fn) ex@(e:_) | isCGlobalRef fn = do
e' : exps' <- mapExps tspec ex
(_, constArray) <- freshSmb
tname <- typeName tspec RInt
ccode <- cTypeCodeRedux tspec (typeOf e)
let lv' = mapLVal (head lv) -- TODO: verify that lv has only one value by pattern matching
len = genericLength exps'
cdecl = cTypeArrayDecl constArray (tPrefix tspec tname) exps'
return [cdecl, cFuncCallStmt (getSymbol fn) [ cExprAddr lv', e', cCharExpr ccode, cVar constArray, cIntExpr len] ]
-- Call to a function of the static library
mapFunCall tspec lv (unLoc -> fn) ex | isCFunction fn = do
ex' <- mapExps tspec ex
let typ = varType fn
opReturnKind' tspec typ (getOpName fn)
(do lv' <- auxFR lv -- OFuncReturn
return [ cAssignStmt lv' (cFuncCall (getSymbol fn) ex' )])
(do lv' <- auxFA lv -- OFuncRef
return [ cFuncCallStmt (getSymbol fn) (lv' : ex') ])
(do lv' <- auxFR lv -- OMacroReturn
(p, d) <- composedCase typ
return $ d ++ [ cAssignStmt lv' (cFuncCall (getSymbol fn) (ex' ++ p) )])
(do lv' <- auxM1 typ lv -- OMacroRef
(p, d) <- composedCase typ
return $ d ++ [ cFuncCallStmt (getSymbol fn) (lv' ++ ex' ++ p) ])
where
composedCase typ = if isComposed typ && (isCAssign fn || isCComp fn) then extractParams' tspec typ else return ([], [])
auxFR = return . mapLVal . head
{-
Note:
A macro "returns" a value in a different way than a function.
For instance, in macro(a, b) we can:
- make it return a value
#define macro(a,b) a+b
v = macro(a,b)
- return the value by 'a' (we call this by "reference", although this is not
completely correct):
#define macro(a,b) a=b
macro(v,b)
Thus, what we pass as "return" argument depends on if the type is used by value
or by reference.
However, there is a special case: when we have a reference to a type used by
value. In this case, we have to cast it to a pointer to the type and the use
an indirection to pass a value.
-}
auxM1 :: CaoMonad m => Type Var -> [LVal Var] -> m [CExpr]
auxM1 tp [LVVar (unLoc -> v)]
| isCRef v = do
tname <- typeName tspec tp
valOrRef tspec tp (return $ singleton . cIndirection) (return singleton) `apM` cPointerCast (tPrefix tspec tname) (cVar' v)
| otherwise = return [cVar' v]
auxM1 _ [LVStruct (LVVar (unLoc -> v)) fld] = return [mapProj v fld]
auxM1 _ _ = caoError defSrcLoc $ mkUnknownErr $ "Not expected function call result"
auxFA [LVVar (unLoc -> v)] = case varType v of
Bullet -> (return $ cVar $ getSymbol v)
t -> valOrRef tspec t
(return $ cExprAddr $ cVar $ getSymbol v)
(return $ cVar $ getSymbol v)
auxFA [LVStruct (LVVar (unLoc -> v)) fld] = case varType v of
Bullet ->
(return $ mapProj v fld)
t -> valOrRef tspec t
(return $ cExprAddr $ mapProj v fld)
(return $ mapProj v fld)
auxFA _ = caoError defSrcLoc $ mkUnknownErr $ "Not expected function call result"
-- TODO: Left values were tested againt Bullet type. This is a HACK and was
-- removed. However, some code that dependend on this hack may fail.
-- Call to a function
mapFunCall tspec lv (unLoc -> fn) args = do
ex' <- mapExps tspec args
(lv', re) <- auxLV lv
let call = cFuncCall (getSymbol fn) (re ++ ex')
return $ singleton $ maybe (cExprStmt call) (flip cAssignStmt call) lv'
where
auxLV :: CaoMonad m => [LVal Var] -> m (Maybe CExpr, [CExpr])
auxLV (r:rt) = do
(r', rt') <- if isCStruct' r
then return (Just $ mapLVal r, rt)
else valOrRefFuncReturn tspec (typeOf r) (return (Just $ mapLVal r, rt)) (return (Nothing, r:rt))
rt'' <- mapM auxFA rt'
return (r', rt'')
auxLV _ = caoError defSrcLoc $ mkUnknownErr $ "Not expected function call result"
auxFA :: CaoMonad m => LVal Var -> m CExpr
auxFA l = valOrRef tspec (typeOf l) (return cExprAddr) (return id) `apM` mapLVal l
--------------------------------------------------------------------------------
-- Constant declaration
mapConstDecl
:: CaoMonad m
=> TranslationSpec -> ConstDecl Var
-> m [CBlockItem]
mapConstDecl tspec c = case c of
ConstD (unLoc -> n) _ _ -> varMemory tspec n >> constDeclaration tspec n
_ -> caoError defSrcLoc $ mkUnknownErr $ ".:\
\ precondition violation:\n" ++ showPpr c
constDeclaration
:: CaoMonad m
=> TranslationSpec -> Var
-> m [CBlockItem]
constDeclaration tspec v = varOrMacroDecl tspec (varType v) auxVar auxMac
-- TODO: This definitions are equal to varDeclaration
where
auxVar = do
decl <- if isGlobalVar v then return [] else liftM (singleton . cVarDeclStmt (getSymbol v) . tPrefix tspec) . cTypeName tspec $ v
alloc <- autoOrAlloc tspec (varType v) (return []) (do
typ <- cTypeName tspec v
(args, d) <- extractParams tspec v
(targs, decl') <- cTypeCodeArgs tspec (typeOf v)
let fcall = fCall tspec typ code_decl
n = cVar' v
valOrRefOpReturn tspec (varType v) code_decl
(return (decl' ++ d ++ [ cAssignStmt n $ cFuncCall fcall $ args ++ targs ]))
(return (decl' ++ d ++ [ cFuncCallStmt fcall (cExprAddr n : args ++ targs) ])))
return $ decl ++ alloc
auxMac = do
tname <- cTypeName tspec v
(p, d) <- extractParams tspec v
let n = cVar' v
fdcall = fCall tspec tname code_decl
decl <- if isGlobalVar v then return [] else valOrRefOpMacroReturn tspec (varType v) code_decl
(caoError defSrcLoc $ mkUnknownErr $ ".:\
\ Not expecting macro variable declaration returning a value")
(return [ cFuncCallStmt fdcall (n : p) ])
let ficall = fCall tspec tname code_init_def
vini <- valOrRefOpMacroReturn tspec (varType v) code_init_def
(return [ cAssignStmt n $ cFuncCall ficall p ])
(return [ cFuncCallStmt ficall (n : p) ])
return (d ++ decl ++ vini)
--------------------------------------------------------------------------------
-- Variable declaration
mapVarDecl
:: CaoMonad m
=> TranslationSpec -> VarDecl Var
-> m [CBlockItem]
mapVarDecl tspec v = case v of
VarD (unLoc -> n) _ Nothing -> varMemory tspec n >> varDeclaration tspec n
ContD (unLoc -> n) _ e -> varMemory tspec n >> varDeclInit tspec n e
_ -> caoError defSrcLoc $ mkUnknownErr $ ".:\
\ precondition violation:\n" ++ showPpr v
varDeclaration
:: CaoMonad m
=> TranslationSpec -> Var
-> m [CBlockItem]
varDeclaration tspec v
| isCRef v = return [ cVarDeclStmt (getSymbol v) (tPrefix tspec caoRef) ]
| isCStruct v = return [ cVarDeclStmt (getSymbol v) (tPrefix tspec (getTName v)) ]
| otherwise = varOrMacroDecl tspec (varType v) auxVar auxMacro
where
auxVar = do
decl <- if isGlobalVar v then return [] else liftM (singleton . cVarDeclStmt (getSymbol v) . tPrefix tspec) . cTypeName tspec $ v
alloc <- autoOrAlloc tspec (varType v) (return []) (do
typ <- cTypeName tspec v
(args, d) <- extractParams tspec v
(targs, decl') <- cTypeCodeArgs tspec (typeOf v)
let fcall = fCall tspec typ code_decl
n = cVar' v
valOrRefOpReturn tspec (varType v) code_decl
(return (decl' ++ d ++ [ cAssignStmt n $ cFuncCall fcall $ args ++ targs ]))
(return (decl' ++ d ++ [ cFuncCallStmt fcall (cExprAddr n : args ++ targs) ])))
return $ decl ++ alloc
auxMacro = do
tname <- cTypeName tspec v
(p, d) <- extractParams tspec v
let n = cVar' v
fdcall = fCall tspec tname code_decl
decl <- if isGlobalVar v then return [] else valOrRefOpMacroReturn tspec (varType v) code_decl
(caoError defSrcLoc $ mkUnknownErr $ ".:\
\ Not expecting macro variable declaration returning a value")
(return [ cFuncCallStmt fdcall (n : p) ])
let ficall = fCall tspec tname code_init_def
vini <- valOrRefOpMacroReturn tspec (varType v) code_init_def
(return [ cAssignStmt n $ cFuncCall ficall p ])
(return [ cFuncCallStmt ficall (n : p) ])
return (d ++ decl ++ vini)
varDeclInit
:: CaoMonad m
=> TranslationSpec -> Var -> [TLExpr Var]
-> m [CBlockItem]
varDeclInit tspec v exps = do
let typ = varType v
intyp = head $ innerType typ
(_, constArray) <- freshSmb
tname <- typeName tspec typ
vdecl <- varDeclaration tspec v
exps' <- mapExps tspec exps
cdecl <- autoOrAlloc tspec intyp
(return $ cIntArrayDecl constArray exps')
(return $ cCharArrayDecl constArray exps')
let fcall = fCall tspec tname code_init
adecl <- opReturnKind' tspec typ code_init
(return [ cAssignStmt (cVar' v) $ cFuncCall fcall [cVar constArray] ])
(return [ cFuncCallStmt fcall [cVar' v, cVar constArray ] ])
(do (p, d) <- extractParams' tspec typ
return $ d ++ [ cAssignStmt (cVar' v) $ cFuncCall fcall $ [cVar constArray] ++ p])
(do (p, d) <- extractParams' tspec typ
return $ d ++ [ cFuncCallStmt fcall $ [cVar' v, cVar constArray] ++ p])
return $ cdecl : vdecl ++ adecl
--------------------------------------------------------------------------------
-------------------------------------------- Exp -------------------------------
mapExps
:: CaoMonad m
=> TranslationSpec -> [TLExpr Var]
-> m [CExpr]
mapExps tspec = mapM (mapExp tspec)
mapExp
:: CaoMonad m
=> TranslationSpec -> TLExpr Var
-> m CExpr
mapExp tspec e = case unTyp $ unLoc e of
Lit l -> mapLiteral tspec (typeOf e) l
Var v -> return $ cVar' v
StructProj (unLoc -> unTyp -> Var v) fld -> return $ mapProj v fld
_ -> internalError
"mapExp" "Not expected case"
--------------------------------------------------------------------------------
-- Left values
mapLVal
:: LVal Var
-> CExpr
mapLVal (LVVar (unLoc -> v)) = cVar' v
mapLVal (LVStruct (LVVar (unLoc -> v)) fld) = mapProj v fld
mapLVal _ = internalError "mapLVal" "Not expected case."
mapProj :: Var -> Var -> CExpr
mapProj v fld = CMember (cVar' v) (internalIdent (getSymbol fld)) False undefNode
--------------------------------------------------------------------------------
------------------------------ Literals ----------------------------------------
mapLiteral
:: CaoMonad m
=> TranslationSpec -> Type Var -> Literal Var
-> m CExpr
mapLiteral tspec i l = case l of
BLit b -> let
b' = mapBoolLiteral b
in autoOrAlloc tspec i
(return $ cIntExpr b')
(return $ cStringExpr $ show b')
ILit v ->
autoOrAlloc tspec i
(return $ cIntExpr v)
(return $ cStringExpr $ show v)
BSLit s bits -> let
v = mapBitString s bits
in autoOrAlloc tspec i
(return $ cIntExpr v)
(return $ cStringExpr $ show v)
PLit p ->
autoOrAlloc tspec i
(return $ cIntExpr $ mapSimplePolynomial p)
(return $ cStringExpr $ showMonomials $ mapPolynomial p)
mapBoolLiteral :: Bool -> Integer
mapBoolLiteral b = if b then cTrueValue else cFalseValue
mapBitString :: Sign -> [Bool] -> Integer
mapBitString s bs = case s of
U -> ubitsToInteger bs
S -> sbitsToInteger bs
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------- Polynomial ---------------------------------------
mapPolynomial :: Pol Var -> [IExpr Var]
mapPolynomial p = case p of
Pol [Mon (CoefI i) EZero] -> [i]
Pol mlst -> mapMonomials mlst
mapSimplePolynomial :: Pol Var -> Integer
mapSimplePolynomial p = case p of
Pol [Mon (CoefI (IInt i)) EZero] -> i
Pol [Mon (CoefI _) EZero] -> internalError "mapSimplePolynomial" "<>: non literal"
Pol _ -> internalError "mapSimplePolynomial" "Unexpected polynomial literal"
--------------------------------------------------------------------------------
----------------------------- Monomials ----------------------------------------
showMonomials :: [IExpr Var] -> String
showMonomials = intercalate ";" . map (show . getInteger)
getInteger :: IExpr Var -> Integer
getInteger (IInt n) = n
getInteger _ = internalError "getInteger" "Not expected non-literal"
mapMonomials :: [Mon Var] -> [IExpr Var]
mapMonomials lm = case lm of
[] -> error ".: precondition violation: empty list"
Mon (CoefP _) _: _ -> error $ ".:\n"
++ concatMap showPpr lm
Mon (CoefI _) _: _ ->
uncurry mapMonomial $ split (getMonExp . head) id lm
-- Invariant: The list of monomials is ordered by decresing degree
mapMonomial :: Integer -> [Mon Var] -> [IExpr Var]
mapMonomial 0 [] = IInt 0 : []
mapMonomial 0 (Mon (CoefI i) _ : _) = i : []
mapMonomial n [] = IInt 0 : mapMonomial (n-1) []
mapMonomial n ml@(Mon (CoefI _) EZero : _) = IInt 0 : mapMonomial (n-1) ml
mapMonomial n ml@(Mon (CoefI icoef) (MExpI _ n'): mlst)
| n == n' = icoef : mapMonomial (n-1) mlst
| otherwise = IInt 0 : mapMonomial (n-1) ml
mapMonomial _ _ = internalError "mapMonomial" "Not expected case"
--------------------------------------------------------------------------------
--
varMemory :: CaoMonad m => TranslationSpec -> Var -> m ()
varMemory tspec v
| isCStruct v || isCRef v || isGlobalVar v = return ()
| otherwise = autoOrAlloc tspec (varType v) (return ()) (storeAllocVar v)
-- Wrapper for creating C declarations from pairs string/type
cArgs :: CaoMonad m =>
TranslationSpec -> String -> TyDecl Var -> Type Var -> m CDecl
cArgs tspec nm (TySynD n) _ =
return $ cParamDecl nm $ cType (tPrefix tspec $ getSymbol (unLoc n))
cArgs tspec nm _ typ = liftM (cParamDecl nm . snd) (mapType tspec typ)
--------------------------------------------------------------------------------
-- Auxiliary functions
cVar' :: Var -> CExpr
cVar' = cVar . getSymbol
mapIndex :: IExpr Var -> CExpr
mapIndex (IInt n) = cIntExpr n
mapIndex (IInd v) = cVar' v
mapIndex _ = internalError "mapIndex" "Not expected index."
--------------------------------------------------------------------------------
cTypeCodeArgs :: CaoMonad m => TranslationSpec -> Type Var -> m ([CExpr], [CBlockItem])
cTypeCodeArgs tspec i
| isSimpleType i = return ([], [])
| otherwise = do
let i' = innerType i
ctc <- concatMapM (cTypeCode tspec) i'
(ilst, d) <- concatMapAndUnzipM (cTypeParams tspec) i'
(param, decl) <- if null ilst
then return ([ cIntExpr 0 ], [])
else do
(_, paramArray) <- freshSmb
let decl = cPointerArrayDecl paramArray ilst
return ([ cVar paramArray ], [decl])
return (cStringExpr ctc : param, d ++ decl)
---------------------------------------- CAOType -------------------------------
cTypeCode :: CaoMonad m => TranslationSpec -> Type Var -> m String
cTypeCode tspec = cTypeCode'
where
cTypeCode' :: CaoMonad m => Type Var -> m String
cTypeCode' t = do
c <- codes tspec t
c' <- case t of
Int -> return []
RInt -> return []
Bool -> return []
Bits _ _ -> return []
Mod Nothing Nothing (Pol [Mon (CoefI _) EZero]) -> return []
Mod (Just b) _ _ ->
if isModInt b
then return []
else caoError defSrcLoc $ NestedModpolErr t
Vector _ t' -> cTypeCode' t'
Matrix _ _ t' -> cTypeCode' t'
Struct _ flds -> concatMapM (cTypeCode' . snd) flds
_ -> caoError defSrcLoc $ NotSupportedTypeErr t
return $ c ++ c'
cTypeParams :: CaoMonad m => TranslationSpec -> Type Var -> m ([CExpr], [CBlockItem])
cTypeParams tspec = worker
where
worker :: CaoMonad m => Type Var -> m ([CExpr], [CBlockItem])
worker t = do
i <- cTypeCode' t
concatMap2M aux i
aux [] = return (cIntExpr 0, [])
aux [IInd v] = do
v' <- valOrRef tspec (varType v) (return cExprAddr) (return id)
`apM` cVar' v
return (v', [])
aux [IInt n] = do
(_, paramArray) <- freshSmb
ctc <- typeName tspec RInt
let decl = cTypeArrayDecl paramArray (tPrefix tspec ctc) [ cIntExpr n ]
return ( cVar paramArray, [decl] )
aux [i, j] = do
(_, paramArray) <- freshSmb
ctc <- typeName tspec RInt
let a = mapIndex i : mapIndex j : []
decl = cTypeArrayDecl paramArray (tPrefix tspec ctc) a
return ( cVar paramArray, [decl])
aux _ = internalError "cTypeCode.aux" "Not expected value"
cTypeCode' :: CaoMonad m => Type Var -> m [[IExpr Var]]
cTypeCode' t =
case t of
Int -> return [[]]
RInt -> return [[]]
Bool -> return [[]]
Bits _ n -> return [[n]]
Mod Nothing Nothing (Pol [Mon (CoefI m) EZero]) -> return [[m]]
Mod (Just b) _ _ ->
if isModInt b
then return [[]]
else caoError defSrcLoc $ NestedModpolErr t
Vector n t' -> do
ilst <- cTypeCode' t'
return ([n] : ilst)
Matrix n m t' -> do
ilst <- cTypeCode' t'
return ([n , m] : ilst)
Struct _ flds -> do
liftM ([IInt $ genericLength flds] : ) $ concatMapM (cTypeCode' . snd) flds
_ -> caoError defSrcLoc $ NotSupportedTypeErr t
extractParams :: CaoMonad m => TranslationSpec -> Var -> m ([CExpr], [CBlockItem])
extractParams tspec = extractParams' tspec . varType
extractParams' :: CaoMonad m => TranslationSpec -> Type Var -> m ([CExpr], [CBlockItem])
extractParams' tspec i = case i of
Bits _ m ->
return (mapIndex m : [], [])
Vector m _ -> do
return (mapIndex m : [], [])
Matrix m n _ -> do
return (mapIndex m : mapIndex n : [], [])
Mod Nothing Nothing (Pol [Mon (CoefI m) EZero]) -> do
return (mapIndex m : [], [])
Mod (Just (Mod Nothing Nothing (Pol [Mon (CoefI m) EZero]))) (Just _) pol
-> polyParams tspec m pol
Mod (Just _) (Just _) _
-> caoError defSrcLoc $ NestedModpolErr i
Struct _ flds -> return ([cIntExpr $ genericLength flds], [])
_ -> return ([], [])
polyParams :: CaoMonad m => TranslationSpec -> IExpr Var -> Pol Var -> m ([CExpr], [CBlockItem])
polyParams tspec m pol = do
(_, paramArray) <- freshSmb
ctc <- typeName tspec Int
let m' = mapIndex m
pol' = map mapIndex (mapPolynomial pol)
decl = cTypeArrayDecl paramArray (tPrefix tspec ctc) pol'
return ([ dg (degree pol), m', cVar paramArray ], [decl])
--return ([ cIntExpr (degree pol), m', cVar paramArray ], [decl])
where
-- XXX: This should be improved since it may not work in all cases
dg n = cVar $ "c_const_int_" ++ show n
--------------------------------------------------------------------------------
isComposed :: Type Var -> Bool
isComposed t = isVector t || isMatrix t
cTypeCodeRedux :: CaoMonad m => TranslationSpec -> Type Var -> m Char
cTypeCodeRedux tspec = liftM head . codes tspec
isCStruct' :: LVal Var -> Bool
isCStruct' (LVVar (unLoc -> v)) = isCStruct v
isCStruct' (LVStruct (LVVar (unLoc -> v)) _) = isCStruct v
isCStruct' _ = error "Not expected function call result"
isCStructExpr :: Expr Var -> Bool
isCStructExpr (Var e) = isCStruct e
isCStructExpr _ = False
-- Translation of type names
cTypeName :: CaoMonad m => TranslationSpec -> Var -> m String
cTypeName tspec = typeName tspec . varType
cProc :: TranslationSpec -> String -> [CBlockItem] -> CFunDef
cProc tspec name body = cFuncDefinition name [] (tPrefix tspec caoRes) $
CCompound [] (body ++ cReturn caoOk : []) undefNode
tPrefix :: TranslationSpec -> String -> String
tPrefix tspec = ((typePrefix tspec ++ "_") ++)
moduleName :: String
moduleName = ""
internalError :: String -> String -> a
internalError funcName msg = error $
moduleName ++ ".<" ++ funcName ++ ">: " ++ msg