pec-0.2.2: Pec/Base.hs
{-# OPTIONS -Wall #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE DeriveDataTypeable #-}
-- The pec embedded compiler
-- Copyright 2011-2012, Brett Letner
module Pec.Base
( module Pec.Base
, module Language.Pir.Abs
, unused
)
where
import Control.Concurrent
import Control.Monad.State
import Data.Data
import Data.Generics.Uniplate.Data
import Data.List
import Development.Shake.FilePath
import Grm.Prims
import Language.Pir.Abs hiding (Exp(..))
import Pec.C
import Pec.IUtil (vtvar, gTyDecls)
import Pec.PUtil
import Prelude hiding (exp)
import System.Console.CmdArgs hiding (atom)
import System.IO.Unsafe
import qualified Language.Pir.Abs as I
import qualified Pec.LLVM as L
data Args = Args
{ march :: Arch
, readable :: Bool
} deriving (Show, Data, Typeable)
argsDesc :: Args
argsDesc = Args
{ march = def &= help "arch to build (C or LLVM)"
, readable = def &= help "generate human readable C (experimental)"
} &= summary summry &= program prog
summry :: String
summry = prog ++ " v" ++ vers ++ ", " ++ copyright
prog :: String
prog = "pecgen"
data E a = E Exp deriving Show
unE :: Typed a => E a -> Exp
unE = f (error "unused:unE")
where
f :: Typed a => a -> E a -> Exp
f a (E x) = seq (addGTyDecls $ tydecls a) x
setE :: Typed a => Exp -> E a
setE = f (error "unused:setE")
where
f :: Typed a => a -> Exp -> E a
f a x = seq (addGTyDecls $ tydecls a) $ E x
data Exp
= VarE TVar
| AppE Exp Exp
| SwitchE Exp Exp [(Exp,Exp)]
| LitE TLit
| LetE TVar Exp Exp
| LamE TVar (Exp -> Exp)
| DefE TVar Exp
instance Show Exp where -- for debugging
show x = case x of
VarE a -> unwords ["VarE", show a]
AppE a b -> unwords ["AppE", show a, show b]
SwitchE a b c -> unwords ["SwitchE", show a, show b, show c]
LitE a -> unwords ["LitE", show a]
LetE a b c -> unwords ["LetE", show a, show b, show c]
LamE a _ -> unwords ["LamE", show a]
DefE a b -> unwords ["DefE", show a, show b]
apps :: [Exp] -> Exp
apps = foldl1 AppE
type M a = State St a
data St = St
{ stmts :: [Stmt]
}
tatom :: Atom -> Type
tatom x = case x of
VarA (TVar _ a) -> a
LitA (TLit _ a) -> a
isVoidA :: Atom -> Bool
isVoidA = isVoidTy . tatom
isVoidE :: I.Exp -> Bool
isVoidE = isVoidTy . texp
isVoidV :: TVar -> Bool
isVoidV = isVoidTy . ttvar
fNoOpS :: [Stmt] -> Maybe [Stmt]
fNoOpS xs | any ((==) NoOpS) xs = Just $ filter ((/=) NoOpS) xs
fNoOpS _ = Nothing
fVoidS :: Stmt -> Maybe Stmt
fVoidS (ReturnS (LitA (TLit VoidL _))) = Nothing
fVoidS (ReturnS a) | isVoidA a = Just $ ReturnS voidA
fVoidS (LetS _ b) | isVoidE b = case b of
I.CallE a bs -> Just $ CallS a bs
_ -> Just NoOpS
fVoidS (CallS a bs) | any isVoidA bs =
Just $ CallS a $ filter (not . isVoidA) bs
-- ^ type of a is no longer correct (it may contain void types)
fVoidS (StoreS _ b) | isVoidA b = Just NoOpS
fVoidS _ = Nothing
fVoidE :: I.Exp -> Maybe I.Exp
fVoidE (I.CallE a bs) | any isVoidA bs =
Just $ I.CallE a $ filter (not . isVoidA) bs
-- ^ type of a is no longer correct (it may contain void types)
fVoidE _ = Nothing
fVoidD :: Define -> Maybe Define
fVoidD (Define a b cs ds) | any isVoidV cs =
Just $ Define a b (filter (not . isVoidV) cs) ds
fVoidD _ = Nothing
texp :: I.Exp -> Type
texp x = case x of
I.CastE _ b -> b
I.AllocaE a -> tyPtr a
I.AtomE a -> tatom a
I.LoadE a -> unTyPtr $ ttvar a
I.CallE a bs -> tcall (ttvar a) $ map tatom bs
tcall :: Type -> [Type] -> Type
tcall a bs = case splitAt (length ts) bs of
(_,[]) -> t
(_,cs) -> tcall t cs
where (t,ts) = unFunTy a
tyRecord :: [(String,Type)] -> TyDecl
tyRecord xs = TyRecord [ FieldT a b | (a,b) <- xs]
tyEnum :: [String] -> TyDecl
tyEnum bs = TyEnum $ map EnumC bs
initSt :: St
initSt = St{ stmts = [] }
stmt :: Stmt -> M ()
stmt x = modify $ \st -> st{ stmts = x : stmts st }
pop_block :: M [Stmt]
pop_block = do
ss0 <- gets stmts
modify $ \st -> st{ stmts = [] }
return $ reverse ss0
push_block :: [Stmt] -> M ()
push_block x = modify $ \st -> st{ stmts = reverse x ++ stmts st }
block :: (Exp -> M a) -> Exp -> M (a,[Stmt])
block f a = do
ss0 <- pop_block
x <- f a
ss1 <- pop_block
push_block ss0
return (x,ss1)
block_ :: (Exp -> M Atom) -> Exp -> M [Stmt]
block_ f a = liftM snd $ block f a
assignAtom :: I.Exp -> M Atom
assignAtom x = do
v <- fresh (texp x)
stmt $ LetS v x
return $ VarA v
ifSwitchS :: Atom -> [Stmt] -> [SwitchAlt] -> M [Stmt]
ifSwitchS _ ys [] = return ys
ifSwitchS x ys (SwitchAlt a bs : zs) = do
ss <- ifSwitchS x ys zs
v <- assignAtom $ I.CallE strEqE [x, LitA a]
return [IfS v bs ss]
strEqE :: TVar
strEqE = TVar "eq" $ tyFun tyIString (tyFun tyIString tyBool)
ttvar :: TVar -> Type
ttvar (TVar _ b) = b
atom :: Exp -> M Atom
atom x = case x of
VarE a -> return $ VarA a
LitE a -> return $ LitA a
DefE a _ -> return $ VarA a
_ -> expr x >>= assignAtom
tvar :: Exp -> M TVar
tvar x = do
a <- atom x
case a of
VarA v -> return v
_ -> error $ "expected variable:" ++ ppShow a
exprFun :: Type -> Exp -> [Exp] -> M I.Exp
exprFun t y ys = do
v <- tvar y
case (v,ys) of
(TVar "load" _, [a]) -> liftM I.LoadE $ tvar a
(TVar "then" _, [a, b]) -> do
ss <- block_ atom a
push_block ss
expr b
(TVar "if" _, [a, b, c]) -> do
r <- fresh (tyPtr t)
stmt $ LetS r $ I.AllocaE t
e <- atom a
bb <- block_ (store r) b
bc <- block_ (store r) c
stmt $ IfS e bb bc
return $ I.LoadE r
(TVar "unsafe_cast" _, [a]) -> do
e <- atom a
case e of
LitA (TLit l _) -> return $ I.AtomE $ LitA $ TLit l t
VarA b -> return $ I.CastE b t
(TVar "when" _, [a, b]) -> do
e <- atom a
bb <- block_ atom b
stmt $ WhenS e bb
return voidE
(TVar "while" _, [a, b]) -> do
(e,aa) <- block atom a
bb <- block_ atom b
stmt $ WhileS aa e bb
return voidE
(TVar "store" _, [a, b]) -> do
r <- tvar a
e <- atom b
stmt $ StoreS r e
return voidE
_ -> do
es <- mapM atom ys
return $ I.CallE v es
unApp :: Exp -> [Exp]
unApp x = case x of
AppE a b -> unApp a ++ [b]
_ -> [x]
unFunTy :: Type -> (Type, [Type])
unFunTy x = case x of
Type "Fun_" ts -> (last ts, init ts)
_ -> error $ "function type expected:" ++ ppShow x
expr :: Exp -> M I.Exp
expr x = case x of
DefE a _ -> case a of
TVar "unsafe_alloca" t -> return $ I.AllocaE (unTyPtr t)
_ -> return $ I.AtomE $ VarA a
VarE a -> return $ I.AtomE $ VarA a
LitE{} -> liftM I.AtomE $ atom x
AppE{} -> do
let (y:ys) = unApp x
let (_,ts) = unFunTy $ tof y
case splitAt (length ts) ys of
(bs,[])
| length bs < length ts ->
error $ "no partial application:" ++ show x
| otherwise -> exprFun (tof x) y ys
(bs,cs) -> do
let e = apps (y:bs)
v <- fresh (tof e)
expr $ LetE v e (apps (VarE v : cs))
SwitchE a b cs -> do
let t = tof b
v <- fresh $ tyPtr t
stmt $ LetS v $ I.AllocaE t
e <- atom a
dflt <- block_ (store v) b
alts <- mapM (alt v) cs
if tof a == tyIString
then ifSwitchS e dflt alts >>= mapM_ stmt -- will have void type
else stmt $ SwitchS e dflt alts
return $ I.LoadE v
LetE a b c -> do
e <- expr b
stmt $ LetS a e
expr c
LamE{} -> error "unapplied lamda expression"
alt :: TVar -> (Exp,Exp) -> M SwitchAlt
alt a (LitE b, c) = do
ss <- block_ (store a) c
return $ SwitchAlt b ss
alt a (AppE (b@LitE{}) _, c) = alt a (b,c)
alt a (b,c) = error $ "alt:pattern match failed:" ++ show (a,b,c)
store :: TVar -> Exp -> M Atom
store a b = do
e <- atom b
stmt $ StoreS a e
return voidA
fresh :: Type -> M TVar
fresh a = return $ TVar (uId a "v") a
fExitS :: [Stmt] -> Maybe [Stmt]
fExitS ss = case break isExitS ss of
(_,[]) -> Nothing
(_,[_]) -> Nothing
(bs,c:_) -> Just $ bs ++ [c]
where
isExitS (CallS a _) = vtvar a == "exit"
isExitS _ = False
fVoidT :: Type -> Maybe Type
fVoidT (Type "Fun_" xs0) | any isVoidTy xs =
Just $ Type "Fun_" $ filter (not . isVoidTy) xs ++ [x]
where
xs = init xs0
x = last xs0
fVoidT _ = Nothing
fSynT :: Type -> Maybe Type
fSynT (Type a _) = case a of
"Idx_" -> Just $ I.Type "W_" [I.Type "Cnt32" []]
"IString_" -> Just $ I.Type "Ptr_" [I.Type "Char_" []]
_ -> Nothing
fCastE :: I.Exp -> Maybe I.Exp
fCastE (I.CastE a b) | ttvar a == b = Just $ I.AtomE $ VarA a
fCastE _ = Nothing
dModule :: FilePath -> String -> [String] -> [Define] -> IO ()
dModule outdir a bs cs = do
let m = Module a (map Import bs) cs
let m1 =
rewriteBi fCastE $
rewriteT $
rewriteBi fExitS $
rewriteBi fNoOpS $
rewriteBi fVoidD $
rewriteBi fVoidE $
rewriteBi fVoidS m
x <- cmdArgs argsDesc
case march x of
C -> cModules outdir (readable x) m1
LLVM -> L.dModule outdir m1
rewriteT :: Data a => a -> a
rewriteT x = rewriteBi fVoidT $ rewriteBi fSynT x
addGTyDecls :: [(Type,TyDecl)] -> ()
{-# NOINLINE addGTyDecls #-}
addGTyDecls xs = unsafePerformIO $ modifyMVar_ gTyDecls $ \ys ->
return $ union (rewriteT xs) ys
defn :: Typed a => E a -> Define
defn x = case unE x of
DefE (TVar a0 t) b -> flip evalState initSt $ do
let a = if a0 == "main_" then "main" else a0
let (vs,c) = unLam b
e <- atom c
ss <- pop_block
return $ Define (fst $ unFunTy t) a vs $ ss ++ [ReturnS e]
_ -> error "defn"
unLam :: Exp -> ([TVar],Exp)
unLam x = case x of
LetE a b c -> let (vs,e) = unLam c in (vs, LetE a b e)
LamE (TVar a b) f -> let (vs,e) = unLam $ f $ VarE v in (v:vs, e)
where v = TVar (a ++ "_") b
_ -> ([],x)
appE :: (Typed a, Typed b) => E (a -> b) -> E a -> E b
appE a b = case unE a of
LamE _ f -> setE (f $ unE b)
_ -> setE (AppE (unE a) (unE b))
data Array_ cnt a
data Pointer_ p a
class Load_ a
class Store_ a
data IString_
data I_ a
data W_ a
data Idx_ a
instance Count a => Arith_ (I_ a)
instance Count a => Arith_ (W_ a)
instance Arith_ Double_
instance Arith_ Float_
instance Floating_ Double_
instance Floating_ Float_
instance Count a => Nmbr (I_ a)
instance Count a => Nmbr (W_ a)
instance Count a => Nmbr (Idx_ a)
instance Nmbr Double_
instance Nmbr Float_
class Ord_ a
class Eq_ a
instance Eq_ Char_
instance Eq_ IString_
instance Count a => Ord_ (I_ a)
instance Count a => Ord_ (W_ a)
instance Ord_ Char_
instance Ord_ Double_
instance Ord_ Float_
instance Count a => Eq_ (W_ a)
instance Count a => Eq_ (I_ a)
instance Count a => Eq_ (Idx_ a)
count_ :: (Count ca, Count cb, Typed a, Typed p) =>
E (Pointer_ p (Array_ ca a) -> W_ cb)
count_ = lamE "" f
where
f :: (Count ca, Count cb, Typed a, Typed p) =>
E (Pointer_ p (Array_ ca a)) -> E (W_ cb)
f (_ :: E (Pointer_ p (Array_ cnt a))) =
nmbrE (show $ countof (unused :: cnt))
data Char_
data Double_
data Float_
data Tag a
class Typed a => Count a where
countof :: a -> Integer
idx_max_ :: E (Idx_ a)
idx_max_ = nmbrE (show $ pred $ countof (unused :: a))
instance Tagged a => Tagged (Tag a) where
tags (_ :: Tag a) = tags (unused :: a)
class StorePtr a
class Typed a => Nmbr a
class Nmbr a => Arith_ a
class Floating_ a
tyPair :: Type -> Type -> Type
tyPair a b = Type "Pair_" [a,b]
unTyPtr :: Type -> Type
unTyPtr (Type _ [a]) = a
unTyPtr _ = error "unTyPtr"
tyPtr :: Type -> Type
tyPtr a = Type "Ptr_" [a]
tyBool :: Type
tyBool = tyPrim "Bool_"
enumTyDecls :: Typed a => [String] -> a -> [(Type, TyDecl)]
enumTyDecls ss a = [(ty a, tyEnum ss)]
class Tagged a where
tags :: a -> [String]
taggedTyDecls :: Typed a =>
[[(Type,TyDecl)]] -> [(String,Type)] -> a -> [(Type, TyDecl)]
taggedTyDecls xs ys z = nub $ concat xs ++
[ (Type (s ++ "tag") [], tyEnum $ map fst ys)
, (t, TyTagged [ ConC a b | (a,b) <- ys ])
]
where
t@(Type s _) = ty z
recordTyDecls :: Typed a =>
[[(Type,TyDecl)]] -> [(String,Type)] -> a -> [(Type, TyDecl)]
recordTyDecls xs ys z =
nub $ concat xs ++ [(ty z, TyRecord [ FieldT a b | (a,b) <- ys ])]
class Typed a where
ty :: a -> Type
tydecls :: a -> [(Type, TyDecl)]
tydecls _ = []
tydecls_ :: (Typed a, Typed b) => a -> b -> [(Type, TyDecl)]
tydecls_ a _ = tydecls a
instance Count a => Typed (I_ a) where
ty _ = Type "I_" [ty (unused :: a)]
instance Count a => Typed (W_ a) where
ty _ = Type "W_" [ty (unused :: a)]
instance Count a => Typed (Idx_ a) where
ty _ = Type "Idx_" [ty (unused :: a)]
instance Typed a => Typed (Tag a) where
ty _ = Type (s ++ "tag") []
where I.Type s _ = ty (unused :: a)
instance Typed () where
ty _ = tyVoid
instance (Typed a, Typed b) => Typed (a -> b) where
ty _ = tyFun (ty (unused :: a)) (ty (unused :: b))
tydecls _ =
tydecls (unused :: a) ++ tydecls (unused :: b)
instance (Count cnt, Typed a) => Typed (Array_ cnt a) where
ty _ = tyArray (countof (unused :: cnt)) (ty (unused :: a))
tydecls _ = tydecls (unused :: a)
tyArray :: Integer -> Type -> Type
tyArray a b = Type "Array_" [tyCnt a, b]
instance Typed Char_ where
ty _ = tyChar
instance Typed Double_ where
ty _ = tyDouble
instance Typed Float_ where
ty _ = tyFloat
instance Typed IString_ where
ty _ = tyIString
instance (Typed p, Typed a) => Typed (Pointer_ p a) where
ty _ = tyPtr (ty (unused :: a))
tydecls _ = tydecls (unused :: a)
isVoidTy :: Type -> Bool
isVoidTy = (==) tyVoid
tyVoid :: Type
tyVoid = tyPrim "Void_"
tyPrim :: String -> Type
tyPrim a = Type a []
letE :: (Typed a, Typed b) => String -> E a -> (E a -> E b) -> E b
letE a0 b f =
let a = uId a0 a0 in
setE (LetE (TVar a (tof $ unE b)) (unE b) $ unE $ f $ varE a)
tyFun :: Type -> Type -> Type
tyFun a b = case b of
Type "Fun_" cs -> Type "Fun_" (a:cs)
_ -> Type "Fun_" [a,b]
tof :: Exp -> Type
tof x = case x of
VarE a -> ttvar a
DefE a _ -> ttvar a
LamE (TVar a b) f -> tyFun b (tof $ f $ VarE $ TVar a b)
AppE a _ -> case tail ts of
[] -> t
bs -> Type "Fun_" $ bs ++ [t]
where (t,ts) = unFunTy $ tof a
SwitchE _ b _ -> tof b
LitE (TLit _ b) -> b
LetE _ _ c -> tof c
fixArity :: Int -> Type -> Type
fixArity 0 x = x
fixArity n x = case splitAt n ts of
(_,[]) -> x
(bs,cs) -> Type "Fun_" $ bs ++ [Type "Fun_" $ cs ++ [t]]
where
(t,ts) = unFunTy x
arityDefE :: Typed a => Int -> String -> E a -> E a
arityDefE n a b = setE (DefE (TVar a (fixArity n $ tof e)) e)
where e = unE b
defE :: Typed a => String -> E a -> E a
defE a b = arityDefE (arityDef $ unE b) a b
arityDef :: Exp -> Int
arityDef = length . fst . unLam
extern :: Typed a => E (IString_ -> IString_ -> a)
extern = lamE "" $ \x -> lamE "" $ \y ->
let v = unStringE x in arityDefE (read $ unStringE y) v (varE v)
unStringE :: E IString_ -> String
unStringE x = case unE x of
LitE (TLit (StringL s) _) -> s
_ -> error "unStringE"
varE :: Typed a => String -> E a
varE = f (error "unused:varE")
where
f :: Typed a => a -> String -> E a
f a s = setE (VarE $ TVar s (ty a))
lamE :: (Typed a, Typed b) => String -> (E a -> E b) -> E (a -> b)
lamE s (f :: (E a -> E b)) =
setE (LamE (TVar s (ty (unused :: a))) (\e -> unE (f (setE e))))
switchE :: (Typed a, Typed b) => E a -> E b -> [(E a, E b)] -> E b
switchE a b cs = setE (SwitchE (unE a) (unE b)
[ (unE x, unE y) | (x,y) <- cs ])
switchE_ :: (Tagged a, Typed a, Typed b) => E a -> [(E a, E b)] -> E b
switchE_ (a :: E a) bs = case tags (unused :: a) \\ xs of
[] -> switchE a (snd $ last bs) (init bs)
ts -> error $ "unmatched tag(s):" ++ unwords (take 10 ts)
where
xs = map (get_tag . unE . fst) bs
get_tag :: Exp -> String
get_tag x = case x of
AppE a _ -> get_tag a
LitE (TLit (EnumL a) _) -> a
_ -> error "unused:get_tag"
litE :: Lit -> Type -> Exp
litE a b = LitE (TLit a b)
charE :: Char -> E Char_
charE x = setE $ litE (CharL x) tyChar
stringE :: String -> E IString_
stringE x = setE $ litE (StringL x) tyIString
nmbrE :: Nmbr a => String -> E a
nmbrE = f (error "unused:nmbrE")
where
f :: Nmbr a => a -> String -> E a
f a i = setE $ litE (NmbrL i) (ty a)
tyChar :: Type
tyChar = tyPrim "Char_"
tyDouble :: Type
tyDouble = tyPrim "Double_"
tyFloat :: Type
tyFloat = tyPrim "Float_"
tyIString :: Type
tyIString = tyPrim "IString_"
tyCnt :: Integer -> Type
tyCnt x = tyPrim ("Cnt" ++ show x)
un :: (Typed a, Typed b, Typed p, Load_ p) =>
E (IString_ -> Pointer_ p a -> b)
un = varE "un"
tg :: (Typed a) => E (IString_ -> a)
tg = f (error "unused:tg")
where
f :: (Typed a) => a -> E (IString_ -> a)
f a = lamE "" $ \x -> setE $ litE (EnumL $ unStringE x) (ty a)
storeE :: (Typed a, Typed p, Store_ p) => E (Pointer_ p a -> a -> ())
storeE = varE "store"
fld :: (Typed a, Typed b, Typed p) =>
E (IString_ -> Pointer_ p a -> Pointer_ p b)
fld = lamE "" $ \x -> lamE "" $ \y -> appE (varE $ unStringE x ++ "fld") y
unwrap_ :: (Typed a, Typed b) => E (IString_ -> a -> b)
unwrap_ = lamE "" $ \_ -> lamE "" $ \a -> setE (unE a)
unwrapptr_ :: (Typed a, Typed b, Typed p) =>
E (IString_ -> Pointer_ p a -> Pointer_ p b)
unwrapptr_ = lamE "" $ \_ -> lamE "" $ \a -> setE (unE a)
mk :: (Typed a) => E (IString_ -> a)
mk = varE "mk"
uni :: (Typed a) => E (IString_ -> a)
uni = lamE "" $ \_ -> setE (LitE voidL)
voidL :: TLit
voidL = TLit VoidL tyVoid
voidA :: Atom
voidA = LitA voidL
voidE :: I.Exp
voidE = I.AtomE voidA
nt :: (Typed a, Typed b) => E (IString_ -> a -> b)
nt = lamE "" $ \_ -> lamE "" $ \a -> setE (unE a)
tagv :: (Typed a, Typed b, Typed p, Load_ p) => E (Pointer_ p a -> b)
tagv = varE "tagv"
unsafe_cast_ :: (Typed a, Typed b) => E (a -> b)
unsafe_cast_ = varE "unsafe_cast"