pec-0.2.0: 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 Distribution.Text
import Grm.Prims
import Language.Pir.Abs hiding (Exp(..))
import Paths_pec
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" ++ display version ++ ", " ++ 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"