{- Tock: a compiler for parallel languages Copyright (C) 2007, 2008 University of Kent 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 2 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 . -} -- | Generate C code from the mangled AST. Most of the exports here are actually -- for GenerateCPPCSP to use module GenerateC ( cgenOps , cgenReplicatorLoop , cgenType , cintroduceSpec , cPreReq , genComma , genCPasses , generate , generateC , genLeftB , genMeta , genName , genRightB , seqComma , withIf ) where import Data.Char import Data.Generics import Data.List import Data.Maybe import qualified Data.Set as Set import Control.Monad.State import System.IO import Text.Printf import Text.Regex import qualified AST as A import BackendPasses import CompState import Errors import EvalConstants import EvalLiterals import GenerateCBased import Metadata import Pass import qualified Properties as Prop import ShowCode import TLP import Types import Utils --{{{ passes related to C generation genCPasses :: [Pass] genCPasses = makePassesDep' ((== BackendC) . csBackend) [ ("Transform wait for guards into wait until guards", transformWaitFor, [], [Prop.waitForRemoved]) ] --}}} cPreReq :: [Property] cPreReq = cCppCommonPreReq ++ [Prop.parsIdentified, Prop.waitForRemoved] --{{{ generator ops -- | Operations for the C backend. cgenOps :: GenOps cgenOps = GenOps { declareFree = cdeclareFree, declareInit = cdeclareInit, genActual = cgenActual, genActuals = cgenActuals, genAlt = cgenAlt, genAllocMobile = cgenAllocMobile, genArrayLiteralElems = cgenArrayLiteralElems, genArrayStoreName = genName, genArraySubscript = cgenArraySubscript, genAssert = cgenAssert, genAssign = cgenAssign, genBytesIn = cgenBytesIn, genCase = cgenCase, genCheckedConversion = cgenCheckedConversion, genClearMobile = cgenClearMobile, genConversion = cgenConversion, genConversionSymbol = cgenConversionSymbol, genDecl = cgenDecl, genDeclType = cgenDeclType, genDeclaration = cgenDeclaration, genDirectedVariable = cgenDirectedVariable, genDyadic = cgenDyadic, genExpression = cgenExpression, genFlatArraySize = cgenFlatArraySize, genForwardDeclaration = cgenForwardDeclaration, genFuncDyadic = cgenFuncDyadic, genFuncMonadic = cgenFuncMonadic, genGetTime = cgenGetTime, genIf = cgenIf, genInput = cgenInput, genInputItem = cgenInputItem, genIntrinsicFunction = cgenIntrinsicFunction, genIntrinsicProc = cgenIntrinsicProc, genListAssign = cgenListAssign, genListConcat = cgenListConcat, genListLiteral = cgenListLiteral, genListSize = cgenListSize, genLiteral = cgenLiteral, genLiteralRepr = cgenLiteralRepr, genMissing = cgenMissing, genMissingC = (\x -> x >>= cgenMissing), genMonadic = cgenMonadic, genOutput = cgenOutput, genOutputCase = cgenOutputCase, genOutputItem = cgenOutputItem, genOverArray = cgenOverArray, genPar = cgenPar, genProcCall = cgenProcCall, genProcess = cgenProcess, genRecordTypeSpec = cgenRecordTypeSpec, genReplicator = cgenReplicator, genReplicatorLoop = cgenReplicatorLoop, genRetypeSizes = cgenRetypeSizes, genSeq = cgenSeq, genSimpleDyadic = cgenSimpleDyadic, genSimpleMonadic = cgenSimpleMonadic, genSizeSuffix = cgenSizeSuffix, genSpec = cgenSpec, genSpecMode = cgenSpecMode, genStop = cgenStop, genStructured = cgenStructured, genTimerRead = cgenTimerRead, genTimerWait = cgenTimerWait, genTopLevel = cgenTopLevel, genType = cgenType, genTypeSymbol = cgenTypeSymbol, genUnfoldedExpression = cgenUnfoldedExpression, genUnfoldedVariable = cgenUnfoldedVariable, genVariable = cgenVariable, genVariableAM = cgenVariableAM, genVariableUnchecked = cgenVariableUnchecked, genWhile = cgenWhile, getScalarType = cgetScalarType, introduceSpec = cintroduceSpec, removeSpec = cremoveSpec } --}}} --{{{ top-level generateC :: Handle -> A.AST -> PassM () generateC = generate cgenOps cgenTopLevel :: A.AST -> CGen () cgenTopLevel s = do tell ["#include \n"] cs <- getCompState (tlpName, tlpChans) <- tlpInterface chans <- sequence [csmLift $ makeNonce "tlp_channel" | _ <- tlpChans] killChans <- sequence [csmLift $ makeNonce "tlp_channel_kill" | _ <- tlpChans] workspaces <- sequence [csmLift $ makeNonce "tlp_channel_ws" | _ <- tlpChans] sequence_ $ map (call genForwardDeclaration) (listify (const True :: A.Specification -> Bool) s) sequence_ [tell ["extern int ", nameString n, "_stack_size;\n"] | n <- Set.toList $ csParProcs cs] tell ["extern int "] genName tlpName tell ["_stack_size;\n"] call genStructured s (\m _ -> tell ["\n#error Invalid top-level item: ", show m]) tell ["void tock_main (Workspace wptr) {\n"] sequence_ [do tell [" Channel ", c, ";\n"] tell [" ChanInit (wptr, &", c, ");\n"] | c <- chans ++ killChans] tell ["\n"] funcs <- sequence [genTLPHandler tc c kc ws | (tc, c, kc, ws) <- zip4 tlpChans chans killChans workspaces] tell [" LightProcBarrier bar;\n\ \ LightProcBarrierInit (wptr, &bar, ", show $ length chans, ");\n"] sequence_ [tell [" LightProcStart (wptr, &bar, ", ws, ", (Process) ", func, ");\n"] | (ws, func) <- zip workspaces funcs] tell ["\n\ \ "] genName tlpName tell [" (wptr"] sequence_ [tell [", &", c] | c <- chans] tell [");\n\ \\n"] sequence_ [tell [" ", func, "_kill (wptr, &", kc, ");\n"] | (func, kc) <- zip funcs killChans] let uses_stdin = if TLPIn `elem` (map snd tlpChans) then "true" else "false" tell [" LightProcBarrierWait (wptr, &bar);\n\ \\n\ \ Shutdown (wptr);\n\ \}\n\ \\n\ \int main (int argc, char *argv[]) {\n\ \ tock_init_ccsp (", uses_stdin, ");\n\ \\n\ \ Workspace p = ProcAllocInitial (0, "] genName tlpName tell ["_stack_size + 512);\n\ \ ProcStartInitial (p, tock_main);\n\ \\n\ \ // NOTREACHED\n\ \ return 0;\n\ \}\n"] where -- | Allocate a TLP channel handler process, and return the function that -- implements it. genTLPHandler :: (A.Direction, TLPChannel) -> String -> String -> String -> CGen String genTLPHandler (_, tc) c kc ws = do tell [" Workspace ", ws, " = ProcAlloc (wptr, 3, 1024);\n\ \ ProcParam (wptr, ", ws, ", 0, &", c, ");\n\ \ ProcParam (wptr, ", ws, ", 1, &", kc, ");\n\ \ ProcParam (wptr, ", ws, ", 2, ", fp, ");\n\ \\n"] return func where (fp, func) = case tc of TLPIn -> ("stdin", "tock_tlp_input") TLPOut -> ("stdout", "tock_tlp_output") TLPError -> ("stderr", "tock_tlp_output") --}}} --{{{ utilities cgenMissing :: String -> CGen () cgenMissing s = tell ["\n#error Unimplemented: ", s, "\n"] --{{{ simple punctuation genComma :: CGen () genComma = tell [","] seqComma :: [CGen ()] -> CGen () seqComma ps = sequence_ $ intersperse genComma ps genLeftB :: CGen () genLeftB = tell ["{"] genRightB :: CGen () genRightB = tell ["}"] --}}} -- | Map an operation over every item of an occam array. cgenOverArray :: Meta -> A.Variable -> (SubscripterFunction -> Maybe (CGen ())) -> CGen () cgenOverArray m var func = do A.Array ds _ <- typeOfVariable var specs <- sequence [csmLift $ makeNonceVariable "i" m A.Int A.VariableName A.Original | _ <- ds] let indices = [A.Variable m n | A.Specification _ n _ <- specs] let arg = (\var -> foldl (\v s -> A.SubscriptedVariable m s v) var [A.Subscript m A.NoCheck $ A.ExprVariable m i | i <- indices]) case func arg of Just p -> do sequence_ [do tell ["for(int "] call genVariable i tell ["=0;"] call genVariable i tell ["<"] case d of A.UnknownDimension -> do call genVariable var call genSizeSuffix (show v) A.Dimension n -> call genExpression n tell [";"] call genVariable i tell ["++){"] | (v :: Integer, i, d) <- zip3 [0..] indices ds] p sequence_ [tell ["}"] | _ <- indices] Nothing -> return () -- | Generate code for one of the Structured types. cgenStructured :: Data a => A.Structured a -> (Meta -> a -> CGen ()) -> CGen () cgenStructured (A.Rep _ rep s) def = call genReplicator rep (call genStructured s def) cgenStructured (A.Spec _ spec s) def = call genSpec spec (call genStructured s def) cgenStructured (A.ProcThen _ p s) def = call genProcess p >> call genStructured s def cgenStructured (A.Several _ ss) def = sequence_ [call genStructured s def | s <- ss] cgenStructured (A.Only m s) def = def m s --}}} --{{{ metadata -- | Turn a Meta into a string literal that can be passed to a function -- expecting a const char * argument. genMeta :: Meta -> CGen () genMeta m = tell ["\"", show m, "\""] --}}} --{{{ names nameString :: A.Name -> String nameString n = [if c == '.' then '_' else c | c <- A.nameName n] genName :: A.Name -> CGen () genName n = tell [nameString n] --}}} --{{{ types -- | If a type maps to a simple C type, return Just that; else return Nothing. cgetScalarType :: A.Type -> Maybe String cgetScalarType A.Bool = Just "bool" cgetScalarType A.Byte = Just "uint8_t" cgetScalarType A.UInt16 = Just "uint16_t" cgetScalarType A.UInt32 = Just "uint32_t" cgetScalarType A.UInt64 = Just "uint64_t" cgetScalarType A.Int8 = Just "int8_t" cgetScalarType A.Int = Just "int" cgetScalarType A.Int16 = Just "int16_t" cgetScalarType A.Int32 = Just "int32_t" cgetScalarType A.Int64 = Just "int64_t" cgetScalarType A.Real32 = Just "float" cgetScalarType A.Real64 = Just "double" cgetScalarType (A.Timer A.OccamTimer) = Just "Time" cgetScalarType A.Time = Just "Time" cgetScalarType _ = Nothing -- | Generate the C type corresponding to a variable being declared. -- It must be possible to use this in arrays. cgenType :: A.Type -> CGen () cgenType (A.Array _ t) = do call genType t case t of A.Chan A.DirUnknown _ _ -> tell ["*"] _ -> return () tell ["*"] cgenType (A.Record n) = genName n cgenType (A.Mobile t@(A.Array {})) = call genType t cgenType (A.Mobile t) = call genType t >> tell ["*"] -- UserProtocol -- not used -- Channels are of type "Channel", but channel-ends are of type "Channel*" cgenType (A.Chan A.DirUnknown _ t) = tell ["Channel"] cgenType (A.Chan _ _ t) = tell ["Channel*"] -- Counted -- not used -- Any -- not used --cgenType (A.Port t) = --TODO have a pass that declares these list types: cgenType t@(A.List {}) = tell [subRegex (mkRegex "[^A-Za-z0-9]") (show t) ""] cgenType t = do f <- fget getScalarType case f t of Just s -> tell [s] Nothing -> call genMissingC $ formatCode "genType %" t indexOfFreeDimensions :: [A.Dimension] -> [Int] indexOfFreeDimensions = (mapMaybe indexOfFreeDimensions') . (zip [0..]) where indexOfFreeDimensions' :: (Int,A.Dimension) -> Maybe Int indexOfFreeDimensions' (_, A.Dimension _) = Nothing indexOfFreeDimensions' (n, A.UnknownDimension) = Just n -- | Generate the number of bytes in a type. cgenBytesIn :: Meta -> A.Type -> Either Bool A.Variable -> CGen () cgenBytesIn m t v = do case (t, v) of (A.Array ds _, Left freeDimensionAllowed) -> case (length (indexOfFreeDimensions ds), freeDimensionAllowed) of (0,_) -> return () (1,False) -> dieP m "genBytesIn type with unknown dimension, when unknown dimensions are not allowed" (1,True) -> return () (_,_) -> dieP m "genBytesIn type with more than one free dimension" _ -> return () genBytesIn' t where genBytesIn' :: A.Type -> CGen () genBytesIn' (A.Array ds t) = do mapM_ genBytesInArrayDim (reverse $ zip ds [0..]) --The reverse is simply to match the existing tests genBytesIn' t genBytesIn' (A.Record n) = do tell ["sizeof("] genName n tell [")"] -- This is so that we can do RETYPES checks on channels; we don't actually -- allow retyping between channels and other things. genBytesIn' t@(A.Chan {}) = do tell ["sizeof("] call genType t tell [")"] genBytesIn' t = do f <- fget getScalarType case f t of Just s -> tell ["sizeof(", s, ")"] Nothing -> diePC m $ formatCode "genBytesIn' %" t -- FIXME: This could be done by generating an expression for the size, -- which is what declareSizesPass has to do -- they should share a helper -- function. genBytesInArrayDim :: (A.Dimension,Int) -> CGen () genBytesInArrayDim (A.Dimension n, _) = do call genExpression n tell ["*"] genBytesInArrayDim (A.UnknownDimension, i) = case v of Right rv -> do call genVariable rv call genSizeSuffix (show i) tell ["*"] _ -> return () --}}} --{{{ declarations cgenDeclType :: A.AbbrevMode -> A.Type -> CGen () cgenDeclType am t = do when (am == A.ValAbbrev) $ tell ["const "] call genType t case t of A.Array _ _ -> return () A.Chan A.DirInput _ _ -> return () A.Chan A.DirOutput _ _ -> return () A.Record _ -> tell ["*const"] _ -> when (am == A.Abbrev) $ tell ["*const"] cgenDecl :: A.AbbrevMode -> A.Type -> A.Name -> CGen () cgenDecl am t n = do call genDeclType am t tell [" "] genName n --}}} --{{{ conversions cgenCheckedConversion :: Meta -> A.Type -> A.Type -> CGen () -> CGen () cgenCheckedConversion m fromT toT exp = do tell ["(("] call genType toT tell [") "] if isSafeConversion fromT toT then exp else do call genTypeSymbol "range_check" fromT tell [" ("] call genTypeSymbol "mostneg" toT tell [", "] call genTypeSymbol "mostpos" toT tell [", "] exp tell [", "] genMeta m tell [")"] tell [")"] cgenConversion :: Meta -> A.ConversionMode -> A.Type -> A.Expression -> CGen () cgenConversion m A.DefaultConversion toT e = do fromT <- typeOfExpression e call genCheckedConversion m fromT toT (call genExpression e) cgenConversion m cm toT e = do fromT <- typeOfExpression e case (isSafeConversion fromT toT, isRealType fromT, isRealType toT) of (True, _, _) -> -- A safe conversion -- no need for a check. call genCheckedConversion m fromT toT (call genExpression e) (_, True, True) -> -- Real to real. do call genConversionSymbol fromT toT cm tell [" ("] call genExpression e tell [", "] genMeta m tell [")"] (_, True, False) -> -- Real to integer -- do real -> int64_t -> int. do let exp = do call genConversionSymbol fromT A.Int64 cm tell [" ("] call genExpression e tell [", "] genMeta m tell [")"] call genCheckedConversion m A.Int64 toT exp (_, False, True) -> -- Integer to real -- do int -> int64_t -> real. do call genConversionSymbol A.Int64 toT cm tell [" ("] call genCheckedConversion m fromT A.Int64 (call genExpression e) tell [", "] genMeta m tell [")"] _ -> call genMissing $ "genConversion " ++ show cm cgenConversionSymbol :: A.Type -> A.Type -> A.ConversionMode -> CGen () cgenConversionSymbol fromT toT cm = do tell ["occam_convert_"] call genType fromT tell ["_"] call genType toT tell ["_"] case cm of A.Round -> tell ["round"] A.Trunc -> tell ["trunc"] --}}} --{{{ literals cgenLiteral :: A.LiteralRepr -> A.Type -> CGen () cgenLiteral lr t = if isStringLiteral lr then do tell ["\""] let A.ArrayLiteral _ aes = lr sequence_ [genByteLiteral m s | A.ArrayElemExpr (A.Literal _ _ (A.ByteLiteral m s)) <- aes] tell ["\""] else call genLiteralRepr lr t -- | Does a LiteralRepr represent something that can be a plain string literal? isStringLiteral :: A.LiteralRepr -> Bool isStringLiteral (A.ArrayLiteral _ []) = False isStringLiteral (A.ArrayLiteral _ aes) = and [case ae of A.ArrayElemExpr (A.Literal _ _ (A.ByteLiteral _ _)) -> True _ -> False | ae <- aes] isStringLiteral _ = False genLitSuffix :: A.Type -> CGen () genLitSuffix A.UInt32 = tell ["U"] genLitSuffix A.Int64 = tell ["LL"] genLitSuffix A.UInt64 = tell ["ULL"] genLitSuffix A.Real32 = tell ["F"] genLitSuffix _ = return () cgenListLiteral :: [A.Expression] -> A.Type -> CGen () cgenListLiteral _ _ = call genMissing "C backend does not yet support lists" cgenListSize :: A.Variable -> CGen () cgenListSize _ = call genMissing "C backend does not yet support lists" cgenListAssign :: A.Variable -> A.Expression -> CGen () cgenListAssign _ _ = call genMissing "C backend does not yet support lists" cgenLiteralRepr :: A.LiteralRepr -> A.Type -> CGen () cgenLiteralRepr (A.RealLiteral m s) t = tell [s] >> genLitSuffix t cgenLiteralRepr (A.IntLiteral m s) t = do genDecimal s genLitSuffix t cgenLiteralRepr (A.HexLiteral m s) t = do f <- fget getScalarType ct <- case f t of Just ct -> return ct Nothing -> diePC m $ formatCode "Non-scalar type for hex literal: " t tell ["((",ct,")0x", s] genLitSuffix t tell [")"] cgenLiteralRepr (A.ByteLiteral m s) _ = tell ["'"] >> genByteLiteral m s >> tell ["'"] cgenLiteralRepr (A.ArrayLiteral m aes) _ = do genLeftB call genArrayLiteralElems aes genRightB cgenLiteralRepr (A.RecordLiteral _ es) _ = do genLeftB seqComma $ map (call genUnfoldedExpression) es genRightB cgenLiteralRepr (A.ListLiteral _ es) t = call genListLiteral es t -- | Generate an expression inside a record literal. -- -- This is awkward: the sort of literal that this produces when there's a -- variable in here cannot always be compiled at the top level of a C99 program -- -- because in C99, an array subscript is not a constant, even if it's a -- constant subscript of a constant array. So we need to be sure that when we -- use this at the top level, the thing we're unfolding only contains literals. -- Yuck! cgenUnfoldedExpression :: A.Expression -> CGen () cgenUnfoldedExpression (A.Literal _ t lr) = do call genLiteralRepr lr t cgenUnfoldedExpression (A.ExprVariable m var) = call genUnfoldedVariable m var cgenUnfoldedExpression e = call genExpression e -- | Generate a variable inside a record literal. cgenUnfoldedVariable :: Meta -> A.Variable -> CGen () cgenUnfoldedVariable m var = do t <- typeOfVariable var case t of A.Array ds _ -> do genLeftB unfoldArray ds var genRightB A.Record _ -> do genLeftB fs <- recordFields m t seqComma [call genUnfoldedVariable m (A.SubscriptedVariable m (A.SubscriptField m n) var) | (n, t) <- fs] genRightB -- We can defeat the usage check here because we know it's safe; *we're* -- generating the subscripts. -- FIXME Is that actually true for something like [a[x]]? _ -> call genVariableUnchecked var where unfoldArray :: [A.Dimension] -> A.Variable -> CGen () unfoldArray [] v = call genUnfoldedVariable m v unfoldArray (A.Dimension e:ds) v = do n <- evalIntExpression e seqComma $ [unfoldArray ds (A.SubscriptedVariable m (A.Subscript m A.NoCheck $ makeConstant m i) v) | i <- [0..(n - 1)]] unfoldArray _ _ = dieP m "trying to unfold array with unknown dimension" -- | Generate a decimal literal -- removing leading zeroes to avoid producing -- an octal literal! genDecimal :: String -> CGen () genDecimal "0" = tell ["0"] genDecimal ('0':s) = genDecimal s genDecimal ('-':s) = tell ["-"] >> genDecimal s genDecimal s = tell [s] cgenArrayLiteralElems :: [A.ArrayElem] -> CGen () cgenArrayLiteralElems aes = seqComma $ map genElem aes where genElem :: A.ArrayElem -> CGen () genElem (A.ArrayElemArray aes) = call genArrayLiteralElems aes genElem (A.ArrayElemExpr e) = call genUnfoldedExpression e genByteLiteral :: Meta -> String -> CGen () genByteLiteral m s = do c <- evalByte m s tell [convByte c] convByte :: Char -> String convByte '\'' = "\\'" convByte '"' = "\\\"" convByte '\\' = "\\\\" convByte '\r' = "\\r" convByte '\n' = "\\n" convByte '\t' = "\\t" convByte c | o == 0 = "\\0" | (o < 32 || o > 127) = printf "\\%03o" o | otherwise = [c] where o = ord c --}}} --{{{ variables {- The various types are generated like this: ================= Use ================= Original ValAbbrev Abbrev -------------------------------------- INT x: int x; int x; int *x; x x x *x [10]INT xs: int xs[10]; int *xs; int *xs; xs xs xs xs xs[i] xs[i] xs[i] xs[i] [20][10]INT xss: int xss[20*10]; int *xss; int *xss; xss xss xss xss xss[i] &xss[i*10] &xss[i*10] &xss[i*10] (where 10 = xss_sizes[1]) xss[i][j] xss[i*10+j] xss[i*10+j] xss[i*10+j] [6][4][2]INT xsss: int xsss[6*4*2]; int *xsss; xsss xsss (as left) xsss[i] &xsss[i*4*2] xsss[i][j] &xsss[i*4*2+j*2] xsss[i][j][k] xsss[i*4*2+j*2+k] MYREC r: MYREC r; MYREC *r; MYREC *r; r &r r r r[F] (&r)->F (r)->F (r)->F [10]MYREC rs: MYREC rs[10]; MYREC *rs; MYREC *rs; rs rs rs rs rs[i] &rs[i] &rs[i] &rs[i] rs[i][F] (&rs[i])->F (&rs[i])->F (&rs[i])->F -- depending on what F is -- if it's another record... CHAN OF INT c: Channel c; Channel *c; c &c c [10]CHAN OF INT cs: Channel* cs[10]; Channel **cs; cs cs cs cs[i] cs[i] cs[i] I suspect there's probably a nicer way of doing this, but as a translation of the above table this isn't too horrible... -} -- | Generate C code for a variable. cgenVariable :: A.Variable -> CGen () cgenVariable = cgenVariable' True -- | Generate C code for a variable without doing any range checks. cgenVariableUnchecked :: A.Variable -> CGen () cgenVariableUnchecked = cgenVariable' False cgenVariable' :: Bool -> A.Variable -> CGen () cgenVariable' checkValid v = do (cg, n) <- inner 0 v Nothing addPrefix cg n where -- The general plan here is to generate the variable, while also -- putting in the right prefixes (&/*/**/***/etc). -- We use an "indirection level" to record the prefix needed. -- 0 means no prefix, -1 means &, 1 means *, 2 means **, etc -- For arrays, we must pass through the inner type of the array -- so that we can add the appropriate prefixes before the array -- name. That is, we make sure we write (&foo[0]), not -- (&foo)[0] inner :: Int -> A.Variable -> Maybe A.Type -> CGen (CGen (), Int) inner ind (A.Variable _ n) mt = do amN <- abbrevModeOfName n (am,t) <- case (amN,mt) of -- Channel arrays are special, because they are arrays of abbreviations: (_, Just t'@(A.Chan {})) -> return (A.Abbrev, t') -- If we are dealing with an array element, treat it as if it had the original abbreviation mode, -- regardless of the abbreviation mode of the array: (_, Just t') -> return (A.Original, t') (am,Nothing) -> do t <- typeOfName n return (am, t) let ind' = case (am, t, indirectedType t) of -- For types that are referred to by pointer (such as records) -- we need to take the address: (A.Original, _, True) -> ind - 1 -- If the type is referred to by pointer but is already abbreviated, -- no need to change the indirection: (_, _, True) -> ind -- Undirected channels will already have been handled, so this is for directed: (A.Abbrev, A.Chan {}, _) -> ind -- Abbreviations of arrays are pointers, just like arrays, so no -- need for a * operator: (A.Abbrev, A.Array {}, _) -> ind (A.Abbrev, _, _) -> ind + 1 _ -> ind return (genName n, ind') inner ind (A.DerefVariable _ v) mt = do (A.Mobile t) <- typeOfVariable v case t of A.Array {} -> inner ind v mt A.Record {} -> inner ind v mt _ -> inner (ind+1) v mt inner ind (A.DirectedVariable _ dir v) mt = do (cg,n) <- (inner ind v mt) return (call genDirectedVariable (addPrefix cg n) dir, 0) inner ind sv@(A.SubscriptedVariable m (A.Subscript _ subCheck _) v) mt = do (es, v, t') <- collectSubs sv t <- if checkValid then typeOfVariable sv else return t' A.Array ds _ <- typeOfVariable v (cg, n) <- inner ind v (Just t) let check = if checkValid then subCheck else A.NoCheck return ((if (length ds /= length es) then tell ["&"] else return ()) >> cg >> call genArraySubscript check v (map (\e -> (findMeta e, call genExpression e)) es), n) inner ind sv@(A.SubscriptedVariable _ (A.SubscriptField m n) v) mt = do (cg, ind') <- inner ind v mt t <- typeOfVariable sv let outerInd :: Int outerInd = if indirectedType t then -1 else 0 return (addPrefix (addPrefix cg ind' >> tell ["->"] >> genName n) outerInd, 0) inner ind sv@(A.SubscriptedVariable m (A.SubscriptFromFor m' start count) v) mt = return ( do tell ["(&"] join $ liftM fst $ inner ind v mt call genArraySubscript A.NoCheck v [(m', do tell ["occam_check_slice("] call genExpression start genComma call genExpression count genComma call genExpression (A.SizeVariable m' v) genComma genMeta m' tell [")"] )] tell [")"], 0) addPrefix :: CGen () -> Int -> CGen () addPrefix cg 0 = cg addPrefix cg n = tell ["(", getPrefix n] >> cg >> tell [")"] getPrefix :: Int -> String getPrefix 0 = "" getPrefix (-1) = "&" getPrefix n = if n > 0 then replicate n '*' else "#error Negative prefix lower than -1" -- | Collect all the plain subscripts on a variable, so we can combine them. collectSubs :: A.Variable -> CGen ([A.Expression], A.Variable, A.Type) collectSubs (A.SubscriptedVariable m (A.Subscript _ _ e) v) = do (es', v', t') <- collectSubs v t <- trivialSubscriptType m t' return (es' ++ [e], v', t) collectSubs v = do t <- typeOfVariable v return ([], v, t) indirectedType :: A.Type -> Bool indirectedType (A.Record {}) = True indirectedType (A.Chan A.DirUnknown _ _) = True indirectedType _ = False cgenDirectedVariable :: CGen () -> A.Direction -> CGen () cgenDirectedVariable var _ = var cgenArraySubscript :: A.SubscriptCheck -> A.Variable -> [(Meta, CGen ())] -> CGen () cgenArraySubscript check v es = do t <- typeOfVariable v let numDims = case t of A.Array ds _ -> length ds tell ["["] sequence_ $ intersperse (tell ["+"]) $ genPlainSub (genDynamicDim v) es [0..(numDims - 1)] tell ["]"] where genDynamicDim :: A.Variable -> Int -> CGen () genDynamicDim v i = call genVariable v >> call genSizeSuffix (show i) -- | Generate the individual offsets that need adding together to find the -- right place in the array. -- FIXME This is obviously not the best way to factor this, but I figure a -- smart C compiler should be able to work it out... genPlainSub :: (Int -> CGen ()) -> [(Meta, CGen ())] -> [Int] -> [CGen ()] genPlainSub _ [] _ = [] genPlainSub genDim ((m,e):es) (sub:subs) = gen : genPlainSub genDim es subs where gen = sequence_ $ intersperse (tell ["*"]) $ genSub : genChunks genSub = case check of A.NoCheck -> e A.CheckBoth -> do tell ["occam_check_index("] e tell [","] genDim sub tell [","] genMeta m tell [")"] A.CheckUpper -> do tell ["occam_check_index_upper("] e tell [","] genDim sub tell [","] genMeta m tell [")"] A.CheckLower -> do tell ["occam_check_index_lower("] e tell [","] genMeta m tell [")"] genChunks = map genDim subs --}}} --{{{ expressions cgenExpression :: A.Expression -> CGen () cgenExpression (A.Monadic m op e) = call genMonadic m op e cgenExpression (A.Dyadic m op e f) = call genDyadic m op e f cgenExpression (A.MostPos m t) = call genTypeSymbol "mostpos" t cgenExpression (A.MostNeg m t) = call genTypeSymbol "mostneg" t --cgenExpression (A.SizeType m t) cgenExpression (A.SizeExpr m e) = do call genExpression e call genSizeSuffix "0" cgenExpression (A.SizeVariable m v) = do t <- typeOfVariable v case t of A.Array (d:_) _ -> case d of A.Dimension n -> call genExpression n A.UnknownDimension -> do call genVariable v call genSizeSuffix "0" A.List _ -> call genListSize v cgenExpression (A.Conversion m cm t e) = call genConversion m cm t e cgenExpression (A.ExprVariable m v) = call genVariable v cgenExpression (A.Literal _ t lr) = call genLiteral lr t cgenExpression (A.True m) = tell ["true"] cgenExpression (A.False m) = tell ["false"] --cgenExpression (A.FunctionCall m n es) cgenExpression (A.IntrinsicFunctionCall m s es) = call genIntrinsicFunction m s es --cgenExpression (A.SubscriptedExpr m s e) --cgenExpression (A.BytesInExpr m e) cgenExpression (A.BytesInType m t) = call genBytesIn m t (Left False) --cgenExpression (A.OffsetOf m t n) --cgenExpression (A.ExprConstr {}) cgenExpression (A.AllocMobile m t me) = call genAllocMobile m t me cgenExpression t = call genMissing $ "genExpression " ++ show t cgenSizeSuffix :: String -> CGen () cgenSizeSuffix dim = tell ["_sizes[", dim, "]"] cgenTypeSymbol :: String -> A.Type -> CGen () cgenTypeSymbol s t = do f <- fget getScalarType case (t, f t) of (A.Time, _) -> tell ["occam_", s, "_time"] (_, Just ct) -> tell ["occam_", s, "_", ct] (_, Nothing) -> call genMissingC $ formatCode "genTypeSymbol %" t cgenIntrinsicFunction :: Meta -> String -> [A.Expression] -> CGen () cgenIntrinsicFunction m s es = do tell ["occam_", s, " ("] sequence [call genExpression e >> genComma | e <- es] genMeta m tell [")"] --}}} --{{{ operators cgenSimpleMonadic :: String -> A.Expression -> CGen () cgenSimpleMonadic s e = do tell ["(", s] call genExpression e tell [")"] cgenFuncMonadic :: Meta -> String -> A.Expression -> CGen () cgenFuncMonadic m s e = do t <- typeOfExpression e call genTypeSymbol s t tell [" ("] call genExpression e tell [", "] genMeta m tell [")"] cgenMonadic :: Meta -> A.MonadicOp -> A.Expression -> CGen () cgenMonadic m A.MonadicSubtr e = call genFuncMonadic m "negate" e cgenMonadic _ A.MonadicMinus e = call genSimpleMonadic "-" e cgenMonadic _ A.MonadicBitNot e = call genSimpleMonadic "~" e cgenMonadic _ A.MonadicNot e = call genSimpleMonadic "!" e cgenSimpleDyadic :: String -> A.Expression -> A.Expression -> CGen () cgenSimpleDyadic s e f = do tell ["("] call genExpression e tell [" ", s, " "] call genExpression f tell [")"] cgenFuncDyadic :: Meta -> String -> A.Expression -> A.Expression -> CGen () cgenFuncDyadic m s e f = do t <- typeOfExpression e call genTypeSymbol s t tell [" ("] call genExpression e tell [", "] call genExpression f tell [", "] genMeta m tell [")"] cgenDyadic :: Meta -> A.DyadicOp -> A.Expression -> A.Expression -> CGen () cgenDyadic m A.Add e f = call genFuncDyadic m "add" e f cgenDyadic m A.Subtr e f = call genFuncDyadic m "subtr" e f cgenDyadic m A.Mul e f = call genFuncDyadic m "mul" e f cgenDyadic m A.Div e f = call genFuncDyadic m "div" e f cgenDyadic m A.Rem e f = call genFuncDyadic m "rem" e f cgenDyadic m A.Plus e f = call genFuncDyadic m "plus" e f cgenDyadic m A.Minus e f = call genFuncDyadic m "minus" e f cgenDyadic m A.Times e f = call genFuncDyadic m "times" e f cgenDyadic m A.LeftShift e f = call genFuncDyadic m "lshift" e f cgenDyadic m A.RightShift e f = call genFuncDyadic m "rshift" e f cgenDyadic _ A.BitAnd e f = call genSimpleDyadic "&" e f cgenDyadic _ A.BitOr e f = call genSimpleDyadic "|" e f cgenDyadic _ A.BitXor e f = call genSimpleDyadic "^" e f cgenDyadic _ A.And e f = call genSimpleDyadic "&&" e f cgenDyadic _ A.Or e f = call genSimpleDyadic "||" e f cgenDyadic _ A.Eq e f = call genSimpleDyadic "==" e f cgenDyadic _ A.NotEq e f = call genSimpleDyadic "!=" e f cgenDyadic _ A.Less e f = call genSimpleDyadic "<" e f cgenDyadic _ A.More e f = call genSimpleDyadic ">" e f cgenDyadic _ A.LessEq e f = call genSimpleDyadic "<=" e f cgenDyadic _ A.MoreEq e f = call genSimpleDyadic ">=" e f cgenDyadic _ A.Concat e f = call genListConcat e f --}}} cgenListConcat :: A.Expression -> A.Expression -> CGen () cgenListConcat _ _ = call genMissing "C backend does not yet support lists" --{{{ input/output items cgenInputItem :: A.Variable -> A.InputItem -> CGen () cgenInputItem c (A.InCounted m cv av) = do call genInputItem c (A.InVariable m cv) t <- typeOfVariable av tell ["ChanIn(wptr,"] call genVariable c tell [","] call genVariableAM av A.Abbrev tell [","] subT <- trivialSubscriptType m t call genVariable cv tell ["*"] call genBytesIn m subT (Right av) tell [");"] cgenInputItem c (A.InVariable m v) = do t <- typeOfVariable v let rhs = call genVariableAM v A.Abbrev case t of A.Int -> do tell ["ChanInInt(wptr,"] call genVariable c tell [","] rhs tell [");"] _ -> do tell ["ChanIn(wptr,"] call genVariable c tell [","] rhs tell [","] call genBytesIn m t (Right v) tell [");"] cgenOutputItem :: A.Variable -> A.OutputItem -> CGen () cgenOutputItem c (A.OutCounted m ce ae) = do call genOutputItem c (A.OutExpression m ce) t <- typeOfExpression ae case ae of A.ExprVariable m v -> do tell ["ChanOut(wptr,"] call genVariable c tell [","] call genVariableAM v A.Abbrev tell [","] subT <- trivialSubscriptType m t call genExpression ce tell ["*"] call genBytesIn m subT (Right v) tell [");"] cgenOutputItem c (A.OutExpression m e) = do t <- typeOfExpression e case (t, e) of (A.Int, _) -> do tell ["ChanOutInt(wptr,"] call genVariable c tell [","] call genExpression e tell [");"] (_, A.ExprVariable _ v) -> do tell ["ChanOut(wptr,"] call genVariable c tell [","] call genVariableAM v A.Abbrev tell [","] call genBytesIn m t (Right v) tell [");"] --}}} --{{{ replicators cgenReplicator :: A.Replicator -> CGen () -> CGen () cgenReplicator rep body = do tell ["for("] call genReplicatorLoop rep tell ["){"] body tell ["}"] isZero :: A.Expression -> Bool isZero (A.Literal _ A.Int (A.IntLiteral _ "0")) = True isZero _ = False cgenReplicatorLoop :: A.Replicator -> CGen () cgenReplicatorLoop (A.For m index base count) = if isZero base then simple else general where simple :: CGen () simple = do tell ["int "] genName index tell ["=0;"] genName index tell ["<"] call genExpression count tell [";"] genName index tell ["++"] general :: CGen () general = do counter <- csmLift $ makeNonce "replicator_count" tell ["int ", counter, "="] call genExpression count tell [","] genName index tell ["="] call genExpression base tell [";", counter, ">0;", counter, "--,"] genName index tell ["++"] cgenReplicatorLoop _ = cgenMissing "ForEach loops not yet supported in the C backend" --}}} --{{{ abbreviations cgenVariableAM :: A.Variable -> A.AbbrevMode -> CGen () cgenVariableAM v am = do when (am == A.Abbrev) $ do t <- typeOfVariable v case (indirectedType t, t) of (True, _) -> return () (False, A.Array {}) -> return () _ -> tell ["&"] call genVariable v -- | Generate the size part of a RETYPES\/RESHAPES abbrevation of a variable. cgenRetypeSizes :: Meta -> A.Type -> A.Name -> A.Type -> A.Variable -> CGen () cgenRetypeSizes _ (A.Chan {}) _ (A.Chan {}) _ = return () cgenRetypeSizes m destT destN srcT srcV = let size = do tell ["occam_check_retype("] call genBytesIn m srcT (Right srcV) tell [","] call genBytesIn m destT (Left True) tell [","] genMeta m tell [")"] isVarArray = case destT of A.Array ds _ -> A.UnknownDimension `elem` ds _ -> False in if isVarArray then size >> tell [";"] else do tell ["if("] size tell ["!=1){"] call genStop m "size mismatch in RETYPES" tell ["}"] -- | Generate the right-hand side of an abbreviation of an expression. abbrevExpression :: A.AbbrevMode -> A.Type -> A.Expression -> CGen () abbrevExpression am t@(A.Array _ _) e = case e of A.ExprVariable _ v -> call genVariableAM v am A.Literal _ t@(A.Array _ _) r -> call genExpression e _ -> call genMissing "array expression abbreviation" abbrevExpression am _ e = call genExpression e --}}} --{{{ specifications cgenSpec :: A.Specification -> CGen () -> CGen () cgenSpec spec body = do call introduceSpec spec body call removeSpec spec -- | Generate a declaration of a new variable. cgenDeclaration :: A.Type -> A.Name -> Bool -> CGen () cgenDeclaration at@(A.Array ds t) n False = do call genType t tell [" "] case t of A.Chan A.DirUnknown _ _ -> do genName n tell ["_storage"] call genFlatArraySize ds tell [";"] call genType t tell ["* "] _ -> return () call genArrayStoreName n call genFlatArraySize ds tell [";"] cgenDeclaration (A.Array ds t) n True = do call genType t tell [" "] call genArrayStoreName n call genFlatArraySize ds tell [";"] cgenDeclaration t n _ = do call genType t tell [" "] genName n tell [";"] -- | Generate the size of the C array that an occam array of the given -- dimensions maps to. cgenFlatArraySize :: [A.Dimension] -> CGen () cgenFlatArraySize ds = do tell ["["] sequence $ intersperse (tell ["*"]) [call genExpression n | A.Dimension n <- ds] tell ["]"] -- FIXME: genBytesInArrayDim could share with this -- | Initialise an item being declared. cdeclareInit :: Meta -> A.Type -> A.Variable -> Maybe (CGen ()) cdeclareInit _ (A.Chan A.DirUnknown _ _) var = Just $ do tell ["ChanInit(wptr,"] call genVariableUnchecked var tell [");"] cdeclareInit m t@(A.Array ds t') var = Just $ do case t' of A.Chan A.DirUnknown _ _ -> do tell ["tock_init_chan_array("] call genVariableUnchecked var tell ["_storage,"] call genVariableUnchecked var tell [","] sequence_ $ intersperse (tell ["*"]) [call genExpression n | A.Dimension n <- ds] -- FIXME: and again tell [");"] _ -> return () fdeclareInit <- fget declareInit init <- return (\sub -> fdeclareInit m t' (sub var)) call genOverArray m var init cdeclareInit m rt@(A.Record _) var = Just $ do fs <- recordFields m rt sequence_ [initField t (A.SubscriptedVariable m (A.SubscriptField m n) var) | (n, t) <- fs] where initField :: A.Type -> A.Variable -> CGen () initField t v = do fdeclareInit <- fget declareInit doMaybe $ fdeclareInit m t v cdeclareInit _ _ _ = Nothing -- | Free a declared item that's going out of scope. cdeclareFree :: Meta -> A.Type -> A.Variable -> Maybe (CGen ()) cdeclareFree _ _ _ = Nothing {- Original Abbrev INT x IS y: int *x = &y; int *x = &(*y); []INT xs IS ys: int *xs = ys; int *xs = ys; const int xs_sizes[] = ys_sizes; CHAN OF INT c IS d: Channel *c = d; [10]CHAN OF INT cs: Channel tmp[10]; Channel *cs[10]; for (...) { cs[i] = &tmp[i]; ChanInit(cs[i]); } const int cs_sizes[] = { 10 }; []CHAN OF INT ds IS cs: Channel **ds = cs; const int *ds_sizes = cs_sizes; -} cintroduceSpec :: A.Specification -> CGen () cintroduceSpec (A.Specification m n (A.Declaration _ t)) = do call genDeclaration t n False fdeclareInit <- fget declareInit case fdeclareInit m t (A.Variable m n) of Just p -> p Nothing -> return () cintroduceSpec (A.Specification _ n (A.Is _ am t v)) = do let rhs = call genVariableAM v am call genDecl am t n tell ["="] rhs tell [";"] cintroduceSpec (A.Specification _ n (A.IsExpr _ am t e)) = do let rhs = abbrevExpression am t e case (am, t, e) of (A.ValAbbrev, A.Array _ ts, A.Literal _ _ _) -> -- For "VAL []T a IS [vs]:", we have to use [] rather than * in the -- declaration, since you can't say "int *foo = {vs};" in C. do tell ["const "] call genType ts tell [" "] genName n tell ["[] = "] rhs tell [";\n"] (A.ValAbbrev, A.Record _, A.Literal _ _ _) -> -- Record literals are even trickier, because there's no way of -- directly writing a struct literal in C that you can use -> on. do tmp <- csmLift $ makeNonce "record_literal" tell ["const "] call genType t tell [" ", tmp, " = "] rhs tell [";\n"] call genDecl am t n tell [" = &", tmp, ";\n"] _ -> do call genDecl am t n tell [" = "] rhs tell [";\n"] cintroduceSpec (A.Specification _ n (A.IsChannelArray _ (A.Array _ c) cs)) = do call genType c tell ["*"] call genArrayStoreName n tell ["[]={"] seqComma (map (call genVariable) cs) tell ["};"] cintroduceSpec (A.Specification _ _ (A.DataType _ _)) = return () cintroduceSpec (A.Specification _ _ (A.RecordType _ _ _)) = return () cintroduceSpec (A.Specification _ n (A.Protocol _ _)) = return () cintroduceSpec (A.Specification _ n (A.ProtocolCase _ ts)) = do tell ["typedef enum{"] seqComma [genName tag >> tell ["_"] >> genName n | (tag, _) <- ts] -- You aren't allowed to have an empty enum. when (ts == []) $ tell ["empty_protocol_"] >> genName n tell ["}"] genName n tell [";"] cintroduceSpec (A.Specification _ n st@(A.Proc _ _ _ _)) = genProcSpec n st False cintroduceSpec (A.Specification _ n (A.Retypes m am t v)) = do origT <- typeOfVariable v let rhs = call genVariableAM v A.Abbrev call genDecl am t n tell ["="] -- For scalar types that are VAL abbreviations (e.g. VAL INT64), -- we need to dereference the pointer that abbrevVariable gives us. let deref = case (am, t) of (_, A.Array _ _) -> False (_, A.Chan {}) -> False (_, A.Record {}) -> False (A.ValAbbrev, _) -> True _ -> False when deref $ tell ["*"] tell ["("] call genDeclType am t when deref $ tell ["*"] tell [")"] rhs tell [";"] call genRetypeSizes m t n origT v --cintroduceSpec (A.Specification _ n (A.RetypesExpr _ am t e)) cintroduceSpec n = call genMissing $ "introduceSpec " ++ show n cgenRecordTypeSpec :: A.Name -> Bool -> [(A.Name, A.Type)] -> CGen () cgenRecordTypeSpec n b fs = do tell ["typedef struct{"] sequence_ [call genDeclaration t n True | (n, t) <- fs] tell ["}"] when b $ tell [" occam_struct_packed "] genName n tell [";"] cgenForwardDeclaration :: A.Specification -> CGen () cgenForwardDeclaration (A.Specification _ n st@(A.Proc _ _ _ _)) = genProcSpec n st True cgenForwardDeclaration (A.Specification _ n (A.RecordType _ b fs)) = call genRecordTypeSpec n b fs cgenForwardDeclaration _ = return () cremoveSpec :: A.Specification -> CGen () cremoveSpec (A.Specification m n (A.Declaration _ t)) = do fdeclareFree <- fget declareFree case fdeclareFree m t var of Just p -> p Nothing -> return () where var = A.Variable m n cremoveSpec _ = return () cgenSpecMode :: A.SpecMode -> CGen () cgenSpecMode A.PlainSpec = return () cgenSpecMode A.InlineSpec = tell ["inline "] --}}} --{{{ formals, actuals, and calling conventions prefixComma :: [CGen ()] -> CGen () prefixComma cs = sequence_ [genComma >> c | c <- cs] cgenActuals :: [A.Formal] -> [A.Actual] -> CGen () cgenActuals fs as = prefixComma [call genActual f a | (f, a) <- zip fs as] cgenActual :: A.Formal -> A.Actual -> CGen () cgenActual f a = seqComma $ realActuals f a -- | Return generators for all the real actuals corresponding to a single -- actual. realActuals :: A.Formal -> A.Actual -> [CGen ()] realActuals _ (A.ActualExpression e) = [call genExpression e] realActuals (A.Formal am _ _) (A.ActualVariable v) = [call genVariableAM v am] -- | Return (type, name) generator pairs for all the real formals corresponding -- to a single formal. realFormals :: A.Formal -> [(CGen (), CGen ())] realFormals (A.Formal am t n) = [(call genDeclType am t, genName n)] -- | Generate a Proc specification, which maps to a C function. -- This will use ProcGetParam if the Proc is in csParProcs, or the normal C -- calling convention otherwise. genProcSpec :: A.Name -> A.SpecType -> Bool -> CGen () genProcSpec n (A.Proc _ sm fs p) forwardDecl = do cs <- getCompState let (header, params) = if n `Set.member` csParProcs cs then (genParHeader, genParParams) else (genNormalHeader, return ()) header if forwardDecl then tell [";\n"] else do tell ["{\n"] params call genProcess p tell ["}\n"] where rfs = concatMap realFormals fs genParHeader :: CGen () genParHeader = do -- These can't be inlined, since they're only used as function -- pointers. tell ["void "] genName n tell [" (Workspace wptr)"] genParParams :: CGen () genParParams = sequence_ [do t tell [" "] n tell [" = ProcGetParam (wptr, " ++ show num ++ ", "] t tell [");\n"] | (num, (t, n)) <- zip [(0 :: Int) ..] rfs] genNormalHeader :: CGen () genNormalHeader = do call genSpecMode sm tell ["void "] genName n tell [" (Workspace wptr"] sequence_ [do tell [", "] t tell [" "] n | (t, n) <- rfs] tell [")"] -- | Generate a ProcAlloc for a PAR subprocess, returning a nonce for the -- workspace pointer and the name of the function to call. cgenProcAlloc :: A.Name -> [A.Formal] -> [A.Actual] -> CGen (String, CGen ()) cgenProcAlloc n fs as = do let ras = concat [realActuals f a | (f, a) <- zip fs as] ws <- csmLift $ makeNonce "workspace" tell ["Workspace ", ws, " = ProcAlloc (wptr, ", show $ length ras, ", "] genName n tell ["_stack_size);\n"] sequence_ [do tell ["ProcParam (wptr, ", ws, ", ", show num, ", "] ra tell [");\n"] | (num, ra) <- zip [(0 :: Int)..] ras] return (ws, genName n) --}}} --{{{ processes cgenProcess :: A.Process -> CGen () cgenProcess p = case p of A.Assign m vs es -> call genAssign m vs es A.Input m c im -> call genInput c im A.Output m c ois -> call genOutput c ois A.OutputCase m c t ois -> call genOutputCase c t ois A.Skip m -> tell ["/* skip */\n"] A.Stop m -> call genStop m "STOP process" A.Seq _ s -> call genSeq s A.If m s -> call genIf m s A.Case m e s -> call genCase m e s A.While m e p -> call genWhile e p A.Par m pm s -> call genPar pm s -- PROCESSOR does nothing special. A.Processor m e p -> call genProcess p A.Alt m b s -> call genAlt b s A.ProcCall m n as -> call genProcCall n as A.IntrinsicProcCall m s as -> call genIntrinsicProc m s as --{{{ assignment cgenAssign :: Meta -> [A.Variable] -> A.ExpressionList -> CGen () cgenAssign m [v] (A.ExpressionList _ [e]) = do t <- typeOfVariable v f <- fget getScalarType case f t of Just _ -> doAssign v e Nothing -> case t of -- Assignment of channel-ends, but not channels, is possible (at least in Rain): A.Chan A.DirInput _ _ -> doAssign v e A.Chan A.DirOutput _ _ -> doAssign v e A.List _ -> call genListAssign v e _ -> call genMissingC $ formatCode "assignment of type %" t where doAssign :: A.Variable -> A.Expression -> CGen () doAssign v e = do call genVariable v tell ["="] call genExpression e tell [";"] cgenAssign m _ _ = call genMissing "Cannot perform assignment with multiple destinations or multiple sources" --}}} --{{{ input cgenInput :: A.Variable -> A.InputMode -> CGen () cgenInput c im = do case im of A.InputTimerRead m (A.InVariable m' v) -> call genTimerRead c v A.InputTimerAfter m e -> call genTimerWait e A.InputSimple m is -> sequence_ $ map (call genInputItem c) is _ -> call genMissing $ "genInput " ++ show im cgenTimerRead :: A.Variable -> A.Variable -> CGen () cgenTimerRead _ v = cgenGetTime v cgenTimerWait :: A.Expression -> CGen () cgenTimerWait e = do tell ["TimerWait(wptr,"] call genExpression e tell [");"] cgenGetTime :: A.Variable -> CGen () cgenGetTime v = do call genVariable v tell [" = TimerRead(wptr);"] --}}} --{{{ output cgenOutput :: A.Variable -> [A.OutputItem] -> CGen () cgenOutput c ois = sequence_ $ map (call genOutputItem c) ois cgenOutputCase :: A.Variable -> A.Name -> [A.OutputItem] -> CGen () cgenOutputCase c tag ois = do t <- typeOfVariable c let proto = case t of A.Chan _ _ (A.UserProtocol n) -> n tell ["ChanOutInt(wptr,"] call genVariable c tell [","] genName tag tell ["_"] genName proto tell [");"] call genOutput c ois --}}} --{{{ stop cgenStop :: Meta -> String -> CGen () cgenStop m s = do tell ["occam_stop("] genMeta m tell [",1,\"", s, "\");"] --}}} --{{{ seq cgenSeq :: A.Structured A.Process -> CGen () cgenSeq s = call genStructured s doP where doP _ p = call genProcess p --}}} --{{{ if cgenIf :: Meta -> A.Structured A.Choice -> CGen () cgenIf m s = do label <- csmLift $ makeNonce "if_end" tell ["/*",label,"*/"] genIfBody label s call genStop m "no choice matched in IF process" tell [label, ":;"] where genIfBody :: String -> A.Structured A.Choice -> CGen () genIfBody label s = call genStructured s doC where doC m (A.Choice m' e p) = do tell ["if("] call genExpression e tell ["){"] call genProcess p tell ["goto ", label, ";"] tell ["}"] --}}} --{{{ case cgenCase :: Meta -> A.Expression -> A.Structured A.Option -> CGen () cgenCase m e s = do tell ["switch("] call genExpression e tell ["){"] seenDefault <- genCaseBody (return ()) s when (not seenDefault) $ do tell ["default:"] call genStop m "no option matched in CASE process" tell ["}"] where genCaseBody :: CGen () -> A.Structured A.Option -> CGen Bool genCaseBody coll (A.Spec _ spec s) = genCaseBody (call genSpec spec coll) s genCaseBody coll (A.Only _ (A.Option _ es p)) = do sequence_ [tell ["case "] >> call genExpression e >> tell [":"] | e <- es] tell ["{"] coll call genProcess p tell ["}break;"] return False genCaseBody coll (A.Only _ (A.Else _ p)) = do tell ["default:"] tell ["{"] coll call genProcess p tell ["}break;"] return True genCaseBody coll (A.Several _ ss) = do seens <- mapM (genCaseBody coll) ss return $ or seens --}}} --{{{ while cgenWhile :: A.Expression -> A.Process -> CGen () cgenWhile e p = do tell ["while("] call genExpression e tell ["){"] call genProcess p tell ["}"] --}}} --{{{ par -- FIXME: The ParMode is now ignored (as it is in occ21), so PRI PAR behaves -- the same as PAR. cgenPar :: A.ParMode -> A.Structured A.Process -> CGen () cgenPar pm s = do (count, _, _) <- constantFold $ countStructured s bar <- csmLift $ makeNonce "par_barrier" tell ["LightProcBarrier ", bar, ";\n"] tell ["LightProcBarrierInit (wptr, &", bar, ", "] call genExpression count tell [");\n"] call genStructured s (startP bar) tell ["LightProcBarrierWait (wptr, &", bar, ");\n"] where startP :: String -> Meta -> A.Process -> CGen () startP bar _ (A.ProcCall _ n as) = do (A.Proc _ _ fs _) <- specTypeOfName n (ws, func) <- cgenProcAlloc n fs as tell ["LightProcStart (wptr, &", bar, ", ", ws, ", "] func tell [");\n"] --}}} --{{{ alt cgenAlt :: Bool -> A.Structured A.Alternative -> CGen () cgenAlt isPri s = do id <- csmLift $ makeNonce "alt_id" tell ["int ", id, " = 0;\n"] let isTimerAlt = containsTimers s tell [if isTimerAlt then "TimerAlt" else "Alt", " (wptr);\n"] tell ["{\n"] genAltEnable id s tell ["}\n"] -- Like occ21, this is always a PRI ALT, so we can use it for both. tell [if isTimerAlt then "TimerAltWait" else "AltWait", " (wptr);\n"] tell [id, " = 0;\n"] tell ["{\n"] genAltDisable id s tell ["}\n"] fired <- csmLift $ makeNonce "alt_fired" tell ["int ", fired, " = AltEnd (wptr);\n"] tell [id, " = 0;\n"] label <- csmLift $ makeNonce "alt_end" tell ["{\n"] genAltProcesses id fired label s tell ["}\n"] tell [label, ":\n;\n"] where containsTimers :: A.Structured A.Alternative -> Bool containsTimers (A.Rep _ _ s) = containsTimers s containsTimers (A.Spec _ _ s) = containsTimers s containsTimers (A.ProcThen _ _ s) = containsTimers s containsTimers (A.Only _ a) = case a of A.Alternative _ _ (A.InputTimerRead _ _) _ -> True A.Alternative _ _ (A.InputTimerAfter _ _) _ -> True _ -> False containsTimers (A.Several _ ss) = or $ map containsTimers ss genAltEnable :: String -> A.Structured A.Alternative -> CGen () genAltEnable id s = call genStructured s doA where doA _ alt = case alt of A.Alternative _ c im _ -> doIn c im A.AlternativeCond _ e c im _ -> withIf e $ doIn c im A.AlternativeSkip _ e _ -> withIf e $ tell ["AltEnableSkip (wptr,", id, "++);\n"] doIn c im = do case im of A.InputTimerRead _ _ -> call genMissing "timer read in ALT" A.InputTimerAfter _ time -> do tell ["AltEnableTimer (wptr,", id, "++,"] call genExpression time tell [");\n"] _ -> do tell ["AltEnableChannel (wptr,", id, "++,"] call genVariable c tell [");\n"] genAltDisable :: String -> A.Structured A.Alternative -> CGen () genAltDisable id s = call genStructured s doA where doA _ alt = case alt of A.Alternative _ c im _ -> doIn c im A.AlternativeCond _ e c im _ -> withIf e $ doIn c im A.AlternativeSkip _ e _ -> withIf e $ tell ["AltDisableSkip (wptr,", id, "++);\n"] doIn c im = do case im of A.InputTimerRead _ _ -> call genMissing "timer read in ALT" A.InputTimerAfter _ time -> do tell ["AltDisableTimer (wptr,", id, "++, "] call genExpression time tell [");\n"] _ -> do tell ["AltDisableChannel (wptr,", id, "++, "] call genVariable c tell [");\n"] genAltProcesses :: String -> String -> String -> A.Structured A.Alternative -> CGen () genAltProcesses id fired label s = call genStructured s doA where doA _ alt = case alt of A.Alternative _ c im p -> doIn c im p A.AlternativeCond _ e c im p -> withIf e $ doIn c im p A.AlternativeSkip _ e p -> withIf e $ doCheck (call genProcess p) doIn c im p = do case im of A.InputTimerRead _ _ -> call genMissing "timer read in ALT" A.InputTimerAfter _ _ -> doCheck (call genProcess p) _ -> doCheck (call genInput c im >> call genProcess p) doCheck body = do tell ["if (", id, "++ == ", fired, ") {\n"] body tell ["goto ", label, ";\n"] tell ["}\n"] withIf :: A.Expression -> CGen () -> CGen () withIf cond body = do tell ["if ("] call genExpression cond tell [") {\n"] body tell ["}\n"] --}}} --{{{ proc call cgenProcCall :: A.Name -> [A.Actual] -> CGen () cgenProcCall n as = do genName n tell [" (wptr"] (A.Proc _ _ fs _) <- specTypeOfName n call genActuals fs as tell [");\n"] --}}} --{{{ intrinsic procs cgenIntrinsicProc :: Meta -> String -> [A.Actual] -> CGen () cgenIntrinsicProc m "ASSERT" [A.ActualExpression e] = call genAssert m e cgenIntrinsicProc _ "RESCHEDULE" [] = tell ["Reschedule (wptr);\n"] cgenIntrinsicProc _ s _ = call genMissing $ "intrinsic PROC " ++ s cgenAssert :: Meta -> A.Expression -> CGen () cgenAssert m e = do tell ["if (!"] call genExpression e tell [") {\n"] call genStop m "assertion failed" tell ["}\n"] --}}} --}}} --{{{ mobiles cgenAllocMobile :: Meta -> A.Type -> Maybe A.Expression -> CGen() cgenAllocMobile m (A.Mobile t) Nothing = tell ["malloc("] >> call genBytesIn m t (Left False) >> tell [")"] --TODO add a pass, just for C, that pulls out the initialisation expressions for mobiles -- into a subsequent assignment cgenAllocMobile _ _ _ = call genMissing "Mobile allocation with initialising-expression" cgenClearMobile :: Meta -> A.Variable -> CGen () cgenClearMobile _ v = do tell ["if("] genVar tell ["!=NULL){free("] genVar tell [");"] genVar tell ["=NULL;}"] where genVar = call genVariable v --}}}