{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeApplications #-} module Compile ( compile, ) where import Control.Monad.Trans.State.Strict import Data.Bifunctor (first, second) import Data.Foldable (toList) import Data.Functor.Const import qualified Data.Functor.Product as Product import Data.List (intersperse, intercalate) import qualified Data.Map.Strict as Map import qualified Data.Set as Set import Data.Set (Set) import AST import AST.Pretty (ppTy) import Data -- In shape and index arrays, the innermost dimension is on the right (last index). data StructDecl = StructDecl String -- ^ name String -- ^ contents String -- ^ comment deriving (Show) data Stmt = SVarDecl Bool String String CExpr -- ^ const, type, variable name, right-hand side | SVarDeclUninit String String -- ^ type, variable name (no initialiser) | SAsg String CExpr -- ^ variable name, right-hand side | SBlock [Stmt] | SIf CExpr [Stmt] [Stmt] | SVerbatim String deriving (Show) data CExpr = CELit String | CEStruct String [(String, CExpr)] | CEProj CExpr String | CECall String [CExpr] | CEBinop CExpr String CExpr | CEIf CExpr CExpr CExpr deriving (Show) printStructDecl :: StructDecl -> ShowS printStructDecl (StructDecl name contents comment) = showString "typedef struct { " . showString contents . showString " } " . showString name . showString ("; // " ++ comment) printStmt :: Int -> Stmt -> ShowS printStmt indent = \case SVarDecl cnst typ name rhs -> showString ((if cnst then "const " else "") ++ typ ++ " " ++ name ++ " = ") . printCExpr 0 rhs . showString ";" SVarDeclUninit typ name -> showString (typ ++ " " ++ name ++ ";") SAsg name rhs -> showString (name ++ " = ") . printCExpr 0 rhs . showString ";" SBlock stmts -> showString "{" . compose [showString ("\n" ++ replicate (2*indent+2) ' ') . printStmt (indent+1) stmt | stmt <- stmts] . showString ("\n" ++ replicate (2*indent) ' ' ++ "}") SIf cond b1 b2 -> showString "if (" . printCExpr 0 cond . showString ") " . printStmt indent (SBlock b1) . showString " else " . printStmt indent (SBlock b2) SVerbatim s -> showString s -- d values: -- * 0: top level -- * 1: in 1st or 2nd component of a ternary operator (technically same as top level, but readability) -- * 2-...: various operators (see precTable) -- * 98: inside unknown operator -- * 99: left of a field projection -- Unlisted operators are conservatively written with full parentheses. printCExpr :: Int -> CExpr -> ShowS printCExpr d = \case CELit s -> showString s CEStruct name pairs -> showParen (d >= 99) $ showString ("(" ++ name ++ "){") . compose (intersperse (showString ", ") [showString ("." ++ n ++ " = ") . printCExpr 0 e | (n, e) <- pairs]) . showString "}" CEProj e name -> printCExpr 99 e . showString ("." ++ name) CECall n es -> showString (n ++ "(") . compose (intersperse (showString ", ") (map (printCExpr 0) es)) . showString ")" CEBinop e1 n e2 -> let mprec = Map.lookup n precTable p = maybe (-1) fst mprec -- precedence of this operator (d1, d2) = maybe (98, 98) snd mprec -- precedences for the arguments in showParen (d > p) $ printCExpr d1 e1 . showString (" " ++ n ++ " ") . printCExpr d2 e2 CEIf e1 e2 e3 -> showParen (d > 0) $ printCExpr 1 e1 . showString " ? " . printCExpr 1 e2 . showString " : " . printCExpr 0 e3 where precTable = Map.fromList [("||", (2, (2, 2))) ,("&&", (3, (3, 3))) ,("==", (4, (5, 5))) ,("!=", (4, (5, 5))) ,("<", (5, (6, 6))) ,(">", (5, (6, 6))) ,("<=", (5, (6, 6))) ,(">=", (5, (6, 6))) ,("+", (6, (6, 6))) ,("-", (6, (6, 7))) ,("*", (7, (7, 7))) ,("/", (7, (7, 8))) ,("%", (7, (7, 8)))] repTy :: Ty -> String repTy (TScal st) = case st of TI32 -> "int32_t" TI64 -> "int64_t" TF32 -> "float" TF64 -> "double" TBool -> "bool" repTy t = genStructName t repSTy :: STy t -> String repSTy = repTy . unSTy genStructName :: Ty -> String genStructName = \t -> "ty_" ++ gen t where -- all tags start with a letter, so the array mangling is unambiguous. gen :: Ty -> String gen TNil = "n" gen (TPair a b) = 'P' : gen a ++ gen b gen (TEither a b) = 'E' : gen a ++ gen b gen (TMaybe t) = 'M' : gen t gen (TArr n t) = "A" ++ show (fromNat n) ++ gen t gen (TScal st) = case st of TI32 -> "i" TI64 -> "j" TF32 -> "f" TF64 -> "d" TBool -> "b" gen (TAccum t) = 'C' : gen t genStruct :: String -> Ty -> Maybe StructDecl genStruct name topty = case topty of TNil -> Just $ StructDecl name "" com TPair a b -> Just $ StructDecl name (repTy a ++ " a; " ++ repTy b ++ " b;") com TEither a b -> -- 0 -> a, 1 -> b Just $ StructDecl name ("uint8_t tag; union { " ++ repTy a ++ " a; " ++ repTy b ++ " b; };") com TMaybe t -> -- 0 -> nothing, 1 -> just Just $ StructDecl name ("uint8_t tag; " ++ repTy t ++ " a;") com TArr n t -> Just $ StructDecl name ("size_t sh[" ++ show (fromNat n) ++ "]; " ++ repTy t ++ " *a;") com TScal _ -> Nothing TAccum t -> Just $ StructDecl name (repTy t ++ " a;") com where com = ppTy 0 topty -- State: (already-generated (skippable) struct names, the structs in declaration order) genStructs :: Ty -> State (Set String, Bag StructDecl) () genStructs ty = do let name = genStructName ty seen <- gets ((name `Set.member`) . fst) case (if seen then Nothing else genStruct name ty) of Nothing -> pure () Just decl -> do -- already mark this struct as generated now, so we don't generate it twice modify (first (Set.insert name)) case ty of TNil -> pure () TPair a b -> genStructs a >> genStructs b TEither a b -> genStructs a >> genStructs b TMaybe t -> genStructs t TArr _ t -> genStructs t TScal _ -> pure () TAccum t -> genStructs t modify (second (<> pure decl)) genAllStructs :: Foldable t => t Ty -> [StructDecl] genAllStructs tys = toList . snd $ execState (mapM_ genStructs tys) (mempty, mempty) data CompState = CompState { csStructs :: Set Ty , csStmts :: Bag Stmt , csNextId :: Int } deriving (Show) type CompM a = State CompState a genId :: CompM Int genId = state $ \s -> (csNextId s, s { csNextId = csNextId s + 1 }) genName :: CompM String genName = ('x' :) . show <$> genId emit :: Stmt -> CompM () emit stmt = modify $ \s -> s { csStmts = csStmts s <> pure stmt } scope :: CompM a -> CompM (a, [Stmt]) scope m = do stmts <- state $ \s -> (csStmts s, s { csStmts = mempty }) res <- m innerStmts <- state $ \s -> (csStmts s, s { csStmts = stmts }) return (res, toList innerStmts) emitStruct :: STy t -> CompM String emitStruct ty = do let ty' = unSTy ty modify $ \s -> s { csStructs = Set.insert ty' (csStructs s) } return (genStructName ty') nameEnv :: SList f env -> SList (Const String) env nameEnv = flip evalState (0::Int) . slistMapA (\_ -> state $ \i -> (Const ("arg" ++ show i), i + 1)) compile :: SList STy env -> Ex env t -> String compile env expr = let args = nameEnv env (res, s) = runState (compile' args expr) (CompState mempty mempty 1) structs = genAllStructs (csStructs s <> Set.fromList (unSList unSTy env)) in ($ "") $ compose [compose $ map (\sd -> printStructDecl sd . showString "\n") structs ,showString "\n" ,showString $ repSTy (typeOf expr) ++ " kernel(" ++ intercalate ", " (reverse (unSList (\(Product.Pair t n) -> repSTy t ++ " " ++ getConst n) (slistZip env args))) ++ ") {\n" ,compose $ map (\st -> showString " " . printStmt 1 st . showString "\n") (toList (csStmts s)) ,showString (" return ") . printCExpr 0 res . showString ";\n}\n"] compile' :: SList (Const String) env -> Ex env t -> CompM CExpr compile' env = \case EVar _ _ i -> return $ CELit (getConst (slistIdx env i)) ELet _ rhs body -> do e <- compile' env rhs var <- genName emit $ SVarDecl True (repSTy (typeOf rhs)) var e compile' (Const var `SCons` env) body EPair _ a b -> do name <- emitStruct (STPair (typeOf a) (typeOf b)) e1 <- compile' env a e2 <- compile' env b return $ CEStruct name [("a", e1), ("b", e2)] EFst _ e -> CEProj <$> compile' env e <*> pure "a" ESnd _ e -> CEProj <$> compile' env e <*> pure "b" ENil _ -> do name <- emitStruct STNil return $ CEStruct name [] EInl _ t e -> do name <- emitStruct (STEither (typeOf e) t) e1 <- compile' env e return $ CEStruct name [("tag", CELit "0"), ("a", e1)] EInr _ t e -> do name <- emitStruct (STEither t (typeOf e)) e2 <- compile' env e return $ CEStruct name [("tag", CELit "1"), ("b", e2)] ECase _ (EOp _ OIf e) a b -> do e1 <- compile' env e (e2, stmts2) <- scope $ compile' (Const undefined `SCons` env) a -- don't access that nil, stupid you (e3, stmts3) <- scope $ compile' (Const undefined `SCons` env) b retvar <- genName emit $ SVarDeclUninit (repSTy (typeOf a)) retvar emit $ SIf e1 (stmts2 <> pure (SAsg retvar e2)) (stmts3 <> pure (SAsg retvar e3)) return (CELit retvar) ECase _ e a b -> do let STEither t1 t2 = typeOf e e1 <- compile' env e var <- genName fieldvar <- genName (e2, stmts2) <- scope $ compile' (Const fieldvar `SCons` env) a (e3, stmts3) <- scope $ compile' (Const fieldvar `SCons` env) b retvar <- genName emit $ SVarDeclUninit (repSTy (typeOf a)) retvar emit $ SBlock (pure (SVarDecl True (repSTy (typeOf e)) var e1) <> pure (SIf (CEBinop (CEProj (CELit var) "tag") "==" (CELit "0")) (pure (SVarDecl True (repSTy t1) fieldvar (CEProj (CELit var) "a")) <> stmts2 <> pure (SAsg retvar e2)) (pure (SVarDecl True (repSTy t2) fieldvar (CEProj (CELit var) "b")) <> stmts3 <> pure (SAsg retvar e3)))) return (CELit retvar) ENothing _ t -> do name <- emitStruct (STMaybe t) return $ CEStruct name [("tag", CELit "0")] EJust _ e -> do name <- emitStruct (STMaybe (typeOf e)) e1 <- compile' env e return $ CEStruct name [("tag", CELit "1"), ("a", e1)] EMaybe _ a b e -> do e1 <- compile' env e var <- genName fieldvar <- genName (e2, stmts2) <- scope $ compile' env a (e3, stmts3) <- scope $ compile' (Const fieldvar `SCons` env) b retvar <- genName emit $ SVarDeclUninit (repSTy (typeOf a)) retvar emit $ SBlock (pure (SVarDecl True (repSTy (typeOf e)) var e1) <> pure (SIf (CEBinop (CEProj (CELit var) "tag") "==" (CELit "0")) (stmts2 <> pure (SAsg retvar e2)) (pure (SVarDecl True (repSTy (typeOf b)) fieldvar (CEProj (CELit var) "a")) <> stmts3 <> pure (SAsg retvar e3)))) return (CELit retvar) -- EConstArr _ n t arr -> do -- name <- emitStruct (STArr n (STScal t)) -- error "TODO" -- EBuild _ n a b -> error "TODO" -- genStruct (STArr n (typeOf b)) <> EBuild ext n (compile' a) (compile' b) -- EFold1Inner _ a b c -> error "TODO" -- EFold1Inner ext (compile' a) (compile' b) (compile' c) -- ESum1Inner _ e -> error "TODO" -- ESum1Inner ext (compile' e) -- EUnit _ e -> error "TODO" -- EUnit ext (compile' e) -- EReplicate1Inner _ a b -> error "TODO" -- EReplicate1Inner ext (compile' a) (compile' b) -- EMaximum1Inner _ e -> error "TODO" -- EMaximum1Inner ext (compile' e) -- EMinimum1Inner _ e -> error "TODO" -- EMinimum1Inner ext (compile' e) EConst _ t x -> case t of STI32 -> return $ CELit $ "(int32_t)" ++ show x STI64 -> return $ CELit $ "(int64_t)" ++ show x STF32 -> return $ CELit $ show x ++ "f" STF64 -> return $ CELit $ show x STBool -> return $ CELit $ if x then "true" else "false" -- EIdx0 _ e -> error "TODO" -- EIdx0 ext (compile' e) -- EIdx1 _ a b -> error "TODO" -- EIdx1 ext (compile' a) (compile' b) -- EIdx _ a b -> error "TODO" -- EIdx ext (compile' a) (compile' b) -- EShape _ e -> error "TODO" -- EShape ext (compile' e) EOp _ op (EPair _ e1 e2) -> do e1' <- compile' env e1 e2' <- compile' env e2 compileOpPair op e1' e2' EOp _ op e -> do e' <- compile' env e compileOpGeneral op e' -- ECustom _ t1 t2 t3 a b c e1 e2 -> error "TODO" -- ECustom ext t1 t2 t3 (compile' a) (compile' b) (compile' c) (compile' e1) (compile' e2) -- EWith _ a b -> error "TODO" -- EWith (compile' a) (compile' b) -- EAccum _ n a b e -> error "TODO" -- EAccum n (compile' a) (compile' b) (compile' e) EError _ t s -> do name <- emitStruct t -- using 'show' here is wrong, but it's good enough for me. emit $ SVerbatim $ "fprintf(stderr, \"ERROR: %s\\n\", " ++ show s ++ "); exit(1);" return $ CEStruct name [] EZero{} -> error "Compile: monoid operations should have been eliminated" EPlus{} -> error "Compile: monoid operations should have been eliminated" EOneHot{} -> error "Compile: monoid operations should have been eliminated" _ -> error "Compile: not implemented" compileOpGeneral :: SOp a b -> CExpr -> CompM CExpr compileOpGeneral op e1 = do let unary cop = return @(State CompState) $ CECall cop [e1] let binary cop = do name <- genName emit $ SVarDecl True (repSTy (opt1 op)) name e1 return $ CEBinop (CEProj (CELit name) "a") cop (CEProj (CELit name) "b") case op of OAdd _ -> binary "+" OMul _ -> binary "*" ONeg _ -> unary "-" OLt _ -> binary "<" OLe _ -> binary "<=" OEq _ -> binary "==" ONot -> unary "!" OAnd -> binary "&&" OOr -> binary "||" OIf -> do name <- emitStruct (STEither STNil STNil) _ <- emitStruct STNil return $ CEIf e1 (CEStruct name [("tag", CELit "0")]) (CEStruct name [("tag", CELit "1")]) ORound64 -> unary "(int64_t)round" -- ew OToFl64 -> unary "(double)" ORecip _ -> return $ CEBinop (CELit "1.0") "/" e1 OExp STF32 -> unary "expf" OExp STF64 -> unary "exp" OLog STF32 -> unary "logf" OLog STF64 -> unary "log" OIDiv _ -> binary "/" compileOpPair :: SOp a b -> CExpr -> CExpr -> CompM CExpr compileOpPair op e1 e2 = do let binary cop = return @(State CompState) $ CEBinop e1 cop e2 case op of OAdd _ -> binary "+" OMul _ -> binary "*" OLt _ -> binary "<" OLe _ -> binary "<=" OEq _ -> binary "==" OAnd -> binary "&&" OOr -> binary "||" OIDiv _ -> binary "/" _ -> error "compileOpPair: got unary operator" compose :: Foldable t => t (a -> a) -> a -> a compose = foldr (.) id