diff --git a/copilot-c99/CHANGELOG b/copilot-c99/CHANGELOG index cb3fe2d8..1c944c35 100644 --- a/copilot-c99/CHANGELOG +++ b/copilot-c99/CHANGELOG @@ -1,3 +1,6 @@ +2023-09-07 + * Clean code. (#453) + 2023-07-07 * Version bump (3.16). (#448) * Introduce testing infrastructure. (#413) diff --git a/copilot-c99/copilot-c99.cabal b/copilot-c99/copilot-c99.cabal index b3c6f2f4..7886b8ee 100644 --- a/copilot-c99/copilot-c99.cabal +++ b/copilot-c99/copilot-c99.cabal @@ -51,9 +51,10 @@ library exposed-modules : Copilot.Compile.C99 - other-modules : Copilot.Compile.C99.Translate + other-modules : Copilot.Compile.C99.Expr + , Copilot.Compile.C99.Type , Copilot.Compile.C99.Error - , Copilot.Compile.C99.Util + , Copilot.Compile.C99.Name , Copilot.Compile.C99.CodeGen , Copilot.Compile.C99.External , Copilot.Compile.C99.Compile diff --git a/copilot-c99/src/Copilot/Compile/C99.hs b/copilot-c99/src/Copilot/Compile/C99.hs index 3602fa6f..e03f1b64 100644 --- a/copilot-c99/src/Copilot/Compile/C99.hs +++ b/copilot-c99/src/Copilot/Compile/C99.hs @@ -6,5 +6,6 @@ module Copilot.Compile.C99 , mkDefaultCSettings ) where -import Copilot.Compile.C99.Compile -import Copilot.Compile.C99.Settings +-- Internal imports +import Copilot.Compile.C99.Compile ( compile, compileWith ) +import Copilot.Compile.C99.Settings ( CSettings (..), mkDefaultCSettings ) diff --git a/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs b/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs index 616bd1db..b9c3feb8 100644 --- a/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs +++ b/copilot-c99/src/Copilot/Compile/C99/CodeGen.hs @@ -1,162 +1,212 @@ -{-# LANGUAGE GADTs #-} -{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE GADTs #-} -- | High-level translation of Copilot Core into C99. -module Copilot.Compile.C99.CodeGen where +module Copilot.Compile.C99.CodeGen + ( + -- * Externs + mkExtCpyDecln + , mkExtDecln + + -- * Type declarations + , mkStructDecln + , mkStructForwDecln + + -- * Ring buffers + , mkBuffDecln + , mkIndexDecln + , mkAccessDecln + + -- * Stream generators + , mkGenFun + , mkGenFunArray + + -- * Monitor processing + , mkStep + ) + where -import Control.Monad.State (runState) -import Data.List (union, unzip4) +-- External imports +import Control.Monad.State ( runState ) +import Data.List ( unzip4 ) import qualified Data.List.NonEmpty as NonEmpty -import Data.Typeable (Typeable) - import qualified Language.C99.Simple as C -import Copilot.Core -import Copilot.Compile.C99.Error (impossible) -import Copilot.Compile.C99.Util -import Copilot.Compile.C99.External -import Copilot.Compile.C99.Settings -import Copilot.Compile.C99.Translate +-- Internal imports: Copilot +import Copilot.Core ( Expr (..), Id, Stream (..), Struct (..), Trigger (..), + Type (..), UExpr (..), Value (..), fieldname, tysize ) + +-- Internal imports +import Copilot.Compile.C99.Error ( impossible ) +import Copilot.Compile.C99.Expr ( constArray, transExpr ) +import Copilot.Compile.C99.External ( External (..) ) +import Copilot.Compile.C99.Name ( argNames, argTempNames, generatorName, + guardName, indexName, streamAccessorName, + streamName ) +import Copilot.Compile.C99.Settings ( CSettings, cSettingsStepFunctionName ) +import Copilot.Compile.C99.Type ( transType ) + +-- * Externs + +-- | Make a extern declaration of a variable. +mkExtDecln :: External -> C.Decln +mkExtDecln (External name _ ty) = decln + where + decln = C.VarDecln (Just C.Extern) cTy name Nothing + cTy = transType ty + +-- | Make a declaration for a copy of an external variable. +mkExtCpyDecln :: External -> C.Decln +mkExtCpyDecln (External _name cpyName ty) = decln + where + decln = C.VarDecln (Just C.Static) cTy cpyName Nothing + cTy = transType ty + +-- * Type declarations --- | Write a declaration for a generator function. -gendecln :: String -> Type a -> C.Decln -gendecln name ty = C.FunDecln Nothing cty name [] +-- | Write a struct declaration based on its definition. +mkStructDecln :: Struct a => Type a -> C.Decln +mkStructDecln (Struct x) = C.TypeDecln struct where - cty = C.decay $ transtype ty + struct = C.TypeSpec $ C.StructDecln (Just $ typename x) fields + fields = NonEmpty.fromList $ map mkField (toValues x) + + mkField :: Value a -> C.FieldDecln + mkField (Value ty field) = C.FieldDecln (transType ty) (fieldname field) + +-- | Write a forward struct declaration. +mkStructForwDecln :: Struct a => Type a -> C.Decln +mkStructForwDecln (Struct x) = C.TypeDecln struct + where + struct = C.TypeSpec $ C.Struct (typename x) + +-- * Ring buffers + +-- | Make a C buffer variable and initialise it with the stream buffer. +mkBuffDecln :: Id -> Type a -> [a] -> C.Decln +mkBuffDecln sId ty xs = C.VarDecln (Just C.Static) cTy name initVals + where + name = streamName sId + cTy = C.Array (transType ty) (Just $ C.LitInt $ fromIntegral buffSize) + buffSize = length xs + initVals = Just $ C.InitList $ constArray ty xs + +-- | Make a C index variable and initialise it to 0. +mkIndexDecln :: Id -> C.Decln +mkIndexDecln sId = C.VarDecln (Just C.Static) cTy name initVal + where + name = indexName sId + cTy = C.TypeSpec $ C.TypedefName "size_t" + initVal = Just $ C.InitExpr $ C.LitInt 0 + +-- | Define an accessor functions for the ring buffer associated with a stream. +mkAccessDecln :: Id -> Type a -> [a] -> C.FunDef +mkAccessDecln sId ty xs = C.FunDef cTy name params [] [C.Return (Just expr)] + where + cTy = C.decay $ transType ty + name = streamAccessorName sId + buffLength = C.LitInt $ fromIntegral $ length xs + params = [C.Param (C.TypeSpec $ C.TypedefName "size_t") "x"] + index = (C.Ident (indexName sId) C..+ C.Ident "x") C..% buffLength + expr = C.Index (C.Ident (streamName sId)) index + +-- * Stream generators -- | Write a generator function for a stream. -genfun :: String -> Expr a -> Type a -> C.FunDef -genfun name expr ty = C.FunDef cty name [] cvars [C.Return $ Just cexpr] +mkGenFun :: String -> Expr a -> Type a -> C.FunDef +mkGenFun name expr ty = C.FunDef cTy name [] cVars [C.Return $ Just cExpr] where - cty = C.decay $ transtype ty - (cexpr, cvars) = runState (transexpr expr) mempty + cTy = C.decay $ transType ty + (cExpr, cVars) = runState (transExpr expr) mempty -- | Write a generator function for a stream that returns an array. -genFunArray :: String -> String -> Expr a -> Type a -> C.FunDef -genFunArray name nameArg expr ty@(Array _) = +mkGenFunArray :: String -> String -> Expr a -> Type a -> C.FunDef +mkGenFunArray name nameArg expr ty@(Array _) = C.FunDef funType name [ outputParam ] varDecls stmts where funType = C.TypeSpec C.Void -- The output value is an array outputParam = C.Param cArrayType nameArg - cArrayType = transtype ty + cArrayType = transType ty -- Output value, and any variable declarations needed - (cexpr, varDecls) = runState (transexpr expr) mempty + (cExpr, varDecls) = runState (transExpr expr) mempty -- Copy expression to output argument - stmts = [ C.Expr $ memcpy (C.Ident nameArg) cexpr size ] + stmts = [ C.Expr $ memcpy (C.Ident nameArg) cExpr size ] size = C.LitInt (fromIntegral $ tysize ty) C..* C.SizeOfType (C.TypeName $ tyElemName ty) -genFunArray name nameArg expr _ = - impossible "genFunArray" "copilot-c99" - --- | Make a extern declaration of a variable. -mkextdecln :: External -> C.Decln -mkextdecln (External name _ ty) = decln - where - decln = C.VarDecln (Just C.Extern) cty name Nothing - cty = transtype ty - --- | Make a declaration for a copy of an external variable. -mkextcpydecln :: External -> C.Decln -mkextcpydecln (External name cpyname ty) = decln - where - cty = transtype ty - decln = C.VarDecln (Just C.Static) cty cpyname Nothing - --- | Make a C buffer variable and initialise it with the stream buffer. -mkbuffdecln :: Id -> Type a -> [a] -> C.Decln -mkbuffdecln sid ty xs = C.VarDecln (Just C.Static) cty name initvals - where - name = streamname sid - cty = C.Array (transtype ty) (Just $ C.LitInt $ fromIntegral buffsize) - buffsize = length xs - initvals = Just $ C.InitList $ constarray ty xs +mkGenFunArray _name _nameArg _expr _ty = + impossible "mkGenFunArray" "copilot-c99" --- | Make a C index variable and initialise it to 0. -mkindexdecln :: Id -> C.Decln -mkindexdecln sid = C.VarDecln (Just C.Static) cty name initval - where - name = indexname sid - cty = C.TypeSpec $ C.TypedefName "size_t" - initval = Just $ C.InitExpr $ C.LitInt 0 +-- * Monitor processing --- | Define an accessor functions for the ring buffer associated with a stream -mkaccessdecln :: Id -> Type a -> [a] -> C.FunDef -mkaccessdecln sid ty xs = C.FunDef cty name params [] [C.Return (Just expr)] - where - cty = C.decay $ transtype ty - name = streamaccessorname sid - bufflength = C.LitInt $ fromIntegral $ length xs - params = [C.Param (C.TypeSpec $ C.TypedefName "size_t") "x"] - index = (C.Ident (indexname sid) C..+ C.Ident "x") C..% bufflength - expr = C.Index (C.Ident (streamname sid)) index - --- | Writes the step function, that updates all streams. -mkstep :: CSettings -> [Stream] -> [Trigger] -> [External] -> C.FunDef -mkstep cSettings streams triggers exts = +-- | Define the step function that updates all streams. +mkStep :: CSettings -> [Stream] -> [Trigger] -> [External] -> C.FunDef +mkStep cSettings streams triggers exts = C.FunDef void (cSettingsStepFunctionName cSettings) [] declns stmts where - void = C.TypeSpec C.Void - stmts = map mkexcopy exts - ++ triggerStmts - ++ tmpassigns - ++ bufferupdates - ++ indexupdates + declns = streamDeclns ++ concat triggerDeclns - (streamDeclns, tmpassigns, bufferupdates, indexupdates) = - unzip4 $ map mkupdateglobals streams + + stmts = map mkExCopy exts + ++ triggerStmts + ++ tmpAssigns + ++ bufferUpdates + ++ indexUpdates + + (streamDeclns, tmpAssigns, bufferUpdates, indexUpdates) = + unzip4 $ map mkUpdateGlobals streams (triggerDeclns, triggerStmts) = - unzip $ map mktriggercheck triggers + unzip $ map mkTriggerCheck triggers -- Write code to update global stream buffers and index. - mkupdateglobals :: Stream -> (C.Decln, C.Stmt, C.Stmt, C.Stmt) - mkupdateglobals (Stream sid buff expr ty) = - (tmpdecln, tmpassign, bufferupdate, indexupdate) + mkUpdateGlobals :: Stream -> (C.Decln, C.Stmt, C.Stmt, C.Stmt) + mkUpdateGlobals (Stream sId buff _expr ty) = + (tmpDecln, tmpAssign, bufferUpdate, indexUpdate) where - tmpdecln = C.VarDecln Nothing cty tmp_var Nothing + tmpDecln = C.VarDecln Nothing cTy tmpVar Nothing - tmpassign = case ty of - Array _ -> C.Expr $ C.Funcall (C.Ident $ generatorname sid) - [ C.Ident tmp_var ] - _ -> C.Expr $ C.Ident tmp_var C..= val + tmpAssign = case ty of + Array _ -> C.Expr $ C.Funcall (C.Ident $ generatorName sId) + [ C.Ident tmpVar ] + _ -> C.Expr $ C.Ident tmpVar C..= val - bufferupdate = case ty of - Array _ -> C.Expr $ memcpy dest (C.Ident tmp_var) size + bufferUpdate = case ty of + Array _ -> C.Expr $ memcpy dest (C.Ident tmpVar) size where - dest = C.Index buff_var index_var - size = C.LitInt (fromIntegral $ tysize ty) - C..* C.SizeOfType (C.TypeName (tyElemName ty)) + dest = C.Index buffVar indexVar + size = C.LitInt + (fromIntegral $ tysize ty) + C..* C.SizeOfType (C.TypeName (tyElemName ty)) _ -> C.Expr $ - C.Index buff_var index_var C..= (C.Ident tmp_var) + C.Index buffVar indexVar C..= C.Ident tmpVar - indexupdate = C.Expr $ index_var C..= (incindex C..% bufflength) + indexUpdate = C.Expr $ indexVar C..= (incIndex C..% buffLength) where - bufflength = C.LitInt $ fromIntegral $ length buff - incindex = index_var C..+ C.LitInt 1 + buffLength = C.LitInt $ fromIntegral $ length buff + incIndex = indexVar C..+ C.LitInt 1 - tmp_var = streamname sid ++ "_tmp" - buff_var = C.Ident $ streamname sid - index_var = C.Ident $ indexname sid - val = C.Funcall (C.Ident $ generatorname sid) [] - cty = transtype ty + tmpVar = streamName sId ++ "_tmp" + buffVar = C.Ident $ streamName sId + indexVar = C.Ident $ indexName sId + val = C.Funcall (C.Ident $ generatorName sId) [] + cTy = transType ty -- Make code that copies an external variable to its local one. - mkexcopy :: External -> C.Stmt - mkexcopy (External name cpyname ty) = C.Expr $ case ty of - Array _ -> memcpy exvar locvar size + mkExCopy :: External -> C.Stmt + mkExCopy (External name cpyName ty) = C.Expr $ case ty of + Array _ -> memcpy exVar locVar size where - exvar = C.Ident cpyname - locvar = C.Ident name + exVar = C.Ident cpyName + locVar = C.Ident name size = C.LitInt (fromIntegral $ tysize ty) C..* C.SizeOfType (C.TypeName (tyElemName ty)) - _ -> C.Ident cpyname C..= C.Ident name + _ -> C.Ident cpyName C..= C.Ident name -- Make if-statement to check the guard, call the handler if necessary. -- This returns two things: @@ -192,15 +242,20 @@ mkstep cSettings streams triggers exts = -- 2. Assigning a struct to a temporary variable defensively ensures that -- any modifications that the handler called makes to the struct argument -- will not affect the internals of the monitoring code. - mktriggercheck :: Trigger -> ([C.Decln], C.Stmt) - mktriggercheck (Trigger name guard args) = - (aTmpDeclns, ifStmt) + mkTriggerCheck :: Trigger -> ([C.Decln], C.Stmt) + mkTriggerCheck (Trigger name _guard args) = + (aTmpDeclns, triggerCheckStmt) where - aTmpDeclns = zipWith (\tmpVar arg -> - C.VarDecln Nothing (tempType arg) tmpVar Nothing) - aTempNames - args + aTmpDeclns :: [C.Decln] + aTmpDeclns = zipWith declare args aTempNames where + declare :: UExpr -> C.Ident -> C.Decln + declare arg tmpVar = + C.VarDecln Nothing (tempType arg) tmpVar Nothing + + -- Type of the temporary variable used to store values of the type + -- of a given expression. + tempType :: UExpr -> C.Type tempType (UExpr { uExprType = ty }) = case ty of -- If a temporary variable is being used to store an array, @@ -209,83 +264,50 @@ mkstep cSettings streams triggers exts = -- the `arg` function will return a pointer, not an array, and -- C doesn't make it easy to cast directly from an array to a -- pointer. - Array ty' -> C.Ptr $ transtype ty' - _ -> transtype ty - - aTempNames = take (length args) (argTempNames name) - - ifStmt = C.If guard' firetrigger + Array ty' -> C.Ptr $ transType ty' + _ -> transType ty - guard' = C.Funcall (C.Ident $ guardname name) [] - - -- The body of the if-statement. This consists of statements that assign - -- the values of the temporary variables, following by a final statement - -- that passes the temporary variables to the handler function. - firetrigger = map C.Expr argAssigns ++ - [C.Expr $ C.Funcall (C.Ident name) - (zipWith passArg aTempNames args)] + triggerCheckStmt :: C.Stmt + triggerCheckStmt = C.If guard' fireTrigger where - passArg aTempName (UExpr { uExprType = ty }) = - case ty of - -- Special case for Struct to pass reference to temporary - -- struct variable to handler. (See the comments for - -- mktriggercheck for details.) - Struct _ -> C.UnaryOp C.Ref $ C.Ident aTempName - _ -> C.Ident aTempName - - argAssigns = zipWith (\aTempName arg -> - C.AssignOp C.Assign (C.Ident aTempName) arg) - aTempNames - args' - args' = take (length args) (map argcall (argnames name)) - argcall name = C.Funcall (C.Ident name) [] - - --- | Write a struct declaration based on its definition. -mkstructdecln :: Struct a => Type a -> C.Decln -mkstructdecln (Struct x) = C.TypeDecln struct - where - struct = C.TypeSpec $ C.StructDecln (Just $ typename x) fields - fields = NonEmpty.fromList $ map mkfield (toValues x) - - mkfield :: Value a -> C.FieldDecln - mkfield (Value ty field) = C.FieldDecln (transtype ty) (fieldname field) - --- | Write a forward struct declaration. -mkstructforwdecln :: Struct a => Type a -> C.Decln -mkstructforwdecln (Struct x) = C.TypeDecln struct - where - struct = C.TypeSpec $ C.Struct (typename x) - --- | List all types of an expression, returns items uniquely. -exprtypes :: Typeable a => Expr a -> [UType] -exprtypes e = case e of - Const ty _ -> typetypes ty - Local ty1 ty2 _ e1 e2 -> typetypes ty1 `union` typetypes ty2 - `union` exprtypes e1 `union` exprtypes e2 - Var ty _ -> typetypes ty - Drop ty _ _ -> typetypes ty - ExternVar ty _ _ -> typetypes ty - Op1 _ e1 -> exprtypes e1 - Op2 _ e1 e2 -> exprtypes e1 `union` exprtypes e2 - Op3 _ e1 e2 e3 -> exprtypes e1 `union` exprtypes e2 `union` exprtypes e3 - Label ty _ _ -> typetypes ty - --- | List all types of a type, returns items uniquely. -typetypes :: Typeable a => Type a -> [UType] -typetypes ty = case ty of - Array ty' -> typetypes ty' `union` [UType ty] - Struct x -> concatMap (\(Value ty' _) -> typetypes ty') (toValues x) `union` [UType ty] - _ -> [UType ty] - --- | Collect all expression of a list of streams and triggers and wrap them --- into an UEXpr. -gatherexprs :: [Stream] -> [Trigger] -> [UExpr] -gatherexprs streams triggers = map streamexpr streams - ++ concatMap triggerexpr triggers - where - streamexpr (Stream _ _ expr ty) = UExpr ty expr - triggerexpr (Trigger _ guard args) = UExpr Bool guard : args + guard' = C.Funcall (C.Ident $ guardName name) [] + + -- The body of the if-statement. This consists of statements that + -- assign the values of the temporary variables, following by a + -- final statement that passes the temporary variables to the + -- handler function. + fireTrigger = map C.Expr argAssigns + ++ [C.Expr $ + C.Funcall (C.Ident name) + (zipWith passArg aTempNames args)] + where + -- List of assignments of values of temporary variables. + argAssigns :: [C.Expr] + argAssigns = zipWith assign aTempNames args' + + assign :: C.Ident -> C.Expr -> C.Expr + assign aTempName = C.AssignOp C.Assign (C.Ident aTempName) + + args' = take (length args) (map argCall (argNames name)) + argCall name' = C.Funcall (C.Ident name') [] + + -- Build an expression to pass a temporary variable as argument + -- to a trigger handler. + -- + -- We need to pass a reference to the variable in some cases, + -- so we also need the type of the expression, which is enclosed + -- in the second argument, an UExpr. + passArg :: String -> UExpr -> C.Expr + passArg aTempName (UExpr { uExprType = ty }) = + case ty of + -- Special case for Struct to pass reference to temporary + -- struct variable to handler. (See the comments for + -- mktriggercheck for details.) + Struct _ -> C.UnaryOp C.Ref $ C.Ident aTempName + _ -> C.Ident aTempName + + aTempNames :: [String] + aTempNames = take (length args) (argTempNames name) -- * Auxiliary functions @@ -301,4 +323,4 @@ memcpy dest src size = C.Funcall (C.Ident "memcpy") [dest, src, size] tyElemName :: Type a -> C.Type tyElemName ty = case ty of Array ty' -> tyElemName ty' - _ -> transtype ty + _ -> transType ty diff --git a/copilot-c99/src/Copilot/Compile/C99/Compile.hs b/copilot-c99/src/Copilot/Compile/C99/Compile.hs index c76d0bc2..bf7c0578 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Compile.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Compile.hs @@ -5,23 +5,35 @@ module Copilot.Compile.C99.Compile , compileWith ) where -import Text.PrettyPrint (render) -import Data.List (nub) -import Data.Maybe (catMaybes) -import System.Directory (createDirectoryIfMissing) -import System.Exit (exitFailure) -import System.FilePath (()) -import System.IO (hPutStrLn, stderr) - -import Language.C99.Pretty (pretty) +-- External imports +import Data.List ( nub, union ) +import Data.Maybe ( mapMaybe ) +import Data.Typeable ( Typeable ) +import Language.C99.Pretty ( pretty ) import qualified Language.C99.Simple as C - -import Copilot.Core -import Copilot.Compile.C99.Util -import Copilot.Compile.C99.External -import Copilot.Compile.C99.Settings -import Copilot.Compile.C99.Translate -import Copilot.Compile.C99.CodeGen +import System.Directory ( createDirectoryIfMissing ) +import System.Exit ( exitFailure ) +import System.FilePath ( () ) +import System.IO ( hPutStrLn, stderr ) +import Text.PrettyPrint ( render ) + +-- Internal imports: Copilot +import Copilot.Core ( Expr (..), Spec (..), Stream (..), Struct (..), + Trigger (..), Type (..), UExpr (..), UType (..), + Value (..) ) + +-- Internal imports +import Copilot.Compile.C99.CodeGen ( mkAccessDecln, mkBuffDecln, mkExtCpyDecln, + mkExtDecln, mkGenFun, mkGenFunArray, + mkIndexDecln, mkStep, mkStructDecln, + mkStructForwDecln ) +import Copilot.Compile.C99.External ( External, gatherExts ) +import Copilot.Compile.C99.Name ( argNames, generatorName, + generatorOutputArgName, guardName ) +import Copilot.Compile.C99.Settings ( CSettings, cSettingsOutputDirectory, + cSettingsStepFunctionName, + mkDefaultCSettings ) +import Copilot.Compile.C99.Type ( transType ) -- | Compile a specification to a .h and a .c file. -- @@ -37,11 +49,11 @@ compileWith cSettings prefix spec exitFailure | otherwise - = do let cfile = render $ pretty $ C.translate $ compilec cSettings spec - hfile = render $ pretty $ C.translate $ compileh cSettings spec + = do let cFile = render $ pretty $ C.translate $ compileC cSettings spec + hFile = render $ pretty $ C.translate $ compileH cSettings spec typeDeclnsFile = safeCRender $ compileTypeDeclns cSettings spec - cmacros = unlines [ "#include " + cMacros = unlines [ "#include " , "#include " , "#include " , "#include " @@ -54,8 +66,8 @@ compileWith cSettings prefix spec let dir = cSettingsOutputDirectory cSettings createDirectoryIfMissing True dir - writeFile (dir prefix ++ ".c") $ cmacros ++ cfile - writeFile (dir prefix ++ ".h") hfile + writeFile (dir prefix ++ ".c") $ cMacros ++ cFile + writeFile (dir prefix ++ ".h") hFile writeFile (dir prefix ++ "_types.h") typeDeclnsFile -- | Compile a specification to a .h and a .c file. @@ -72,99 +84,103 @@ compile = compileWith mkDefaultCSettings -- * Declarations of global buffers and indices. -- * Generator functions for streams, guards and trigger arguments. -- * Declaration of the @step()@ function. -compilec :: CSettings -> Spec -> C.TransUnit -compilec cSettings spec = C.TransUnit declns funs +compileC :: CSettings -> Spec -> C.TransUnit +compileC cSettings spec = C.TransUnit declns funs where + declns = mkExts exts + ++ mkGlobals streams + + funs = mkGenFuns streams triggers + ++ [mkStep cSettings streams triggers exts] + streams = specStreams spec triggers = specTriggers spec - exts = gatherexts streams triggers - - declns = mkexts exts ++ mkglobals streams - funs = genfuns streams triggers ++ [mkstep cSettings streams triggers exts] + exts = gatherExts streams triggers -- Make declarations for copies of external variables. - mkexts :: [External] -> [C.Decln] - mkexts exts = map mkextcpydecln exts + mkExts :: [External] -> [C.Decln] + mkExts = map mkExtCpyDecln -- Make buffer and index declarations for streams. - mkglobals :: [Stream] -> [C.Decln] - mkglobals streams = map buffdecln streams ++ map indexdecln streams + mkGlobals :: [Stream] -> [C.Decln] + mkGlobals streamList = map buffDecln streamList + ++ map indexDecln streamList where - buffdecln (Stream sid buff _ ty) = mkbuffdecln sid ty buff - indexdecln (Stream sid _ _ _ ) = mkindexdecln sid + buffDecln (Stream sId buff _ ty) = mkBuffDecln sId ty buff + indexDecln (Stream sId _ _ _ ) = mkIndexDecln sId -- Make generator functions, including trigger arguments. - genfuns :: [Stream] -> [Trigger] -> [C.FunDef] - genfuns streams triggers = map accessdecln streams - ++ map streamgen streams - ++ concatMap triggergen triggers + mkGenFuns :: [Stream] -> [Trigger] -> [C.FunDef] + mkGenFuns streamList triggerList = map accessDecln streamList + ++ map streamGen streamList + ++ concatMap triggerGen triggerList where + accessDecln :: Stream -> C.FunDef + accessDecln (Stream sId buff _ ty) = mkAccessDecln sId ty buff - accessdecln :: Stream -> C.FunDef - accessdecln (Stream sid buff _ ty) = mkaccessdecln sid ty buff - - streamgen :: Stream -> C.FunDef - streamgen (Stream sid _ expr ty@(Array _)) = - genFunArray (generatorname sid) (generatorOutputArgName sid) expr ty - streamgen (Stream sid _ expr ty) = genfun (generatorname sid) expr ty + streamGen :: Stream -> C.FunDef + streamGen (Stream sId _ expr ty@(Array _)) = + mkGenFunArray (generatorName sId) (generatorOutputArgName sId) expr ty + streamGen (Stream sId _ expr ty) = mkGenFun (generatorName sId) expr ty - triggergen :: Trigger -> [C.FunDef] - triggergen (Trigger name guard args) = guarddef : argdefs + triggerGen :: Trigger -> [C.FunDef] + triggerGen (Trigger name guard args) = guardDef : argDefs where - guarddef = genfun (guardname name) guard Bool - argdefs = map arggen (zip (argnames name) args) + guardDef = mkGenFun (guardName name) guard Bool + argDefs = zipWith argGen (argNames name) args - arggen :: (String, UExpr) -> C.FunDef - arggen (argname, UExpr ty expr) = genfun argname expr ty + argGen :: String -> UExpr -> C.FunDef + argGen argName (UExpr ty expr) = mkGenFun argName expr ty -- | Generate the .h file from a 'Spec'. -compileh :: CSettings -> Spec -> C.TransUnit -compileh cSettings spec = C.TransUnit declns [] +compileH :: CSettings -> Spec -> C.TransUnit +compileH cSettings spec = C.TransUnit declns [] where + declns = mkStructForwDeclns exprs + ++ mkExts exts + ++ extFunDeclns triggers + ++ [stepDecln] + + exprs = gatherExprs streams triggers + exts = gatherExts streams triggers streams = specStreams spec triggers = specTriggers spec - exts = gatherexts streams triggers - exprs = gatherexprs streams triggers - - declns = mkstructforwdeclns exprs - ++ mkexts exts - ++ extfundeclns triggers - ++ [stepdecln] - mkstructforwdeclns :: [UExpr] -> [C.Decln] - mkstructforwdeclns es = catMaybes $ map mkdecln utypes + mkStructForwDeclns :: [UExpr] -> [C.Decln] + mkStructForwDeclns es = mapMaybe mkDecln uTypes where - mkdecln (UType ty) = case ty of - Struct x -> Just $ mkstructforwdecln ty + mkDecln (UType ty) = case ty of + Struct _ -> Just $ mkStructForwDecln ty _ -> Nothing - utypes = nub $ concatMap (\(UExpr _ e) -> exprtypes e) es + uTypes = nub $ concatMap (\(UExpr _ e) -> exprTypes e) es -- Make declarations for external variables. - mkexts :: [External] -> [C.Decln] - mkexts = map mkextdecln + mkExts :: [External] -> [C.Decln] + mkExts = map mkExtDecln - extfundeclns :: [Trigger] -> [C.Decln] - extfundeclns triggers = map extfundecln triggers + extFunDeclns :: [Trigger] -> [C.Decln] + extFunDeclns = map extFunDecln where - extfundecln :: Trigger -> C.Decln - extfundecln (Trigger name _ args) = C.FunDecln Nothing cty name params + extFunDecln :: Trigger -> C.Decln + extFunDecln (Trigger name _ args) = C.FunDecln Nothing cTy name params where - cty = C.TypeSpec C.Void - params = map mkparam $ zip (argnames name) args - mkparam (name, UExpr ty _) = C.Param (mkParamTy ty) name + cTy = C.TypeSpec C.Void + params = zipWith mkParam (argNames name) args + + mkParam paramName (UExpr ty _) = C.Param (mkParamTy ty) paramName -- Special case for Struct, to pass struct arguments by reference. -- Arrays are also passed by reference, but using C's array type -- does that automatically. mkParamTy ty = case ty of - Struct _ -> C.Ptr (transtype ty) - _ -> transtype ty + Struct _ -> C.Ptr (transType ty) + _ -> transType ty -- Declaration for the step function. - stepdecln :: C.Decln - stepdecln = C.FunDecln Nothing (C.TypeSpec C.Void) + stepDecln :: C.Decln + stepDecln = C.FunDecln Nothing (C.TypeSpec C.Void) (cSettingsStepFunctionName cSettings) [] -- | Generate a C translation unit that contains all type declarations needed @@ -174,18 +190,18 @@ compileTypeDeclns _cSettings spec = C.TransUnit declns [] where declns = mkTypeDeclns exprs - exprs = gatherexprs streams triggers + exprs = gatherExprs streams triggers streams = specStreams spec triggers = specTriggers spec -- Generate type declarations. mkTypeDeclns :: [UExpr] -> [C.Decln] - mkTypeDeclns es = catMaybes $ map mkTypeDecln uTypes + mkTypeDeclns es = mapMaybe mkTypeDecln uTypes where - uTypes = nub $ concatMap (\(UExpr _ e) -> exprtypes e) es + uTypes = nub $ concatMap (\(UExpr _ e) -> exprTypes e) es mkTypeDecln (UType ty) = case ty of - Struct _ -> Just $ mkstructdecln ty + Struct _ -> Just $ mkStructDecln ty _ -> Nothing -- * Auxiliary definitions @@ -195,3 +211,37 @@ compileTypeDeclns _cSettings spec = C.TransUnit declns [] safeCRender :: C.TransUnit -> String safeCRender (C.TransUnit [] []) = "" safeCRender transUnit = render $ pretty $ C.translate transUnit + +-- ** Obtain information from Copilot Core Exprs and Types. + +-- | List all types of an expression, returns items uniquely. +exprTypes :: Typeable a => Expr a -> [UType] +exprTypes e = case e of + Const ty _ -> typeTypes ty + Local ty1 ty2 _ e1 e2 -> typeTypes ty1 `union` typeTypes ty2 + `union` exprTypes e1 `union` exprTypes e2 + Var ty _ -> typeTypes ty + Drop ty _ _ -> typeTypes ty + ExternVar ty _ _ -> typeTypes ty + Op1 _ e1 -> exprTypes e1 + Op2 _ e1 e2 -> exprTypes e1 `union` exprTypes e2 + Op3 _ e1 e2 e3 -> exprTypes e1 `union` exprTypes e2 + `union` exprTypes e3 + Label ty _ _ -> typeTypes ty + +-- | List all types of a type, returns items uniquely. +typeTypes :: Typeable a => Type a -> [UType] +typeTypes ty = case ty of + Array ty' -> typeTypes ty' `union` [UType ty] + Struct x -> concatMap (\(Value ty' _) -> typeTypes ty') (toValues x) + `union` [UType ty] + _ -> [UType ty] + +-- | Collect all expression of a list of streams and triggers and wrap them +-- into an UEXpr. +gatherExprs :: [Stream] -> [Trigger] -> [UExpr] +gatherExprs streams triggers = map streamUExpr streams + ++ concatMap triggerUExpr triggers + where + streamUExpr (Stream _ _ expr ty) = UExpr ty expr + triggerUExpr (Trigger _ guard args) = UExpr Bool guard : args diff --git a/copilot-c99/src/Copilot/Compile/C99/Error.hs b/copilot-c99/src/Copilot/Compile/C99/Error.hs index 8534b6dc..f42c794d 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Error.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Error.hs @@ -1,9 +1,9 @@ --------------------------------------------------------------------------------- --- Copyright © 2011 National Institute of Aerospace / Galois, Inc. --------------------------------------------------------------------------------- {-# LANGUAGE Safe #-} --- | Custom functions to report error messages to users. +-- | +-- Copyright: (c) 2011 National Institute of Aerospace / Galois, Inc. +-- +-- Custom functions to report error messages to users. module Copilot.Compile.C99.Error ( impossible ) where diff --git a/copilot-c99/src/Copilot/Compile/C99/Translate.hs b/copilot-c99/src/Copilot/Compile/C99/Expr.hs similarity index 65% rename from copilot-c99/src/Copilot/Compile/C99/Translate.hs rename to copilot-c99/src/Copilot/Compile/C99/Expr.hs index d1f33606..faa522b8 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Translate.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Expr.hs @@ -1,59 +1,71 @@ {-# LANGUAGE GADTs #-} -- | Translate Copilot Core expressions and operators to C99. -module Copilot.Compile.C99.Translate where +module Copilot.Compile.C99.Expr + ( transExpr + , constArray + ) + where -import Control.Monad.State +-- External imports +import Control.Monad.State ( State, modify ) import qualified Data.List.NonEmpty as NonEmpty +import qualified Language.C99.Simple as C -import Copilot.Core -import Copilot.Compile.C99.Error (impossible) -import Copilot.Compile.C99.Util +-- Internal imports: Copilot +import Copilot.Core ( Expr (..), Field (..), Op1 (..), Op2 (..), Op3 (..), + Type (..), Value (..), accessorname, arrayelems, + toValues ) -import qualified Language.C99.Simple as C +-- Internal imports +import Copilot.Compile.C99.Error ( impossible ) +import Copilot.Compile.C99.Name ( exCpyName, streamAccessorName ) +import Copilot.Compile.C99.Type ( transLocalVarDeclType, transTypeName ) -- | Translates a Copilot Core expression into a C99 expression. -transexpr :: Expr a -> State FunEnv C.Expr -transexpr (Const ty x) = return $ constty ty x +transExpr :: Expr a -> State FunEnv C.Expr +transExpr (Const ty x) = return $ constTy ty x + +transExpr (Local ty1 _ name e1 e2) = do + e1' <- transExpr e1 + let cTy1 = transLocalVarDeclType ty1 + initExpr = Just $ C.InitExpr e1' -transexpr (Local ty1 _ name e1 e2) = do - e1' <- transexpr e1 - let cty1 = transLocalVarDeclType ty1 - init = Just $ C.InitExpr e1' - statetell [C.VarDecln Nothing cty1 name init] + -- Add new decl to the tail of the fun env + modify (++ [C.VarDecln Nothing cTy1 name initExpr]) - transexpr e2 + transExpr e2 -transexpr (Var _ n) = return $ C.Ident n +transExpr (Var _ n) = return $ C.Ident n -transexpr (Drop _ amount sid) = do - let accessvar = streamaccessorname sid +transExpr (Drop _ amount sId) = do + let accessVar = streamAccessorName sId index = C.LitInt (fromIntegral amount) - return $ funcall accessvar [index] + return $ funCall accessVar [index] -transexpr (ExternVar _ name _) = return $ C.Ident (excpyname name) +transExpr (ExternVar _ name _) = return $ C.Ident (exCpyName name) -transexpr (Label _ _ e) = transexpr e -- ignore label +transExpr (Label _ _ e) = transExpr e -- ignore label -transexpr (Op1 op e) = do - e' <- transexpr e - return $ transop1 op e' +transExpr (Op1 op e) = do + e' <- transExpr e + return $ transOp1 op e' -transexpr (Op2 op e1 e2) = do - e1' <- transexpr e1 - e2' <- transexpr e2 - return $ transop2 op e1' e2' +transExpr (Op2 op e1 e2) = do + e1' <- transExpr e1 + e2' <- transExpr e2 + return $ transOp2 op e1' e2' -transexpr (Op3 op e1 e2 e3) = do - e1' <- transexpr e1 - e2' <- transexpr e2 - e3' <- transexpr e3 - return $ transop3 op e1' e2' e3' +transExpr (Op3 op e1 e2 e3) = do + e1' <- transExpr e1 + e2' <- transExpr e2 + e3' <- transExpr e3 + return $ transOp3 op e1' e2' e3' -- | Translates a Copilot unary operator and its argument into a C99 -- expression. -transop1 :: Op1 a b -> C.Expr -> C.Expr -transop1 op e = +transOp1 :: Op1 a b -> C.Expr -> C.Expr +transOp1 op e = -- There are three types of ways in which a function in Copilot Core can be -- translated into C: -- @@ -64,32 +76,32 @@ transop1 op e = Not -> (C..!) e Abs ty -> transAbs ty e Sign ty -> transSign ty e - Recip ty -> (constNumTy ty 1) C../ e - Acos ty -> funcall (specializeMathFunName ty "acos") [e] - Asin ty -> funcall (specializeMathFunName ty "asin") [e] - Atan ty -> funcall (specializeMathFunName ty "atan") [e] - Cos ty -> funcall (specializeMathFunName ty "cos") [e] - Sin ty -> funcall (specializeMathFunName ty "sin") [e] - Tan ty -> funcall (specializeMathFunName ty "tan") [e] - Acosh ty -> funcall (specializeMathFunName ty "acosh") [e] - Asinh ty -> funcall (specializeMathFunName ty "asinh") [e] - Atanh ty -> funcall (specializeMathFunName ty "atanh") [e] - Cosh ty -> funcall (specializeMathFunName ty "cosh") [e] - Sinh ty -> funcall (specializeMathFunName ty "sinh") [e] - Tanh ty -> funcall (specializeMathFunName ty "tanh") [e] - Exp ty -> funcall (specializeMathFunName ty "exp") [e] - Log ty -> funcall (specializeMathFunName ty "log") [e] - Sqrt ty -> funcall (specializeMathFunName ty "sqrt") [e] - Ceiling ty -> funcall (specializeMathFunName ty "ceil") [e] - Floor ty -> funcall (specializeMathFunName ty "floor") [e] + Recip ty -> constNumTy ty 1 C../ e + Acos ty -> funCall (specializeMathFunName ty "acos") [e] + Asin ty -> funCall (specializeMathFunName ty "asin") [e] + Atan ty -> funCall (specializeMathFunName ty "atan") [e] + Cos ty -> funCall (specializeMathFunName ty "cos") [e] + Sin ty -> funCall (specializeMathFunName ty "sin") [e] + Tan ty -> funCall (specializeMathFunName ty "tan") [e] + Acosh ty -> funCall (specializeMathFunName ty "acosh") [e] + Asinh ty -> funCall (specializeMathFunName ty "asinh") [e] + Atanh ty -> funCall (specializeMathFunName ty "atanh") [e] + Cosh ty -> funCall (specializeMathFunName ty "cosh") [e] + Sinh ty -> funCall (specializeMathFunName ty "sinh") [e] + Tanh ty -> funCall (specializeMathFunName ty "tanh") [e] + Exp ty -> funCall (specializeMathFunName ty "exp") [e] + Log ty -> funCall (specializeMathFunName ty "log") [e] + Sqrt ty -> funCall (specializeMathFunName ty "sqrt") [e] + Ceiling ty -> funCall (specializeMathFunName ty "ceil") [e] + Floor ty -> funCall (specializeMathFunName ty "floor") [e] BwNot _ -> (C..~) e - Cast _ ty -> C.Cast (transtypename ty) e + Cast _ ty -> C.Cast (transTypeName ty) e GetField (Struct _) _ f -> C.Dot e (accessorname f) -- | Translates a Copilot binary operator and its arguments into a C99 -- expression. -transop2 :: Op2 a b c -> C.Expr -> C.Expr -> C.Expr -transop2 op e1 e2 = case op of +transOp2 :: Op2 a b c -> C.Expr -> C.Expr -> C.Expr +transOp2 op e1 e2 = case op of And -> e1 C..&& e2 Or -> e1 C..|| e2 Add _ -> e1 C..+ e2 @@ -98,10 +110,10 @@ transop2 op e1 e2 = case op of Mod _ -> e1 C..% e2 Div _ -> e1 C../ e2 Fdiv _ -> e1 C../ e2 - Pow ty -> funcall (specializeMathFunName ty "pow") [e1, e2] - Logb ty -> funcall (specializeMathFunName ty "log") [e2] C../ - funcall (specializeMathFunName ty "log") [e1] - Atan2 ty -> funcall (specializeMathFunName ty "atan2") [e1, e2] + Pow ty -> funCall (specializeMathFunName ty "pow") [e1, e2] + Logb ty -> funCall (specializeMathFunName ty "log") [e2] C../ + funCall (specializeMathFunName ty "log") [e1] + Atan2 ty -> funCall (specializeMathFunName ty "atan2") [e1, e2] Eq _ -> e1 C..== e2 Ne _ -> e1 C..!= e2 Le _ -> e1 C..<= e2 @@ -117,8 +129,8 @@ transop2 op e1 e2 = case op of -- | Translates a Copilot ternary operator and its arguments into a C99 -- expression. -transop3 :: Op3 a b c d -> C.Expr -> C.Expr -> C.Expr -> C.Expr -transop3 op e1 e2 e3 = case op of +transOp3 :: Op3 a b c d -> C.Expr -> C.Expr -> C.Expr -> C.Expr +transOp3 op e1 e2 e3 = case op of Mux _ -> C.Cond e1 e2 e3 -- | Translate @'Abs' e@ in Copilot Core into a C99 expression. @@ -135,7 +147,7 @@ transAbs :: Type a -> C.Expr -> C.Expr transAbs ty e -- Abs for floats/doubles is called fabs in C99's math.h. | typeIsFloating ty - = funcall (specializeMathFunName ty "fabs") [e] + = funCall (specializeMathFunName ty "fabs") [e] -- C99 provides multiple implementations of abs, depending on the type of -- the arguments. For integers, it provides C99 abs, labs, and llabs, which @@ -213,32 +225,30 @@ transSign ty e = positiveCase $ negativeCase e -- | Transform a Copilot Core literal, based on its value and type, into a C99 -- literal. -constty :: Type a -> a -> C.Expr -constty ty = case ty of +constTy :: Type a -> a -> C.Expr +constTy ty = case ty of Bool -> C.LitBool - Int8 -> explicitty ty . C.LitInt . fromIntegral - Int16 -> explicitty ty . C.LitInt . fromIntegral - Int32 -> explicitty ty . C.LitInt . fromIntegral - Int64 -> explicitty ty . C.LitInt . fromIntegral - Word8 -> explicitty ty . C.LitInt . fromIntegral - Word16 -> explicitty ty . C.LitInt . fromIntegral - Word32 -> explicitty ty . C.LitInt . fromIntegral - Word64 -> explicitty ty . C.LitInt . fromIntegral - Float -> explicitty ty . C.LitFloat - Double -> explicitty ty . C.LitDouble - Struct _ -> \v -> - C.InitVal (transtypename ty) (constStruct (toValues v)) - Array ty' -> \v -> - C.InitVal (transtypename ty) (constarray ty' (arrayelems v)) + Int8 -> explicitTy ty . C.LitInt . fromIntegral + Int16 -> explicitTy ty . C.LitInt . fromIntegral + Int32 -> explicitTy ty . C.LitInt . fromIntegral + Int64 -> explicitTy ty . C.LitInt . fromIntegral + Word8 -> explicitTy ty . C.LitInt . fromIntegral + Word16 -> explicitTy ty . C.LitInt . fromIntegral + Word32 -> explicitTy ty . C.LitInt . fromIntegral + Word64 -> explicitTy ty . C.LitInt . fromIntegral + Float -> explicitTy ty . C.LitFloat + Double -> explicitTy ty . C.LitDouble + Struct _ -> C.InitVal (transTypeName ty) . constStruct . toValues + Array ty' -> C.InitVal (transTypeName ty) . constArray ty' . arrayelems -- | Transform a Copilot Core literal, based on its value and type, into a C99 -- initializer. -constinit :: Type a -> a -> C.Init -constinit ty val = case ty of - -- We include two special cases for Struct and Array to avoid using constty +constInit :: Type a -> a -> C.Init +constInit ty val = case ty of + -- We include two special cases for Struct and Array to avoid using constTy -- on them. -- - -- In the default case (i.e., InitExpr (constty ty val)), constant + -- In the default case (i.e., InitExpr (constTy ty val)), constant -- initializations are explicitly cast. However, doing so 1) may result in -- incorrect values for arrays, and 2) will be considered a non-constant -- expression in the case of arrays and structs, and thus not allowed as the @@ -255,7 +265,7 @@ constinit ty val = case ty of -- whole expression as an array of two int32_t's (as opposed to a nested -- array). This can either lead to compile-time errors (if you're lucky) or -- incorrect runtime semantics (if you're unlucky). - Array ty' -> C.InitList $ constarray ty' $ arrayelems val + Array ty' -> C.InitList $ constArray ty' $ arrayelems val -- We use InitArray to initialize a struct because the syntax used for -- initializing arrays and structs is compatible. For instance, {1, 2} works @@ -263,59 +273,26 @@ constinit ty val = case ty of -- two int fields, although the two expressions are conceptually different -- (structs can also be initialized as { .a = 1, .b = 2}. Struct _ -> C.InitList $ constStruct (toValues val) - _ -> C.InitExpr $ constty ty val + _ -> C.InitExpr $ constTy ty val -- | Transform a Copilot Core struct field into a C99 initializer. -constfieldinit :: Value a -> C.InitItem -constfieldinit (Value ty (Field val)) = C.InitItem Nothing $ constinit ty val +constFieldInit :: Value a -> C.InitItem +constFieldInit (Value ty (Field val)) = C.InitItem Nothing $ constInit ty val -- | Transform a Copilot Struct, based on the struct fields, into a list of C99 -- initializer values. constStruct :: [Value a] -> NonEmpty.NonEmpty C.InitItem -constStruct val = NonEmpty.fromList $ map constfieldinit val +constStruct val = NonEmpty.fromList $ map constFieldInit val -- | Transform a Copilot Array, based on the element values and their type, -- into a list of C99 initializer values. -constarray :: Type a -> [a] -> NonEmpty.NonEmpty C.InitItem -constarray ty = - NonEmpty.fromList . map (C.InitItem Nothing . constinit ty) +constArray :: Type a -> [a] -> NonEmpty.NonEmpty C.InitItem +constArray ty = + NonEmpty.fromList . map (C.InitItem Nothing . constInit ty) -- | Explicitly cast a C99 value to a type. -explicitty :: Type a -> C.Expr -> C.Expr -explicitty ty = C.Cast (transtypename ty) - --- | Translate a Copilot type to a C99 type. -transtype :: Type a -> C.Type -transtype ty = case ty of - Bool -> C.TypeSpec $ C.TypedefName "bool" - Int8 -> C.TypeSpec $ C.TypedefName "int8_t" - Int16 -> C.TypeSpec $ C.TypedefName "int16_t" - Int32 -> C.TypeSpec $ C.TypedefName "int32_t" - Int64 -> C.TypeSpec $ C.TypedefName "int64_t" - Word8 -> C.TypeSpec $ C.TypedefName "uint8_t" - Word16 -> C.TypeSpec $ C.TypedefName "uint16_t" - Word32 -> C.TypeSpec $ C.TypedefName "uint32_t" - Word64 -> C.TypeSpec $ C.TypedefName "uint64_t" - Float -> C.TypeSpec C.Float - Double -> C.TypeSpec C.Double - Array ty' -> C.Array (transtype ty') length - where - length = Just $ C.LitInt $ fromIntegral $ tylength ty - Struct s -> C.TypeSpec $ C.Struct (typename s) - --- | Translate a Copilot type to a valid (local) variable declaration C99 type. --- --- If the type denotes an array, translate it to a pointer to whatever the --- array holds. This special case is needed when the type is used for a local --- variable declaration. We treat global variables differently (we generate --- list initializers). -transLocalVarDeclType :: Type a -> C.Type -transLocalVarDeclType (Array ty') = C.Ptr $ transtype ty' -transLocalVarDeclType ty = transtype ty - --- | Translate a Copilot type intro a C typename -transtypename :: Type a -> C.TypeName -transtypename ty = C.TypeName $ transtype ty +explicitTy :: Type a -> C.Expr -> C.Expr +explicitTy ty = C.Cast (transTypeName ty) -- Translate a literal number of type @ty@ into a C99 literal. -- @@ -387,3 +364,14 @@ typeIsFloating :: Type a -> Bool typeIsFloating Float = True typeIsFloating Double = True typeIsFloating _ = False + +-- | Auxiliary type used to collect all the declarations of all the variables +-- used in a function to be generated, since variable declarations are always +-- listed first at the top of the function body. +type FunEnv = [C.Decln] + +-- | Define a C expression that calls a function with arguments. +funCall :: C.Ident -- ^ Function name + -> [C.Expr] -- ^ Arguments + -> C.Expr +funCall name = C.Funcall (C.Ident name) diff --git a/copilot-c99/src/Copilot/Compile/C99/External.hs b/copilot-c99/src/Copilot/Compile/C99/External.hs index 0d75fafd..8d808045 100644 --- a/copilot-c99/src/Copilot/Compile/C99/External.hs +++ b/copilot-c99/src/Copilot/Compile/C99/External.hs @@ -2,53 +2,61 @@ -- | Represent information about externs needed in the generation of C99 code -- for stream declarations and triggers. -module Copilot.Compile.C99.External where +module Copilot.Compile.C99.External + ( External(..) + , gatherExts + ) + where +-- External imports import Data.List (unionBy) -import Copilot.Core -import Copilot.Compile.C99.Util +-- Internal imports: Copilot +import Copilot.Core ( Expr (..), Stream (..), Trigger (..), Type, UExpr (..) ) + +-- Internal imports +import Copilot.Compile.C99.Name ( exCpyName ) -- | Representation of external variables. data External = forall a. External - { extname :: String - , extcpyname :: String - , exttype :: Type a + { extName :: String + , extCpyName :: String + , extType :: Type a } --- | Union over lists of External, we solely base the equality on the --- extname's. -extunion :: [External] -> [External] -> [External] -extunion = unionBy (\a b -> extname a == extname b) - -- | Collect all external variables from the streams and triggers. -- -- Although Copilot specifications can contain also properties and theorems, -- the C99 backend currently only generates code for streams and triggers. -gatherexts :: [Stream] -> [Trigger] -> [External] -gatherexts streams triggers = streamsexts `extunion` triggersexts +gatherExts :: [Stream] -> [Trigger] -> [External] +gatherExts streams triggers = streamsExts `extUnion` triggersExts where - streamsexts = foldr extunion mempty $ map streamexts streams - triggersexts = foldr extunion mempty $ map triggerexts triggers + streamsExts = foldr (extUnion . streamExts) mempty streams + triggersExts = foldr (extUnion . triggerExts) mempty triggers - streamexts :: Stream -> [External] - streamexts (Stream _ _ expr _) = exprexts expr + streamExts :: Stream -> [External] + streamExts (Stream _ _ expr _) = exprExts expr - triggerexts :: Trigger -> [External] - triggerexts (Trigger _ guard args) = guardexts `extunion` argexts + triggerExts :: Trigger -> [External] + triggerExts (Trigger _ guard args) = guardExts `extUnion` argExts where - guardexts = exprexts guard - argexts = concat $ map uexprexts args - - uexprexts :: UExpr -> [External] - uexprexts (UExpr _ expr) = exprexts expr - - exprexts :: Expr a -> [External] - exprexts expr = let rec = exprexts in case expr of - Local _ _ _ e1 e2 -> rec e1 `extunion` rec e2 - ExternVar ty name _ -> [External name (excpyname name) ty] - Op1 _ e -> rec e - Op2 _ e1 e2 -> rec e1 `extunion` rec e2 - Op3 _ e1 e2 e3 -> rec e1 `extunion` rec e2 `extunion` rec e3 - Label _ _ e -> rec e - _ -> [] + guardExts = exprExts guard + argExts = concatMap uExprExts args + + uExprExts :: UExpr -> [External] + uExprExts (UExpr _ expr) = exprExts expr + + exprExts :: Expr a -> [External] + exprExts (Local _ _ _ e1 e2) = exprExts e1 `extUnion` exprExts e2 + exprExts (ExternVar ty name _) = [External name (exCpyName name) ty] + exprExts (Op1 _ e) = exprExts e + exprExts (Op2 _ e1 e2) = exprExts e1 `extUnion` exprExts e2 + exprExts (Op3 _ e1 e2 e3) = exprExts e1 `extUnion` exprExts e2 + `extUnion` exprExts e3 + exprExts (Label _ _ e) = exprExts e + exprExts _ = [] + + -- | Union over lists of External, we solely base the equality on the + -- extName's. + extUnion :: [External] -> [External] -> [External] + extUnion = unionBy (\a b -> extName a == extName b) diff --git a/copilot-c99/src/Copilot/Compile/C99/Name.hs b/copilot-c99/src/Copilot/Compile/C99/Name.hs new file mode 100644 index 00000000..bebaf6be --- /dev/null +++ b/copilot-c99/src/Copilot/Compile/C99/Name.hs @@ -0,0 +1,61 @@ +-- | Naming of variables and functions in C. +module Copilot.Compile.C99.Name + ( argNames + , argTempNames + , exCpyName + , generatorName + , generatorOutputArgName + , guardName + , indexName + , streamAccessorName + , streamName + ) + where + +-- External imports: Copilot +import Copilot.Core (Id) + +-- | Turn a stream id into a suitable C variable name. +streamName :: Id -> String +streamName sId = "s" ++ show sId + +-- | Turn a stream id into the global varname for indices. +indexName :: Id -> String +indexName sId = streamName sId ++ "_idx" + +-- | Turn a stream id into the name of its accessor function +streamAccessorName :: Id -> String +streamAccessorName sId = streamName sId ++ "_get" + +-- | Add a postfix for copies of external variables the name. +exCpyName :: String -> String +exCpyName name = name ++ "_cpy" + +-- | Turn stream id into name of its generator function. +generatorName :: Id -> String +generatorName sId = streamName sId ++ "_gen" + +-- | Turn stream id into name of its output argument array. +generatorOutputArgName :: Id -> String +generatorOutputArgName sId = streamName sId ++ "_output" + +-- | Turn the name of a trigger into a guard generator. +guardName :: String -> String +guardName name = name ++ "_guard" + +-- | Turn a trigger name into a trigger argument name. +argName :: String -> Int -> String +argName name n = name ++ "_arg" ++ show n + +-- | Turn a handler function name into a name for a temporary variable for a +-- handler argument. +argTempName :: String -> Int -> String +argTempName name n = name ++ "_arg_temp" ++ show n + +-- | Enumerate all argument names based on trigger name. +argNames :: String -> [String] +argNames base = map (argName base) [0..] + +-- | Enumerate all temporary variable names based on handler function name. +argTempNames :: String -> [String] +argTempNames base = map (argTempName base) [0..] diff --git a/copilot-c99/src/Copilot/Compile/C99/Settings.hs b/copilot-c99/src/Copilot/Compile/C99/Settings.hs index 782096e5..bb52271a 100644 --- a/copilot-c99/src/Copilot/Compile/C99/Settings.hs +++ b/copilot-c99/src/Copilot/Compile/C99/Settings.hs @@ -1,5 +1,9 @@ -- | Settings used by the code generator to customize the code. -module Copilot.Compile.C99.Settings where +module Copilot.Compile.C99.Settings + ( CSettings(..) + , mkDefaultCSettings + ) + where -- | Settings used to customize the code generated. data CSettings = CSettings diff --git a/copilot-c99/src/Copilot/Compile/C99/Type.hs b/copilot-c99/src/Copilot/Compile/C99/Type.hs new file mode 100644 index 00000000..f94dbac6 --- /dev/null +++ b/copilot-c99/src/Copilot/Compile/C99/Type.hs @@ -0,0 +1,48 @@ +{-# LANGUAGE GADTs #-} + +-- | Translate Copilot Core expressions and operators to C99. +module Copilot.Compile.C99.Type + ( transType + , transLocalVarDeclType + , transTypeName + ) + where + +-- External imports +import qualified Language.C99.Simple as C + +-- Internal imports: Copilot +import Copilot.Core ( Type (..), tylength, typename ) + +-- | Translate a Copilot type to a C99 type. +transType :: Type a -> C.Type +transType ty = case ty of + Bool -> C.TypeSpec $ C.TypedefName "bool" + Int8 -> C.TypeSpec $ C.TypedefName "int8_t" + Int16 -> C.TypeSpec $ C.TypedefName "int16_t" + Int32 -> C.TypeSpec $ C.TypedefName "int32_t" + Int64 -> C.TypeSpec $ C.TypedefName "int64_t" + Word8 -> C.TypeSpec $ C.TypedefName "uint8_t" + Word16 -> C.TypeSpec $ C.TypedefName "uint16_t" + Word32 -> C.TypeSpec $ C.TypedefName "uint32_t" + Word64 -> C.TypeSpec $ C.TypedefName "uint64_t" + Float -> C.TypeSpec C.Float + Double -> C.TypeSpec C.Double + Array ty' -> C.Array (transType ty') len + where + len = Just $ C.LitInt $ fromIntegral $ tylength ty + Struct s -> C.TypeSpec $ C.Struct (typename s) + +-- | Translate a Copilot type to a valid (local) variable declaration C99 type. +-- +-- If the type denotes an array, translate it to a pointer to whatever the +-- array holds. This special case is needed when the type is used for a local +-- variable declaration. We treat global variables differently (we generate +-- list initializers). +transLocalVarDeclType :: Type a -> C.Type +transLocalVarDeclType (Array ty') = C.Ptr $ transType ty' +transLocalVarDeclType ty = transType ty + +-- | Translate a Copilot type intro a C typename +transTypeName :: Type a -> C.TypeName +transTypeName ty = C.TypeName $ transType ty diff --git a/copilot-c99/src/Copilot/Compile/C99/Util.hs b/copilot-c99/src/Copilot/Compile/C99/Util.hs deleted file mode 100644 index 10d6f9a8..00000000 --- a/copilot-c99/src/Copilot/Compile/C99/Util.hs +++ /dev/null @@ -1,77 +0,0 @@ --- | Auxiliary helper functions to generate C99 code. -module Copilot.Compile.C99.Util where - -import Control.Monad.State - -import Copilot.Core (Id) -import qualified Language.C99.Simple.AST as C - --- | Auxiliary type used to collect all the declarations of all the variables --- used in a function to be generated, since variable declarations are always --- listed first at the top of the function body. -type FunEnv = [C.Decln] - --- | `tell` equivalent for `State`. -statetell :: Monoid m => m -> State m () -statetell m = modify ((flip mappend) m) - --- | Generate fresh variable name based on a given one. -fresh :: String -> [String] -> String -fresh name used = head $ dropWhile (flip elem used) (name:freshnames) - where - freshnames = (name ++).show <$> [0..] - --- | Collect all the names from a list of C99 declarations. -names :: [C.Decln] -> [String] -names ds = map match ds - where - match (C.VarDecln _ _ name _) = name - --- | Turn a stream id into a suitable C variable name. -streamname :: Id -> String -streamname sid = "s" ++ show sid - --- | Turn a stream id into the global varname for indices. -indexname :: Id -> String -indexname sid = streamname sid ++ "_idx" - --- | Turn a stream id into the name of its accessor function -streamaccessorname :: Id -> String -streamaccessorname sid = streamname sid ++ "_get" - --- | Add a postfix for copies of external variables the name. -excpyname :: String -> String -excpyname name = name ++ "_cpy" - --- | Turn stream id into name of its generator function. -generatorname :: Id -> String -generatorname sid = streamname sid ++ "_gen" - --- | Turn stream id into name of its output argument array. -generatorOutputArgName :: Id -> String -generatorOutputArgName sid = streamname sid ++ "_output" - --- | Turn the name of a trigger into a guard generator. -guardname :: String -> String -guardname name = name ++ "_guard" - --- | Turn a trigger name into a an trigger argument name. -argname :: String -> Int -> String -argname name n = name ++ "_arg" ++ show n - --- | Turn a handler function name into a name for a temporary variable for a --- handler argument. -argTempName :: String -> Int -> String -argTempName name n = name ++ "_arg_temp" ++ show n - --- | Enumerate all argument names based on trigger name. -argnames :: String -> [String] -argnames base = [aname | n <- [0..], let aname = argname base n] - --- | Enumerate all temporary variable names based on handler function name. -argTempNames :: String -> [String] -argTempNames base = map (argTempName base) [0..] - --- | Define a C expression that calls a function with arguments. -funcall :: C.Ident -> [C.Expr] -> C.Expr -funcall name args = C.Funcall (C.Ident name) args