pec-0.2.0: Pec/C.hs
{-# OPTIONS -Wall #-}
-- The pec embedded compiler
-- Copyright 2011-2012, Brett Letner
module Pec.C (cModules) where
import Control.Concurrent
import Control.Monad
import Data.Char
import Data.Generics.Uniplate.Data
import Data.List
import Data.Maybe
import Development.Shake.FilePath
import Grm.Prims
import Language.C.Abs
import Pec.IUtil
import qualified Language.Pir.Abs as I
cModules :: FilePath -> Bool -> I.Module -> IO ()
cModules outdir is_readable (I.Module a _bs cs) = do
let defs = map cDefine cs
writeFileBinary (joinPath [outdir, n ++ ".c"]) $ ppShow $
cleanup $ optimize $ dModule is_readable $
CModule [Import hnfn] defs
xs <- liftM (nub . map cTypeD) $ readMVar gTyDecls
let ifvs = nub [ ifv | ifv@(I.TVar v _) <- concatMap fvsIDefine cs
, v `notElem` cBuiltins ]
writeFileBinary (joinPath [outdir, hnfn]) $ ppShow $
cleanup $
transformBi tArrayArgTy $
HModule hn hn imps $ xs ++ map (Declare . cFunDecl) ifvs
where
hn = n ++ "_H"
hnfn = n ++ ".h"
n = case a of
"" -> error "unused:cModules"
_ -> init a
imps = map GImport
[ "stdio.h"
, "stdint.h"
, "stdlib.h"
, "string.h"
]
cBuiltins :: [String]
cBuiltins = [ "puts", "putchar", "strlen", "strncpy", "strcmp"] -- BAL: figure out how to handle this generically
cTypeD :: (I.Type, I.TyDecl) -> Declare
cTypeD (x@(I.Type s _),y) = Typedef $ Decl t (tyName x)
where
t = case y of
I.TyEnum bs -> TyEnum [ EnumC b | I.EnumC b <- bs ]
I.TyRecord bs -> TyStruct [ cDecl $ I.TVar c d | I.FieldT c d <- bs ]
I.TyTagged bs -> TyStruct
[ Decl (TyName $ s ++ "tag") "tag"
, Decl (TyUnion [ cDecl $ I.TVar c d | I.ConC c d <- bs
, d /= tyVoid ]) "data"
]
cDecl :: I.TVar -> Decl
cDecl (I.TVar a b) = Decl (cType b) a
cType :: I.Type -> Type
cType x@(I.Type a bs) = case a of
"Fun_" -> TyFun (cType $ last bs) (map cType $ init bs)
"Ptr_" -> case bs of
[t] -> TyPtr (cType t)
_ -> error "cType:TyPtr"
"Array_" -> case bs of
[c,d] -> TyArray (cType d) (nCnt [c])
_ -> error "cType:TyArray"
_ -> cTyName x
cTyName :: I.Type -> Type
cTyName = TyName . tyName
tyName :: I.Type -> String
tyName (I.Type a bs) = case (a,bs) of
("Void_",[]) -> "void"
("W_",_) -> "uint" ++ (promote $ nCnt bs) ++ "_t"
("I_",_) -> "int" ++ (promote $ nCnt bs) ++ "_t"
("Double_",[]) -> "double"
("Float_",[]) -> "float"
("Char_",[]) -> "char"
("Fun_",_) -> tyName (I.Type (a ++ show (length bs)) bs)
_ -> mkTyConstr (a : map tyName bs)
mkTyConstr :: [String] -> String
mkTyConstr ss = concat $ intersperse "_" $ map strip_underscore ss
cDefine :: I.Define -> Define
cDefine x@(I.Define a b cs d) =
Define (funDecl a b cs) $ map (DeclS . cDecl) (lvsIDefine x) ++ cBlock d
funDecl :: I.Type -> Lident -> [I.TVar] -> FunDecl
funDecl a b cs = case cType a of
TyFun t ts -> RetFunFD t b vs ts
t -> FunFD t b vs
where vs = map cDecl cs
cFunDecl :: I.TVar -> FunDecl
cFunDecl (I.TVar a b) = case b of
I.Type "Fun_" xs -> funDecl (last xs) a (map (I.TVar "") $ init xs)
_ -> error "unused:cFunDecl:not TyFun"
cExp :: I.Exp -> Exp
cExp x = case x of
I.CallE f [b] | has_suffix "_fld" $ vtvar f ->
AddrE $ ArrowE (cAtom b) $ VarE $ drop_suffix "fld" (vtvar f)
I.CallE f [b] | vtvar f == "tagv" -> ArrowE (cAtom b) (enum "tag")
I.CallE f [b,c] | vtvar f == "un" ->
AddrE $ ArrowE (cAtom c) $ DotE (VarE "data") (cTag b)
I.CallE f [b,c] | vtvar f == "idx" ->
IdxE (cAtom b) (cAtom c)
I.CallE a [b,c] | isBinOp a -> BinOpE (cAtom b) (cBinOp a) (cAtom c)
I.CallE a bs -> CallE (cVarE a) $ map cAtom bs
I.AtomE a -> cAtom a
I.CastE a@(I.TVar _ t) b
| isTypeEquiv t b -> cVarE a
| otherwise -> CastE (cType b) (cVarE a)
I.AllocaE a -> AllocaE $ cType a
I.LoadE a -> LoadE $ cVarE a
cTag :: I.Atom -> Exp
cTag x = case x of
I.LitA (I.TLit (I.EnumL a) _) -> enum a
I.LitA (I.TLit (I.StringL a) _) -> enum a -- BAL: should be EnumL...
_ -> error $ "cTag:" ++ ppShow x
enum :: Uident -> Exp
enum = LitE . EnumL
cVarE :: I.TVar -> Exp
cVarE (I.TVar a _) = VarE a
cStmt :: I.Stmt -> [Stmt]
cStmt x = case x of
I.LetS a b -> case b of
I.CallE f [c] | vtvar f == "mk" ->
[ AssignS (DotE (cVarE a) (enum "tag")) $ cTag c ]
I.CallE f [c,d] | vtvar f == "mk" ->
[ AssignS (DotE (cVarE a) (enum "tag")) $ cTag c
, AssignS (DotE (DotE (cVarE a) (enum "data")) (cTag c)) $
cAtom d
]
I.CallE f [c] | vtvar f == "mk" ->
[AssignS (DotE (cVarE a) (enum "tag")) (cAtom c)]
_ -> [AssignS (cVarE a) (cExp b)]
I.StoreS a b -> [AssignS (LoadE $ cVarE a) (cAtom b)]
I.CallS a bs -> [CallS (cVarE a) $ map cAtom bs]
I.SwitchS a bs cs ->
[SwitchS (cAtom a) $ map cSwitchAlt cs ++
[DefaultAlt $ cBlock bs ++ [BreakS]]]
I.IfS a b c -> [IfS (cAtom a) (cBlock b) (cBlock c)]
I.WhenS a b -> [WhenS (cAtom a) (cBlock b)]
I.WhileS a b c -> ss ++ [WhileS (cAtom b) (cBlock c ++ ss)]
where ss = cBlock a
I.ReturnS (I.LitA (I.TLit I.VoidL _)) -> [RetVoidS]
I.ReturnS a -> [ReturnS (cAtom a)]
I.NoOpS -> error "unused:cStmt:NoOpS"
cAtom :: I.Atom -> Exp
cAtom x = case x of
I.VarA a -> cVarE a
I.LitA (I.TLit I.VoidL _) -> error "void atom not removed"
I.LitA a -> cTLitE a
cTLitE :: I.TLit -> Exp
cTLitE = LitE . cTLit
cBlock :: I.StmtList -> [Stmt]
cBlock = concatMap cStmt
cSwitchAlt :: I.SwitchAlt -> SwitchAlt
cSwitchAlt (I.SwitchAlt a b) =
SwitchAlt (cTLit a) (cBlock b ++ [BreakS])
cTLit :: I.TLit -> Lit
cTLit (I.TLit x y) = case x of
I.StringL a -> StringL a
I.CharL a -> CharL a
-- BAL add int and float suffixes
I.NmbrL a -> NmbrL $ case y of
I.Type "Float_" []
| isFloat a -> a
| otherwise -> show (readNumber a :: Double) ++ "f"
I.Type "Double_" []
| isFloat a -> a
| otherwise -> show (readNumber a :: Double)
_ | isFloat a -> error $ "integral type with float syntax:" ++ a ++
":" ++ ppShow y
| isBinary a -> show (readBinary a)
| isOctal a -> '0' : drop 2 a
| otherwise -> a
I.EnumL a -> EnumL a
I.VoidL -> error "unused:cLTit:VoidL"
cleanup :: Module -> Module
cleanup x =
rewriteBi tTypeD $
transformBi tVarName $
transformBi tName $
x
tMath :: Stmt -> Maybe Stmt
tMath (AssignS a (BinOpE b "+" (LitE (NmbrL s)))) -- BAL: Don't do if float/double?
| a == b && (readNumber s :: Integer) == 1 = Just $ IncS a
tMath (AssignS a (BinOpE b "-" (LitE (NmbrL s)))) -- BAL: Don't do if float/double?
| a == b && (readNumber s :: Integer) == 1 = Just $ DecS a
tMath _ = Nothing
tVarName :: Define -> Define
tVarName x = transformBi k $ transformBi h x
where
tbl = concatMap g $ groupBy (\a b -> f a == f b) $
sort [ v | Decl _ v <- universeBi x ]
f s =
case reverse $ dropWhile (\c -> isDigit c || c == '_') $ reverse s of
"" -> "_"
s1 -> s1
g ss = case ss of
[s] -> [(s, f s)]
_ -> [ (s, f s ++ show i) | (s,i) <- zip ss [0 :: Int .. ]]
h (VarE v) = case lookup v tbl of
Nothing -> VarE v
Just v1 -> VarE v1
h a = a
k (Decl t v) = case lookup v tbl of
Nothing -> error "unused:tVarName"
Just v1 -> Decl t v1
k a = a
optimize :: Module -> Module
optimize x =
rewriteBi canon $
rewriteBi tNoOpS $
rewriteBi canonSS $
rewriteBi opt $
x
opt :: Stmt -> Maybe Stmt
opt (IfS e a _) | isTrue e = Just $ BlockS a
opt (IfS e _ b) | isFalse e = Just $ BlockS b
opt (WhenS e a) | isTrue e = Just $ BlockS a
opt (WhenS e _) | isFalse e = Just NoOpS
opt (WhileS e _) | isFalse e = Just NoOpS
opt _ = Nothing
canon :: Stmt -> Maybe Stmt
canon (IfS e a []) = Just $ WhenS e a
canon (IfS e [] b) = Just $ WhenS (NotE e) b
canon _ = Nothing
canonSS :: [Stmt] -> Maybe [Stmt]
canonSS xs | any isBlockS xs = Just $ concatMap f xs
where f (BlockS ss) = ss
f s = [s]
canonSS _ = Nothing
isBlockS :: Stmt -> Bool
isBlockS BlockS{} = True
isBlockS _ = False
isTrue :: Exp -> Bool
isTrue (LitE (EnumL "True_")) = True
isTrue _ = False
isFalse :: Exp -> Bool
isFalse (LitE (EnumL "False_")) = True
isFalse _ = False
dModule :: Bool -> Module -> Module
dModule is_readable x =
rewriteBi canon $
rewriteBi tNoOpS $
rewriteBi canonSS $
rewriteBi tMath $
transformBi tSort $
transformBi reParen $
transformBi tArray $
transformBi tArrayArgTy $
transformBi tUnused $
(if is_readable then rewriteBi tLive else id) $
transformBi tOnlyAssigned $
transformBi tBlock $
rewriteBi tNoOpS $
rewriteBi tNoOpE $
transformBi tPtr $
rewriteBi tNoOpS $
rewriteBi tNoOpE $
transformBi tRHS $
rewriteBi tNoOpS $
rewriteBi tNoOpE $
transformBi tLHS $
transformBi tAlloca $
transformBi tLit $
x
tTypeD :: Decl -> Maybe Decl
tTypeD (Decl (TyArray a b) c) = Just $ Decl a $ c ++ "[" ++ b ++ "]"
tTypeD (Decl (TyPtr a) b) = Just $ Decl a $ "(*" ++ b ++ ")"
tTypeD (Decl (TyFun t ts) x) = Just $ FunD t ("(*" ++ x ++ ")") ts
tTypeD _ = Nothing
tArray :: Define -> Define
tArray x = transformBi i $ transformBi h $ transformBi g x
where
vs = [ v | Decl (TyArray{}) v <- universeBi x ]
f (AddrE (VarE v)) | v `elem` vs = VarE v -- C passes arrays by reference
f a = a
g (CallE a bs) = CallE a $ map f bs
g a = a
h (CallS a bs) = CallS a $ map f bs
h a = a
i (AssignS a@(VarE v) b) | v `elem` vs =
CallS (VarE "memcpy") [a, b, CallE (VarE "sizeof") [a]]
i a = a
tSort :: Define -> Define
tSort (Define x ys) = Define x $ sortBy f cs ++ ds
where
(cs,ds) = partition isDeclS ys
f (DeclS a) (DeclS b) = compare (declNm a) (declNm b)
f _ _ = error "unused:tSort"
isDeclS :: Stmt -> Bool
isDeclS DeclS{} = True
isDeclS _ = False
tName :: Module -> Module
tName x = transformBi j $ transformBi i $ transformBi h $ transformBi g $
transformBi f x
where
f (Decl a b) = Decl a $ rName b
f _ = error "unused:tName"
g (VarE a) = VarE $ rName a
g a = a
h (FunFD a b cs) = FunFD a (rName b) cs
h (RetFunFD a b cs ds) = RetFunFD a (rName b) cs ds
i (EnumC a) = EnumC $ rName a
j (EnumL a) = EnumL $ rName a
j a = a
rName :: String -> String
rName x = strip_underscore $ map f $ filter ((/=) '~') x
where
f '.' = '_'
f c = c
tBlock :: Define -> Define
tBlock x = transformBi f x
where
tbl :: [(String,Stmt)]
tbl = catMaybes $ map h $ universeBi x
f :: Stmt -> Stmt
f s0@(AssignS (VarE v) e) = case lookup v tbl of
Nothing -> s0
Just s -> transformBi g s
where
g (VarE v1) | v1 == v = e
g a = a
f s | s `elem` map snd tbl = NoOpS
f s = s
h :: Stmt -> Maybe (String,Stmt)
h s = case [ v | VarE v <- universeBi s, isFreshVar v ] \\ vs of
[v] -> Just (v,s)
_ -> Nothing
where
vs = case s of
AssignS e _ -> [basename e]
ReturnS e -> [basename e]
_ -> []
-- make sure arithmetic expressions are fully parenthesized
reParen :: Exp -> Exp
reParen (ArrowE (AddrE a) b) = DotE a b
reParen (DotE (LoadE a) b) = ArrowE a b
reParen (CallE a@LoadE{} bs) = CallE (ParenE a) bs
reParen x = x
tUnused :: Define -> Define
tUnused (Define a bs) = Define a (filter f bs)
where
f (DeclS x) = declNm x `elem` vs
f _ = True
vs = nub [ v | VarE v <- universeBi bs ]
tOnlyAssigned :: Define -> Define
tOnlyAssigned x = rewriteBi f x
where
vs = concat [ [v,v] | AssignS (VarE v) (CallE{}) <- universeBi x ] \\
[ v | VarE v <- universeBi x ]
f (AssignS (VarE v) (CallE a bs)) | v `elem` vs = Just $ CallS a bs
f _ = Nothing
tNoOpE :: Exp -> Maybe Exp
tNoOpE (LoadE (AddrE e)) = Just e
tNoOpE (ArrowE (AddrE (ArrowE a b)) c) = Just $ DotE (ArrowE a b) c
tNoOpE (IdxE (AddrE a) b) = Just $ AddrE (IdxE a b)
tNoOpE _ = Nothing
tNoOpS :: [Stmt] -> Maybe [Stmt]
tNoOpS xs
| any isNoOpS xs = Just $ filter (not . isNoOpS) xs
| otherwise = Nothing
isNoOpS :: Stmt -> Bool
isNoOpS NoOpS = True
isNoOpS (AssignS a b) = a == b
isNoOpS (CallS (VarE "memcpy") [a, b, _]) = a == b
isNoOpS _ = False
declNm :: Decl -> String
declNm (Decl _ v) = v
declNm _ = error "unused:declNm"
tLit :: Define -> Define
tLit x = rewriteBi g $ rewriteBi f x
where
tbl = [ (v,e) | AssignS (VarE v) e@LitE{} <- universeBi x ]
f (AssignS (VarE v) _) | isJust (lookup v tbl) = Just NoOpS
f _ = Nothing
g (VarE v) = lookup v tbl
g _ = Nothing
tAlloca :: Define -> Define
tAlloca x@(Define fd _) =
transformBi g $ transformBi f $ rewriteBi h x
where
vs = [ v | AssignS (VarE v) AllocaE{} <- universeBi x ] ++
[ v | Decl (TyPtr TyArray{}) v <- universeBi fd ]
f :: Exp -> Exp
f (ArrowE (AddrE (VarE v)) e) | v `elem` vs = DotE (VarE v) e
f (VarE v) | v `elem` vs = AddrE (VarE v)
f e = e
g (Decl (TyPtr t) v) | v `elem` vs = Decl t v
g a = a
h :: Stmt -> Maybe Stmt
h s | isAllocaS s = Just NoOpS
| otherwise = Nothing
tArrayArgTy :: FunDecl -> FunDecl
tArrayArgTy = transformBi f
where
f :: Type -> Type
f (TyPtr t@TyArray{}) = t
f x = x
isAllocaS :: Stmt -> Bool
isAllocaS (AssignS VarE{} AllocaE{}) = True
isAllocaS _ = False
tPtr :: Define -> Define
tPtr x = transformBi h $ transformBi g $ rewriteBi f x
where
tbl = concat [ let e = VarE ('$':a) in [(a, AddrE e) ,(b, e)]
| AssignS (VarE a) (AddrE (VarE b)) <- universeBi x ]
f (VarE v) = lookup v tbl
f _ = Nothing
g (VarE ('$':v)) = VarE v
g e = e
h (Decl (TyPtr t) v) | v `elem` map fst tbl = Decl t v
h p = p
tRHS :: Define -> Define
tRHS x = rewriteBi f x
where
tbl = [ (v,e) | AssignS e (VarE v) <- universeBi x, isFreshVar v ]
f (VarE v) = lookup v tbl
f _ = Nothing
tLHS :: Define -> Define
tLHS x = rewriteBi f x
where
tbl = [ (v,e) | AssignS (VarE v) e <- universeBi x, isFreshVar v ]
f (VarE v) = lookup v tbl
f _ = Nothing
isFreshVar :: String -> Bool
isFreshVar v = '_' `notElem` v && isDigit (last v)
-- Liveness
tLive :: Define -> Maybe Define -- this is hacky and inefficient, but seems to work
tLive x = case live_tbl x of
[] -> Nothing
(y:_) -> Just $ transformBi (f y) x
where
f (a,b) (VarE v) | v == a = VarE b
f _ a = a
is_reuse :: (String,String) -> Bool
is_reuse (a,b) = a /= b
live_tbl :: Define -> [(Nm, Nm)]
live_tbl (Define _ ss) =
filter is_reuse $ reuse $ liveSS [ d | DeclS d <- ss ] initSt $ sStmts ss
sStmts :: [Stmt] -> [S]
sStmts = concatMap sStmt . reverse
basename :: Exp -> String
basename x = case x of
VarE a -> a
DotE a _ -> basename a
AddrE a -> basename a
ArrowE a _ -> basename a
IdxE a _ -> basename a
CastE _ b -> basename b
LoadE a -> basename a
ParenE a -> basename a
_ -> error $ "unused:basename:" ++ ppShow x
sStmt :: Stmt -> [S]
sStmt x = case x of
AssignS a b -> sExp b ++ [Init $ basename a]
SwitchS a bs -> [Branch $
[ sStmts cs | DefaultAlt cs <- bs ] ++
[ sStmts cs | SwitchAlt _ cs <- bs ]
] ++ sExp a
IfS a bs cs -> [Branch [sStmts bs, sStmts cs]] ++ sExp a
WhenS a bs -> [Branch [sStmts bs]] ++ sExp a
WhileS a bs -> [Loop $ sStmts bs ++ sExp a]
CallS a bs -> sExp a ++ concatMap sExp bs
ReturnS a -> sExp a
DeclS{} -> []
BreakS -> []
RetVoidS -> []
NoOpS -> []
BlockS ss -> sStmts ss
DecS{} -> error $ "unused:sStmt:DecS"
IncS{} -> error $ "unused:sStmt:IncS"
sExp :: Exp -> [S]
sExp x = [ Use v | VarE v <- universeBi x ]
type Nm = String
data S
= Use Nm
| Init Nm
| Branch [[S]]
| Loop [S]
deriving Show
data St = St
{ in_use :: [Nm]
, reuse :: [(Nm, Nm)]
} deriving Show
initSt :: St
initSt = St [] []
liveSS :: [Decl] -> St -> [S] -> St
liveSS vs = loop
where
loop st [] = st
loop st (x:xs) = case x of
Use a -> loop st1 xs
where st1 = st{ in_use = nub $ union [a] $ in_use st }
Init a | isJust $ lookup a $ reuse st -> loop st xs
Init a -> loop st1 xs
where
st1 = St{ in_use = in_use st \\ [a]
, reuse = nub ((a, a1) : reuse st)
}
a1 = reuse_nm a vs $ in_use st
Branch bs -> loop st1 xs
where
sts = map (loop st) bs
st1 = St{ in_use = nub $ foldr1 union $ map in_use sts
, reuse = nub (concatMap reuse sts)
}
Loop bs -> loop st $ concatMap no_inits bs ++ bs ++ xs
reuse_nm :: Nm -> [Decl] -> [Nm] -> Nm
reuse_nm a bs cs =
head $ sort $ a : ((map declNm $ filter (equiv_decl b0) bs) \\ cs)
where
b0 = head $ filter ((==) a . declNm) bs
equiv_decl :: Decl -> Decl -> Bool
equiv_decl (Decl a _) (Decl b _) = a == b
equiv_decl _ _ = error "unused:equiv_decl"
no_inits :: S -> [S]
no_inits x = case x of
Init{} -> []
Use{} -> [x]
Branch bs -> concatMap (concatMap no_inits) bs
Loop bs -> concatMap no_inits bs