{- 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 PatternGuards #-} {- | Module : $Header$ Description : CAO to C pre-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 After handling constants, the next step is to make the CAO code as close as possible to the final C code. In more detail, the following actions are performed: * All CAO native operations are replaced by a call to a function with the same name as the respective implementation in the static library. For instance, the following CAO code, where 'a', 'b' and 'r' are integer variables @r := a + b;@ is transformed to @r := CAO_int_add(a, b);@ In particular, the called name follows the convention: @{prefix} {type name} {operation name}(argument list)@ * Accesses to vectors or matrices are replaced by extraction of references. Although CAO does not support references, this is completely transparent since these are treated as a special kind of variable. For instance, the following assignment to a vector v of integers, where n is an integer @v[0] := n;@ is transformed to @ def t : REF; t := CAO_vector_ref(v, 0); t := CAO_int_assign(n); @ In the final translation step, the variable t will be translated to a real reference. * Operation and function arguments are handled according to the specification of safety, as explained in the previous section. If an operation in specified as unsafe, a copy of its arguments is generated. If an operation is argument safe, a copy of the argument used as result is generated as in the case @a := (a,b)@. This assumes that the compiler does not introduce aliasing and that variables can be distinguished by their name. The introduction of references described above in not a problem since it always references a region of a container and cannot be mixed with other references. We should notice that the implementation of the init and assign operations in the library must be safe. Otherwise, a copy would not solve the problem as these are the operations used to copy values. Also, ref operations must be safe since extracting a reference should not change its argument. * Calls to structure fields are replaced by integers accordingly with the specification. For instance, let us consider the following structure in CAO: @ typedef S := struct [ def n : int; def b : bool; ]; @ and that we have the following code: @ def i : int; i := s.n; s.b := true; @ The inlining option makes the integer constant to be used directly in the selection functions: @ c_i := CAO_struct_select(c_s, 0); c_t := CAO_bool_init(true); c_t50 := CAO_struct_ref(c_s, 1); c_t50 := CAO_bool_assign(c_t); @ In this case, the field n was replaced by 0 and the field b was replaced by 1. The global variable option replaces the call by a global variable: @ c_i := CAO_struct_select(c_s, c_n); c_t52 := CAO_struct_ref(c_s, c_b); c_t52 := CAO_bool_assign(c_const_true); @ which is initialized in the global init procedure: @ def init() : void { c_n := CAO_int_init(0); c_b := CAO_int_init(1); c_const_true := CAO_bool_init(true); } @ * The specification allows us to determine if values should be returned by value or by reference. The language also allows us to return several results simultaneously. The chosen mechanism for returning several values or references were C structures. For instance, let us consider the swap function which returns two integer, and that integers in this platform are returned by value: @ def swap(a : int, b : int) : int, int { return b, a; } @ In this phase, a structure to return the two integers is generated using CAO syntax. However, this will be directly mapped to C structures since it is not a user defined CAO structure. @ typedef sRes55 := struct[def c_sRes55_0 : int; def c_sRes55_1 : int;]; def c_swap(c_a : int, c_b : int) : sRes55 { def c_CAO_t58 : sRes55; c_CAO_t58.c_sRes55_0 := CAO_int_assign(c_b); c_CAO_t58.c_sRes55_1 := CAO_int_assign(c_a); return c_CAO_t58; } @ A call to the swap function: @ n1, n2 := swap(m1, m2); @ is transformed to @ def c_CAO_t65 : sRes59; c_CAO_t65 := c_swap(c_m1, c_m2); c_n1 := CAO_int_assign(c_CAO_t65.c_sRes59_0); c_n2 := CAO_int_assign(c_CAO_t65.c_sRes59_1); @ -} module Language.CAO.Translation.PreC ( cao2prec , precStatement , precAssignment ) where import Control.Monad import Data.List (partition) import Language.CAO.Common.Fresh import Language.CAO.Common.Literal import Language.CAO.Common.Monad import Language.CAO.Common.SrcLoc import Language.CAO.Common.State import Language.CAO.Common.Utils import Language.CAO.Common.Var import Language.CAO.Platform.Literals import Language.CAO.Platform.Naming import Language.CAO.Platform.Query import Language.CAO.Platform.Specification import Language.CAO.Syntax import Language.CAO.Syntax.Utils (type2TyDecl, typeOf, Typeable) import Language.CAO.Translation.Names import Language.CAO.Type import Language.CAO.Type.Utils -- This module should: -- * Replace CAO operations by calls to functions with the same name as in the backend -- * Handle operation and function arguments accordingly with the specification of safety, -- making copies whenever necessary -- *_init and *_assign have to be safe. Otherwise, a copy would not solve the problem. -- * Distinguish between values returned by value or by reference and create the necessary -- structure declarations -- * Replace calls to struct fields by integers -- * ??? Replace CAO types by backend types??? -- Safe operations by design: -- *_init -- *_assign -- *_ref -------------------------------------------------------------------------------- -- CaoAST cao2prec :: CaoMonad m => TranslationSpec -> Prog Var -> m (Prog Var) cao2prec tspec (Prog defs (Just ip)) = withPreCST $ do (defs', fldDefs) <- concatMapAndUnzipM (precDefinition tspec) defs -- The init procedure does not return any value, thus it is safe to ignore -- the second value of 'precFunc' (ip', _) <- precFunc tspec ip return $ Prog defs' (Just (aux fldDefs ip')) where aux :: [FieldDef] -> Fun Var -> Fun Var aux s f = f { funBody = s ++ funBody f } cao2prec _ _ = internalError "cao2prec" "No init function" -------------------------------------------------------------------------------- precDefinition :: CaoMonad m => TranslationSpec -> LDef Var -> m ([LDef Var], [FieldDef]) precDefinition tspec (L l d) = case d of VarDef _ -> return (L l d : [], []) FunDef fd -> do (fd', structDecl) <- precFunc tspec fd return (consMaybe structDecl $ L l (FunDef fd') : [], []) TyDef td -> precTypeDef tspec l td ConstDef _ -> return (L l d : [], []) -------------------------------------------------------------------------------- type FieldDef = LStmt Var type StructResDecl = LDef Var precTypeDef :: CaoMonad m => TranslationSpec -> SrcLoc -> TyDef Var -> m ([LDef Var], [FieldDef]) precTypeDef tspec l (StructDecl sname lFlds) = liftM (mapFst (L l (TyDef $ TySynDef sname $ TySynD sname) : )) $ globalOrInlinedField tspec (liftM unzip $ zipWithSeqM auxGlobal lFlds) (liftM (const ([], [])) $ zipWithSeqM auxInlined lFlds) where auxGlobal :: CaoMonad m => Integer -> (Located Var, TyDecl Var) -> m (LDef Var, FieldDef) auxGlobal fldIndex (nm, _) = do nm' <- liftM (L (getLoc nm)) $ freshVar' Global (getSymbol (unLoc nm)) RInt let decl = L l $ VarDef $ VarD nm' (type2TyDecl RInt) Nothing assign <- fCallSAux tspec code_init (LVVar nm') [rintLit fldIndex] return (decl, genLoc assign) auxInlined :: CaoMonad m => Integer -> (Located Var, TyDecl Var) -> m () auxInlined fldIndex (nm, _) = putFieldProj (unLoc nm, fldIndex) precTypeDef _ l t = return ([L l $ TyDef t], []) -------------------------------------------------------------------------------- precFunc :: CaoMonad m => TranslationSpec -> Fun Var -> m (Fun Var, Maybe StructResDecl) precFunc tspec (Fun (L loc fn) args _ body) = do resetPreCST let typ@(FuncSig ta _ c) = varType fn (rtype, sdef, rsdef) <- precReturnType tspec typ let retD' = map type2TyDecl rtype typ' = FuncSig ta (Tuple rtype) c fn' = L loc $ setType typ' fn putFunType fn typ' body' <- precBlocks tspec body refV <- getRefVar tmpvs <- getTmpVars refV' <- mapMaybeM refVarDecl refV let body'' = insertTmps body' tmpvs return (Fun fn' args retD' (consMaybe refV' (consMaybe rsdef body'')), sdef) where refVarDecl v = do t <- freshVar' Global caoRef Bullet return $ genLoc $ VDecl $ VarD (genLoc v) (TySynD (genLoc t)) Nothing insertTmps bd tmpvs = let (dep, noDep) = partition (isDependent . varType) tmpvs in insertAll (map varDecl' noDep) (map varDecl' dep) bd varDecl' = genLoc . VDecl . varDecl insertAll nodeps deps [] = nodeps ++ deps insertAll [] deps (L l (Nop EndIndex) : sts) = L l (Nop EndIndex) : deps ++ sts insertAll _ _ (L _ (Nop EndIndex) : _) = internalError "insertAll" "Not expected order" insertAll nodeps deps (L l (Nop EndConsts) : sts) = L l (Nop EndConsts) : nodeps ++ insertAll [] deps sts insertAll nodeps deps (s : sts) = s : insertAll nodeps deps sts precReturnType :: CaoMonad m => TranslationSpec -> Type Var -> m ([Type Var], Maybe StructResDecl, Maybe ReturnStructDecl) precReturnType tspec (FuncSig _ (fromTuple -> rtype) _) = do (vtyp, rtyp) <- returnByValOrRef tspec rtype (vtyp', sdecl, rsdecl) <- precByVal vtyp return (consMaybe vtyp' rtyp, sdecl, rsdecl) where precByVal :: CaoMonad m => [Type Var] -> m (Maybe (Type Var), Maybe StructResDecl, Maybe ReturnStructDecl) precByVal [] = return (Nothing, Nothing, Nothing) precByVal [t] = valOrRef tspec t (return (Just t, Nothing, Nothing)) -- Single value variable returned directly (returnStruct =<< newStructRes [t]) precByVal typs = returnStruct =<< newStructRes typs returnStruct (t, sd) = do (fv, sdecl) <- returnStructDecl tspec t putRetStruct fv return (Just t, Just sd, Just sdecl) precReturnType _ _ = internalError "precReturnType" "Unexpected function type." -------------------------------------------------------------------------------- -- Handling structs to return results of functions type ReturnStructDecl = LStmt Var -- Returns a new struct with a field of each given typ newStructRes :: CaoMonad m => [Type Var] -> m (Type Var, StructResDecl) newStructRes typs = do uid <- uniqId let tname = structRes ++ show uid sname = mkGId (mkTvName tname) uid Bullet sflds <- zipWithSeqM (newStructField tname sname) typs let struct = Struct sname sflds return ( struct , genLoc $ TyDef $ StructDecl (genLoc sname) (map (mapPair genLoc type2TyDecl) sflds)) where newStructField tname sname n typ = do fld <- freshSFld (tname ++ "_" ++ show n) (SField sname typ) return (fld, typ) -- Declares a new struct variable to return results returnStructDecl :: CaoMonad m => TranslationSpec -> Type Var -> m (Var, ReturnStructDecl) returnStructDecl tspec typ@(Struct sname _) = do (i, sn) <- freshSmb let fv = mkCStruct sn i typ (typePrefix tspec) (getSymbol sname) let decl = genLoc $ VDecl $ VarD (genLoc fv) (type2TyDecl typ) Nothing return (fv, decl) returnStructDecl _ _ = internalError "returnStructDecl" "Not expected case" -------------------------------------------------------------------------------- precBlocks :: CaoMonad m => TranslationSpec -> [LStmt Var] -> m [LStmt Var] precBlocks tspec = concatMapM (precStatement tspec) precStatement :: CaoMonad m => TranslationSpec -> LStmt Var -> m [LStmt Var] precStatement tspec (L l (VDecl vd)) = liftM (singleton . L l . VDecl) $ precVDecl tspec vd -- TODO: Constant declaration must be processed because of the change in Target precStatement tspec (L l (CDecl cd)) = do (cd', stmt) <- precCDecl tspec cd return (L l (CDecl cd') : stmt) precStatement tspec (L l (Assign lv [L l' (TyE _ (FunCall (L lf fn) args))])) = do Just ftyp <- getFunType fn let fn' = L lf $ setType ftyp fn (lv', decl, assign) <- precReturnLVal tspec lv ftyp (args', stmts) <- safetyCopy tspec lv args return $ decl ++ stmts ++ L l (Assign lv' [L l' (annTyE ftyp (FunCall fn' args'))]) : assign precStatement tspec (L l (Assign [lv] [ex])) = precAssignment tspec l lv ex precStatement _ (L _ (Assign _ _)) = internalError "precStatement" "Unexpected assignment case" precStatement tspec (L l (FCallS pn ex)) = do (ex', stmts) <- safeOrUnsafeDefault tspec (return (ex, [])) (genUnsafeCopy tspec ex) (return (ex, [])) --- There is no need to make a copy to make it arg -- safe, because there is no result assignment since this is a -- procedure return $ stmts ++ [ L l $ FCallS pn ex' ] precStatement tspec (L l (Ret exps)) = do precReturn tspec l exps precStatement tspec (L l (Ite ex ifBlock eBlock)) = do ifBlock' <- precBlocks tspec ifBlock eBlock' <- mapMaybeM (precBlocks tspec) eBlock return [ L l $ Ite ex ifBlock' eBlock' ] precStatement tspec (L l (While ex whileBlock)) = do whileBlock' <- precBlocks tspec whileBlock return [ L l $ While ex whileBlock' ] precStatement tspec (L l (Seq i seqBlock)) = do seqBlock' <- precBlocks tspec seqBlock return [ L l $ Seq i seqBlock' ] precStatement _ (L l (Nop a)) = return [L l (Nop a)] {- Note The translation of 'if' and 'while' statements will use native C support. Therefore, it is assumed that these operations are safe and do not need previous copy of the values. -} -------------------------------------------------------------------------------- precReturnLVal :: CaoMonad m => TranslationSpec -> [LVal Var] -> Type Var -> m ([LVal Var], [ReturnStructDecl], [LStmt Var]) precReturnLVal tspec lvs (FuncSig _ (Tuple (ht:_)) _) = do (vlv, rlv) <- returnByValOrRef tspec lvs if null vlv then return (rlv, [], []) else case ht of typ@(Struct _ flds) -> do (fv, decl) <- returnStructDecl tspec typ assign <- zipWithM (aux fv) vlv flds return (LVVar (genLoc fv) : rlv, [decl], assign) _ -> return (vlv ++ rlv, [], []) where aux fv lv (fld, _) = liftM genLoc $ fCallSAux tspec code_init lv [genLoc $ annTyE (varType fld) $ StructProj (genLoc (annTyE (varType fv) (Var fv))) fld] precReturnLVal _ _ _ = internalError "precReturnLVal" "Unexpected case." precReturn :: CaoMonad m => TranslationSpec -> SrcLoc -> [TLExpr Var] -> m [LStmt Var] precReturn tspec loc exps = do (val, ref) <- returnByValOrRef tspec exps (val', stmt) <- precByVal val return $ stmt ++ [ L loc $ Ret $ val' `consMaybe` ref ] where precByVal :: CaoMonad m => [TLExpr Var] -> m (Maybe (TLExpr Var), [LStmt Var]) precByVal [] = return (Nothing, []) precByVal [v] = valOrRef tspec (typeOf v) (return (Just v, [])) -- Single value variable returned directly (liftM wrap $ precByVal' [v]) precByVal vals = liftM wrap $ precByVal' vals wrap (a, b) = (Just a, b) precByVal' :: CaoMonad m => [TLExpr Var] -> m (TLExpr Var, [LStmt Var]) precByVal' vals = do Just fv <- getRetStruct let Struct _ flds = varType fv block <- zipWithM (\ (fld, _) ex -> do sfld <- freshSFld (getSymbol fld) (varType fld) case unLoc ex of -- Init is safe because it takes constants TyE t (Lit l) -> do l' <- precLiteral tspec t l liftM genLoc $ fCallSAux tspec code_init (LVStruct (LVVar (genLoc fv)) sfld) (map (L (getLoc ex) . TyE t . Lit) l') -- Assign must be safe, otherwise the system would not be safe TyE _ (Var _) -> liftM genLoc $ fCallSAux tspec code_assign (LVStruct (LVVar (genLoc fv)) sfld) [ex] _ -> error "Not expected" ) flds vals return (genLoc $ annTyE (varType fv) $ Var fv, block) -------------------------------------------------------------------------------- precAssignment :: CaoMonad m => TranslationSpec -> SrcLoc -> LVal Var -> TLExpr Var -> m [LStmt Var] precAssignment tspec loc lv ex = case lv of LVVar (unLoc -> vid) -> assignSimpleLVal vid LVStruct lv' fld -> do fld' <- precField tspec fld precSimpleAssign tspec loc lv' [ex, fld'] LVCont _ lv' (VectP (CElem iexp)) -> precSimpleAssign tspec loc lv' [ex, iexp] LVCont _ lv' (VectP (CRange iexp jexp)) -> precRangeAssign tspec loc code_range_set lv' [ex, iexp, jexp] LVCont _ lv' (MatP (CElem ce) (CElem re)) -> precSimpleAssign tspec loc lv' [ex, ce, re] LVCont _ lv' (MatP (CRange ci cj) (CRange ri rj)) -> precRangeAssign tspec loc code_range_set lv' [ex, ci, cj, ri, rj] LVCont _ lv' (MatP (CRange lre rre) (CElem cole)) -> precRangeAssign tspec loc code_row_range_set lv' [ex, cole, lre, rre] LVCont _ lv' (MatP (CElem rowe) (CRange lce rce)) -> precRangeAssign tspec loc code_col_range_set lv' [ex, rowe, lce, rce] where assignSimpleLVal vid = case unLoc ex of TyE t (Lit l) -> do l' <- precLiteral tspec t l let ex' = map (L (getLoc ex) . TyE t . Lit) l' let typ = typeOf ex n <- cCall tspec code_init typ return $ L loc (Assign [lv] [genLoc $ annTyE typ $ FunCall (genLoc n) ex' ]) : [] TyE _ (Var _) -> do let typ = typeOf ex n <- cCall tspec code_assign typ return $ L loc (Assign [lv] [genLoc $ annTyE typ $ FunCall (genLoc n) [ex] ]) : [] TyE td (Cast _ _ ce) -> assignCast vid td ce TyE _ (StructProj s f) -> do f' <- precField tspec f assignGeneral vid [s, f'] _ -> assignGeneral vid (getArgExps ex) assignCast vid td ce = do let typ = typeOf ce n <- liftM (L (getLoc ex)) $ cCastCall tspec typ td (ce', stmts) <- safetyCopy' tspec typ code_cast [vid] [ce] return $ stmts ++ [ L loc $ Assign [lv] [L (getLoc ex) $ annTyE td $ FunCall n ce'] ] assignGeneral vid args = do let fcode = codeOf $ unTyp $ unLoc ex typ = typeOf $ head args n <- cCall tspec fcode typ (args', stmts) <- safetyCopy' tspec typ fcode [vid] args return $ stmts ++ [ L loc $ Assign [lv] [L (getLoc ex) $ annTyE (typeOf lv) $ FunCall (genLoc n) args' ] ] {- Note Some operations have of the backend must be safe. Otherwise it would not be possible to ensure the safety of the translation. These operations are: * initialization -> code_init * assignment -> code_assign * reference extraction -> code_ref * global references -> cGlobalRef -} -- Precondition: the list is not empty precRangeAssign :: CaoMonad m => TranslationSpec -> SrcLoc -> OpCode -> LVal Var -> [TLExpr Var] -> m [LStmt Var] precRangeAssign tspec loc op lv exps = do (root, path) <- precLValue tspec lv if null path then simpleLVal root else composedLVal root path where simpleLVal (L rl root) = do n1 <- cCall tspec op (typeOf lv) (exps'', stmts) <- safetyCopy' tspec (typeOf lv) op [root] exps return $ stmts ++ assign (L rl root) n1 exps'' : [] composedLVal (L rl root) path = do fv <- getRefVariable let n1 = cGlobalRef (typePrefix tspec) -- [See Note] n2 <- cCall tspec op (typeOf lv) (exps', stmts) <- safeOfUnsafe tspec (typeOf lv) op (return (exps, [])) (genUnsafeCopy tspec exps) (return (exps, [])) -- TODO: Is this a bug? return $ stmts ++ assign (genLoc fv) n1 (L rl (annTyE (varType root) (Var root)) : path ) : assign (genLoc fv) n2 exps' : [] assign l fn exps' = L loc $ Assign [LVVar l] [genLoc $ annTyE (typeOf l) $ FunCall (genLoc fn) exps'] -- Precondition: the list is not empty precSimpleAssign :: CaoMonad m => TranslationSpec -> SrcLoc -> LVal Var -> [TLExpr Var] -> m [LStmt Var] precSimpleAssign tspec loc lv exps@(ex:exps') = do (root, path) <- precLValue tspec lv if null path then simpleLVal root else composedLVal root path where simpleLVal (L rl root) = bitsCase (typeOf lv) (auxBits rl root) (do fv <- getRefVariable n1 <- cCall tspec code_ref (typeOf lv) -- [See Note] let c1 = assign (genLoc fv) n1 $ L rl (annTyE (varType root) (Var root)) : exps' c2 <- auxGen fv return $ c1 : c2 : [] ) composedLVal (L rl root) path = do fv <- getRefVariable let n1 = cGlobalRef (typePrefix tspec) -- [See Note] bitsCase (typeOf lv) (do let c1 = assign (genLoc fv) n1 $ L rl (annTyE (varType root) (Var root)) : path c2 <- auxBits rl fv return $ c1 : c2) (do let c1 = assign (genLoc fv) n1 $ L rl (annTyE (varType root) (Var root)) : path ++ exps' c2 <- auxGen fv return $ c1 : c2 : []) auxBits rl lv' = do n2 <- cCall tspec code_set (typeOf lv) (exps'', stmts) <- safetyCopy' tspec (typeOf lv) code_set [lv'] exps return $ stmts ++ assign (L rl lv') n2 exps'' : [] -- [See Note] auxGen fv = do (n2, ex') <- case unLoc ex of TyE ty (Lit l) -> do nn <- cCall tspec code_init ty l' <- precLiteral tspec ty l return (nn, map (L (getLoc ex) . TyE ty . Lit) l') TyE _ (Var v) -> do let ty = varType v nn <- cCall tspec code_assign ty return (nn, [ex]) _ -> error "precGenericAssign" return $ assign (genLoc fv) n2 ex' assign l fn exs = L loc $ Assign [LVVar l] [genLoc $ annTyE (typeOf l) $ FunCall (genLoc fn) exs] precSimpleAssign _ _ _ _ = internalError "precSimpleAssign" "Not expected" -------------------------------------------------------------------------------- -- This function takes as left value and returns its inner variable, -- together with the complete path from the variable to the value. precLValue :: CaoMonad m => TranslationSpec -> LVal Var -> m (Located Var, [TLExpr Var]) precLValue _ (LVVar (L l vid)) = return (L l vid, []) precLValue tspec (LVStruct lv fld) = do (e, lres) <- precLValue tspec lv fld' <- precField tspec fld return (e, lres ++ [fld']) precLValue tspec (LVCont _ lv (VectP (CElem iexp))) = do (e, lres) <- precLValue tspec lv return (e, lres ++ [iexp]) precLValue tspec (LVCont _ lv (MatP (CElem ce) (CElem re))) = do (e, lres) <- precLValue tspec lv return (e, lres ++ [ce, re]) precLValue _ _ = internalError "precLValue" "Not expected case" -------------------------------------------------------------------------------- -- Only to split literals whenever needed precVDecl :: CaoMonad m => TranslationSpec -> VarDecl Var -> m (VarDecl Var) precVDecl tspec (ContD loc typ exps) = liftM (ContD loc typ) $ concatMapM aux exps where aux (L ll (TyE t (Lit l))) = liftM (map (L ll . TyE t . Lit)) $ precLiteral tspec t l aux v = return [v] precVDecl _ vd = return vd -------------------------------------------------------------------------------- precCDecl :: CaoMonad m => TranslationSpec -> ConstDecl Var -> m (ConstDecl Var, [LStmt Var]) precCDecl tspec (ConstD (L l c) d (ConstInit e)) = do stmt <- precAssignment tspec l (LVVar (L l c)) (annL (typeOf c) e) return (ConstD (L l c) d None, stmt) precCDecl _ cd = return (cd, []) ------------------------------------------------------------------------------- precField :: CaoMonad m => TranslationSpec -> Var -> m (TLExpr Var) precField tspec fld = globalOrInlinedField tspec (return $ genLoc $ annTyE RInt $ Var $ setType RInt fld) (do nfld <- lookupFieldProj fld case nfld of Just nfld' -> return $ rintLit nfld' Nothing -> internalError "precField" "Field of structure not found") ------------------------------------------------------------------------------- -- Getting the reference variable getRefVariable :: CaoMonad m => m Var getRefVariable = getRefVar >>= \mstr -> case mstr of Nothing -> do (i, vn) <- freshSmb let refV = mkCRef vn i caoRef setRefVar refV return refV Just refV -> return refV ------------------------------------------------------------------------------- -- Literals -- TODO: difference between signed and unsigned bits -- TODO: Somewhere before this phase, the size of the bit string is being -- truncated without verification of the overflow. -- TODO: The chunk size has to be an exact divisor of the size of the type. -- Otherwise, the is unreliable or unpredicatable. -- Is the current backend for HIACE handling negative numbers correctly? -- Integers are signed. Possible sign specification (signed/unsigned platform)? -- The typechecker verifies the validity (range) of mod and modpol literals -- We only have to validate statically if the precision of the platform is enough -- and possibly if the literal has to be split precLiteral :: CaoMonad m => TranslationSpec -> Type Var -> Literal Var -> m [Literal Var] -- For boolean literals, it is assumed that literals cannot be split. -- This should be added to the system documentation. precLiteral tspec typ l = checkLiteral tspec typ (return [l]) (\ls -> case l of -- This ensures that booleans are supported BLit _ -> return [l] ILit v -> do l' <- checkILit ls v return $ map ILit l' -- TODO: signed/unsigned bit strings!! BSLit s bits -> do l' <- checkBSLit ls typ bits return $ map (BSLit s) l' PLit p -> do l' <- checkPLit ls typ p return $ map PLit l') ------------------------------------------------------------------------------- -- Argument copy -- Creates a copy of all variables in a list. This is used when the call to a function is -- not safe, i.e., it may not preserve the contents of the arguments. genUnsafeCopy :: CaoMonad m => TranslationSpec -> [TLExpr Var] -> m ([TLExpr Var], [LStmt Var]) genUnsafeCopy tspec = safeCopy (\ _ _ -> True) tspec [] -- Creates a copy of all variables which are in the specified list. This is used when the call to a -- function is argument safe but it may have problems if a variable is simultaneously argument and -- result. genArgSafeCopy :: CaoMonad m => TranslationSpec -> [Var] -> [TLExpr Var] -> m ([TLExpr Var], [LStmt Var]) genArgSafeCopy = safeCopy elem safeCopy :: CaoMonad m => (Var -> [Var] -> Bool) -> TranslationSpec -> [Var] -> [TLExpr Var] -> m ([TLExpr Var], [LStmt Var]) safeCopy cond tspec lv exps = do (e, stmt, vars) <- concatMap3M worker exps mapM_ storeTmpVar vars return (e, stmt) where worker :: CaoMonad m => TLExpr Var -> m (TLExpr Var, [LStmt Var], [Var]) worker ex@(unLoc -> unTyp -> Var v) | cond v lv = do let typ = varType v n <- cCall tspec code_assign typ (var, decl, vars) <- ifM isDependent workerDepend workerNonDep typ return ( genLoc $ annTyE typ $ Var var , decl ++ [genLoc $ Assign [LVVar $ genLoc var] [genLoc $ annTyE typ $ FunCall (genLoc n) [ex]]] , vars) worker e = return (e, [], []) workerDepend typ = do v <- freshVar Local typ return (v, [genLoc $ VDecl $ varDecl v], []) workerNonDep typ = do var <- freshTmpVar typ return (var, [], [var]) safetyCopy :: CaoMonad m => TranslationSpec -> [LVal Var] -> [TLExpr Var] -> m ([TLExpr Var], [LStmt Var]) safetyCopy tspec lv ex = safeOrUnsafeDefault tspec (return (ex, [])) (genUnsafeCopy tspec ex) (genArgSafeCopy tspec (getVars lv) ex) safetyCopy' :: CaoMonad m => TranslationSpec -> Type Var -> OpCode -> [Var] -> [TLExpr Var] -> m ([TLExpr Var], [LStmt Var]) safetyCopy' tspec typ fcode lv ex = safeOfUnsafe tspec typ fcode (return (ex, [])) (genUnsafeCopy tspec ex) (genArgSafeCopy tspec lv ex) ------------------------------------------------------------------------------- -- Auxiliary -- Calls -- This function was changed to accept a list of expressions since literals can -- be decomposed is several literal expressions to fit the platform representation fCallSAux :: CaoMonad m => TranslationSpec -> OpCode -> LVal Var -> [TLExpr Var] -> m (Stmt Var) fCallSAux tspec op lv ex = do -- This is the type to choose from the several libraries let typ = typeOf $ head ex n <- cCall tspec op typ return $ Assign [lv] [genLoc $ annTyE typ $ FunCall (genLoc n) ex ] -- TODO: is this type annotation correct? cCall :: CaoMonad m => TranslationSpec -> OpCode -> Type Var -> m Var cCall tspec op typ = liftM (cFun op typ (callPrefix $ globalTransSpec tspec)) $ typeName tspec typ cCastCall :: CaoMonad m => TranslationSpec -> Type Var -> Type Var -> m Var cCastCall tspec typ typD = do tname <- typeName tspec typ tnameD <- typeName tspec typD return $ cCast typ (callPrefix $ globalTransSpec tspec) tname tnameD returnByValOrRef :: (CaoMonad m, Typeable a) => TranslationSpec -> [a] -> m ([a], [a]) returnByValOrRef tspec = partitionM byVal where byVal e = valOrRefFuncReturn tspec (typeOf e) (return True) (return False) ------------------------------------------------------------------------------- bitsCase :: Type Var -> m a -> m a -> m a bitsCase typ fb fe = case typ of Bits _ _ -> fb _ -> fe getVars :: [LVal Var] -> [Var] getVars = map aux where aux (LVVar v) = unLoc v aux _ = internalError "getVars" "Not expected case." ------------------------------------------------------------------------------- getArgExps :: TLExpr Var -> [TLExpr Var] getArgExps ex = case unTyp $ unLoc ex of Lit _ -> [ex] StructProj s f -> [s, genLoc $ annTyE (varType f) $ Var f] UnaryOp _ e -> [e] BinaryOp _ l r -> [l, r] Access e1 (VectP (CElem ei)) -> [e1, ei] Access e1 (VectP (CRange ei ej)) -> [e1, ei, ej] Access e1 (MatP (CElem ei) (CElem ej)) -> [e1, ei, ej] Access e1 (MatP (CRange ei ej) (CRange ek el)) -> [e1, ei, ej, ek, el] Access e1 (MatP (CRange ei ej) (CElem cole)) -> [e1, cole, ei, ej] Access e1 (MatP (CElem rowe) (CRange ei ej)) -> [e1, rowe, ei, ej] _ -> [] ------------------------------------------------------------------------------- moduleName :: String moduleName = "" internalError :: String -> String -> a internalError funcName msg = error $ moduleName ++ ".<" ++ funcName ++ ">: " ++ msg varDecl :: Var -> VarDecl Var varDecl v = VarD (genLoc v) (type2TyDecl (varType v)) Nothing rintLit :: Integer -> TLExpr Var rintLit = genLoc . annTyE RInt . Lit . ILit