-- | Functions to write bytecode sequences to file module ByteCode.Write(bcWrite,withDirectory ) where import ByteCode.Type --import BCTags import qualified Data.Map as Map import Control.Monad.State import List(sortBy) import Char(chr) import SysDeps(trace,openBinaryFileWrite) import Flags import Util.Extra import Util.Text(splitList) import IntState import IO import System.FilePath import System.Directory import ForeignCode import Syntax(CallConv(..)) import Data.Maybe(fromJust) ----------------------------------------------------------------------------------------------------------- -- | the state of the writing monad data WState = WS { wsStrings :: Map.Map String StringId, wsFreeStrings :: [StringId], wsOutput :: Binary } type StringId = Int type Binary = [Char] -- | a writing monad type Writer a = State WState a ----------------------------------------------------------------------------------------------------------- -- | the current bytecode version bcVersion :: (Int,Int) bcVersion = (1,10) -- | Write a sequence of bytecode declarations into a file. bcWrite :: IntState -- ^ internal compiler state -> Flags -- ^ compiler flags -> FileFlags -- ^ info about the file to write -> BCModule -- ^ bytecode declarations to write to file -> IO () bcWrite state flags fflags prog = withDirectory (sObjectFile fflags) id (reverse $ wsOutput ws') where (_,ws') = runState (wProgram prog) $ WS Map.empty [0..] [] -- | write the data to the correct file and directory withDirectory :: String -> (String -> String) -> String -> IO () withDirectory dstPath xform dat = do let dstDir = takeDirectory dstPath dstFile = takeFileName dstPath createDirectoryIfMissing True dstDir handle <- openBinaryFileWrite (combine dstDir (xform dstFile)) hPutStr handle dat hFlush handle hClose handle -- | write a program to a file wProgram :: BCModule -> Writer () wProgram m = do mapM_ wChar "HSBC" wUShort (fst bcVersion) wUShort (snd bcVersion) wUShort 0x00 -- flags wUShort (length $ bcmDecls m) decls <- inNewBlock $ mapM_ wDecl (bcmDecls m) mref <- inNewBlock $ wModuleName (bcmModule m) wStringTable wBlock mref wBlock decls -- | write a single declaration wDecl :: BCDecl -> Writer () wDecl (Fun name pos arity args code consts prim stack numDict fl) = do wLocal name wSizedBlock $ do wChar 'F' wUByte arity wUShort stack wUByte (intFlags fl) wConstTable consts wCode code wDecl (Con name pos size tag) = do wLocal name wSizedBlock $ do { wChar 'C' ; wUByte size ; wUByte tag } wDecl (External name pos arity cname cconv args) = do wLocal name wSizedBlock $ do wChar 'X' wString cname wUShort arity wCallConv cconv wExternalArg (last args) mapM_ wExternalArg (init args) wDecl (Prim name pos) = do wLocal name wSizedBlock $ do { wChar 'P' ; wQualif name } -- | write a constant table (of a function) wConstTable :: ConstTable -> Writer () wConstTable ct = do wUShort (length ct') mapM_ wConst ct' where ct' = map snd $ sortBy (\(x,_) (y,_) -> compare x y) $ Map.toList ct -- | write a single constant table item wConst :: ConstItem -> Writer () wConst (CInt i) = do { wChar 'i' ; wInt i } wConst (CInteger i) = do { wChar 'l' ; wInteger i } wConst (CFloat f) = do { wChar 'f' ; wFloat f } wConst (CDouble d) = do { wChar 'd' ; wDouble d } wConst (CString s) = do { wChar 's' ; wString s } wConst (CPos p) = return () wConst (CVarDesc n p) = return () wConst (CGlobal i y) = do { wChar (gType y) ; wQualif i } where gType g = fromJust $ lookup g [ (GCAF,'A'), (GFUN,'F'), (GFUN0,'0'), (GCON,'C'), (GZCON,'Z'), (GPRIM,'P'), (GEXT,'X') ] -- | write the code of a function wCode :: Code -> Writer () wCode (CWrites ws) = wSizedBlock $ mapM_ wWrite ws where wWrite (WUByte n) = wUByte n wWrite (WUShort n) = wUShort n wWrite (WByte n) = wByte n wWrite (WShort n) = wShort n -- | write out a calling convention wCallConv :: String -> Writer () wCallConv cc = case lookup cc cmap of Just c -> wChar c _ -> error $ "Writing bytecode: the calling convention '"++cc++"' is not supported" where cmap = [ ("ccall",'c'), ("cast",'x'), ("address",'a'), ("primitive",'p'), ("stdcall",'s'), ("fastccall",'C'), ("faststdcall",'S'), ("builtin",'b') ] -- | write an external arg wExternalArg :: String -> Writer () wExternalArg ex = case lookup ex exs of Just c -> wChar c _ -> error $ "Unknown external arg type '"++ex++"'" where exs = [ ("Data.Int;Int8",'i'), ("Data.Int;Int16",'j'), ("Data.Int;Int32",'k'), ("Data.Int;Int64",'l'), ("Data.Word;Word8",'w'), ("Data.Word;Word16",'x'), ("Data.Word;Word32",'y'), ("Data.Word;Word64",'z'), ("Prelude;Int",'I'), ("Prelude;Float",'F'), ("Prelude;Double",'D'), ("Prelude;Char",'C'), ("Prelude;Bool",'B'), ("Foreign.Ptr;Ptr",'P'), ("Foreign.Ptr;FunPtr",'P'),("Foreign.StablePtr;StablePtr",'p'), ("Foreign.ForeignPtr;ForeignPtr",'f'), ("Data.PackedString;PackedString",'u'), ("Prelude;Integer",'N'), ("Prelude;->",'H'), ("Prelude;a",'u'), ("Prelude;()",'U') ] -- | write the table of strings wStringTable :: Writer () wStringTable = do st <- gets wsStrings let st' = map fst $ sortBy (\(_,x) (_,y) -> compare x y) $ Map.toList st wUShort (length st') mapM_ wString st' ----------------------------------------------------------------------------------------------------------- -- | write a fully qualified id, i.e. module name and item name wQualif :: String -> Writer () wQualif name = do { wModuleName mod ; wUnqualif ";" item } where (mod,item) = splitQualified name -- | write the local part of a fully qualified name (i.e. no module name) wLocal :: String -> Writer () wLocal name = wUnqualif ";" $ snd $ splitQualified name -- | write a module name wModuleName :: String -> Writer () wModuleName name = wUnqualif "." name -- | write an unqualified name, this should either be a module name or an item name but not both wUnqualif :: String -> String -> Writer () wUnqualif sep name | length parts == 0 = error $ "wUnqualif: really shouldn't get empty name '"++name++"'" | otherwise = do wUByte (length parts) mapM_ wStringRef parts where parts = splitList sep name -- | write a reference to a string, this allocates a new string in the table and writes it's id wStringRef :: String -> Writer () wStringRef s = do { i <- addString s ; wUShort i } -- | write a single character (8 bits) wChar :: Char -> Writer () wChar c = modify $ \ ws -> ws { wsOutput = c : wsOutput ws } -- | write a signed byte (8 bits) wByte :: Int -> Writer () wByte i | isByte i = wChar $ chr $ (i + 256) `mod` 256 | otherwise = error $ "wByte of "++show i -- | write an unsigned byte (8 bits) wUByte :: Int -> Writer () wUByte i | isUByte i = wChar $ chr i | otherwise = error $ "wUByte of "++show i -- | write a generic integer up to some number of bytes wIntGeneric :: Integer -> Int -> Writer () wIntGeneric i 0 | i == 0 || i == (-1) = return () | otherwise = error $ "wIntGeneric "++show i ++" 0" wIntGeneric i n = do { wIntGeneric hi (n-1) ; wUByte (fromInteger lo) } where (hi,lo) = i `divMod` 256 -- | write a signed short integer (16 bits) wShort :: Int -> Writer () wShort i | isShort i = wIntGeneric (toInteger i) 2 | otherwise = error $ "wShort of "++show i -- | write an unsigned short integer (16 bits) wUShort :: Int -> Writer () wUShort i | isUShort i = wIntGeneric (toInteger i) 2 | otherwise = error $ "wUShort of "++show i -- | write a signed integer (32 bits) wInt :: Int -> Writer () wInt i | isInt i = wIntGeneric (toInteger i) 4 | otherwise = error $ "wInt of "++show i -- | write an arbitrary sized integer wInteger :: Integer -> Writer () wInteger i | i == 0 = wByte 0 | otherwise = do iblock <- inNewBlock $ wInteger' (abs i) wByte $ length iblock * fromInteger (signum i) wBlock iblock where wInteger' 0 = return () wInteger' i = do { wInteger' hi ; wUByte (fromInteger lo) } where (hi,lo) = i `divMod` 256 -- | write a string wString :: String -> Writer () wString s = do { wUShort (length s) ; mapM_ wChar s } -- | write a floating point number wFloat :: Float -> Writer () wFloat f = do { wInteger mant ; wShort exp } where (mant,exp) = decodeFloat f -- | write a double precision floating point number wDouble :: Double -> Writer () wDouble f = do { wInteger mant ; wShort exp } where (mant,exp) = decodeFloat f -- | write a block with preceeded by its length wSizedBlock :: Writer () -> Writer () wSizedBlock w = do block <- inNewBlock w wUShort (length block) wBlock block -- | write a block (previous returned by 'inNewBlock') -- | assumes block is already reversed wBlock :: Binary -> Writer () wBlock block = modify $ \ ws -> ws { wsOutput = block ++ wsOutput ws } ----------------------------------------------------------------------------------------------------------- -- | take a writer computation and perform its operations in a new output block inNewBlock :: Writer () -> Writer Binary inNewBlock w = do old <- State $ \ ws -> (wsOutput ws, ws { wsOutput = [] }) w State $ \ ws -> (wsOutput ws, ws { wsOutput = old }) -- | add a new string to the string table addString :: String -> Writer StringId addString s = State $ \ ws -> case Map.lookup s (wsStrings ws) of Just i -> (i,ws) Nothing -> let (i:is) = wsFreeStrings ws in (i, ws { wsFreeStrings = is, wsStrings = Map.insert s i (wsStrings ws) }) ----------------------------------------------------------------------------------------------------------- {- -- | separate a list with a separator, e.g. -- -- seperateBy (==';') "abc;def;;gh;" = [ "abc","def","","gh","" ] separateBy :: (a -> Bool) -> [a] -> [[a]] separateBy sep [] = [] separateBy sep xs = case break sep xs of (before,[]) -> [before] (before,_:ys) -> before : separateBy sep ys -} ----------------------------------------------------------------------------------------------------------- {- wConstTable :: ConstTable -> Writer wConstTable ct = wUShort (length ct') >>> wMap wConst ct' where ct' = map snd $ sortBy (\(x,_) (y,_) -> compare x y) $ Map.toList ct wConst :: ConstItem -> Writer wConst (CGlobal i y) = wGType y >>> wId i wConst (CInt i) = wChar 'i' >>> wInt i wConst (CInteger i) = wChar 'l' >>> wInteger i wConst (CFloat f) = wChar 'f' >>> wFloat f wConst (CDouble f) = wChar 'd' >>> wDouble f wConst (CString s) = wChar 's' >>> wString s wConst (CPos p) = let (P s e) = p in wChar 'p' >>> wInt s >>> wInt e wConst (CVarDesc n p) = let (P s e) = p in wChar 'v' >>> wString n >>> wInt s >>> wInt e wGType :: GType -> Writer wGType GCAF = wChar 'A' wGType GFUN = wChar 'F' wGType GFUN0 = wChar '0' wGType GCON = wChar 'C' wGType GZCON = wChar 'Z' wGType GPRIM = wChar 'P' wGType GEXT = wChar 'X' wExternal :: String -> Pos -> Int -> String -> CallConv -> Writer wExternal name pos arity cName cc = do state <- getWIntState let syms = getSymbolTable state memo = foreignMemo syms forn = toForeign syms memo cc Imported cName arity name cName' = reverse $ takeWhile (/='&') $ reverse cName wForeign name cName' cc forn wForeign :: String -> String -> CallConv -> Foreign -> Writer wForeign name cName cc (Foreign ie proto style mpath _ htok arity args res) = do id <- wIdRef name rest <- wChar 'X' >>> wString cName >>> wUShort arity >>> wCallConv cc style >>> wExternalArg res >>> wMap wExternalArg args let rest' = rest [] len <- wUShort (length rest') return $ id . len . (rest'++) wCallConv :: CallConv -> Style -> Writer wCallConv _ Address = wChar 'a' wCallConv _ FunAddress = wChar 'a' wCallConv C _ = wChar 'c' wCallConv Cast _ = wChar 'x' wCallConv (Other s) _ | s == "primitive" = wChar 'p' | s == "stdcall" = wChar 's' | s == "fastccall" = wChar 'C' | s == "faststdcall" = wChar 'S' | s == "builtin" = wChar 'b' wCallConv e _ = error $ "calling convention "++show e++" is not supported yet" wExternalArg :: Arg -> Writer wExternalArg a = wChar c where c = case a of Int8 -> 'i' Int16 -> 'j' Int32 -> 'k' Int64 -> 'l' Word8 -> 'w' Word16 -> 'x' Word32 -> 'y' Word64 -> 'z' Int -> 'I' Float -> 'F' Double -> 'D' Char -> 'C' Bool -> 'B' Ptr -> 'P' (FunPtr _) -> 'P' StablePtr -> 'p' ForeignPtr -> 'f' Addr -> error "wExternalArg: Addr is no longer supported" ForeignObj -> error "wExternalArg: ForeignObj is no longer supported" PackedString -> 'u' Integer -> 'N' (HaskellFun _) -> 'H' (Unknown _) -> 'u' Unit -> 'U' wReference :: BCDecl -> Writer wReference (Fun name _ _ _ _ _ _ _ _ _) = wIdRef name wReference (Con name _ _ _) = wIdRef name wStringTable :: Map.Map String StringId -> Writer wStringTable st = wUShort (length st') >>> wMap wString st' where -} {- wAll :: Flags -> IntState -> [BCDecl] -> Binary wAll flags state ds = bs [] where ws = WS state Map.empty [0..] Map.empty [0..] (bs,ws') = runState (wProgram flags ds) ws wProgram :: Flags -> [BCDecl] -> Writer wProgram flags ds = do header <- wChar 'H' >>> wChar 'S' >>> wChar 'B' >>> wChar 'C' >>> wUShort (fst bcVersion) >>> wUShort (snd bcVersion) >>> wUShort fl decls <- wMap wDecl ds state <- readState wsState mref <- wModule (sepM $ strIS state $ miIS state) strings <- readState wsStrings extra <- wUShort (length ds) >>> wStringTable strings return $ header . extra . mref . decls where fl = 0 + if sHat flags then 1 else 0 wDecl :: BCDecl -> Writer wDecl (Fun name pos arity args code consts prim stack numDict fl) = do id <- wIdRef name rest <- wChar 'F' >>> wUByte arity >>> wUShort stack >>> wUByte (intFlags fl) >>> wConstTable consts >>> wCode code let rest' = rest [] len <- wUShort (length rest') return $ id . len . (rest'++) wDecl (Con name pos size tag) = do id <- wIdRef name rest <- wChar 'C' >>> wUByte size >>> wUByte tag let rest' = rest [] len <- wUShort (length rest') return $ id . len . (rest'++) wDecl (Prim name pos) = do id <- wIdRef name rest <- wChar 'P' >>> wId name let rest' = rest [] len <- wUShort (length rest') return $ id . len . (rest'++) wDecl (External name pos arity cname cc nt) = wExternal name pos arity cname cc wConstTable :: ConstTable -> Writer wConstTable ct = wUShort (length ct') >>> wMap wConst ct' where ct' = map snd $ sortBy (\(x,_) (y,_) -> compare x y) $ Map.toList ct wConst :: ConstItem -> Writer wConst (CGlobal i y) = wGType y >>> wId i wConst (CInt i) = wChar 'i' >>> wInt i wConst (CInteger i) = wChar 'l' >>> wInteger i wConst (CFloat f) = wChar 'f' >>> wFloat f wConst (CDouble f) = wChar 'd' >>> wDouble f wConst (CString s) = wChar 's' >>> wString s wConst (CPos p) = let (P s e) = p in wChar 'p' >>> wInt s >>> wInt e wConst (CVarDesc n p) = let (P s e) = p in wChar 'v' >>> wString n >>> wInt s >>> wInt e wGType :: GType -> Writer wGType GCAF = wChar 'A' wGType GFUN = wChar 'F' wGType GFUN0 = wChar '0' wGType GCON = wChar 'C' wGType GZCON = wChar 'Z' wGType GPRIM = wChar 'P' wGType GEXT = wChar 'X' wExternal :: String -> Pos -> Int -> String -> CallConv -> Writer wExternal name pos arity cName cc = do state <- getWIntState let syms = getSymbolTable state memo = foreignMemo syms forn = toForeign syms memo cc Imported cName arity name cName' = reverse $ takeWhile (/='&') $ reverse cName wForeign name cName' cc forn wForeign :: String -> String -> CallConv -> Foreign -> Writer wForeign name cName cc (Foreign ie proto style mpath _ htok arity args res) = do id <- wIdRef name rest <- wChar 'X' >>> wString cName >>> wUShort arity >>> wCallConv cc style >>> wExternalArg res >>> wMap wExternalArg args let rest' = rest [] len <- wUShort (length rest') return $ id . len . (rest'++) wCallConv :: CallConv -> Style -> Writer wCallConv _ Address = wChar 'a' wCallConv _ FunAddress = wChar 'a' wCallConv C _ = wChar 'c' wCallConv Cast _ = wChar 'x' wCallConv (Other s) _ | s == "primitive" = wChar 'p' | s == "stdcall" = wChar 's' | s == "fastccall" = wChar 'C' | s == "faststdcall" = wChar 'S' | s == "builtin" = wChar 'b' wCallConv e _ = error $ "calling convention "++show e++" is not supported yet" wExternalArg :: Arg -> Writer wExternalArg a = wChar c where c = case a of Int8 -> 'i' Int16 -> 'j' Int32 -> 'k' Int64 -> 'l' Word8 -> 'w' Word16 -> 'x' Word32 -> 'y' Word64 -> 'z' Int -> 'I' Float -> 'F' Double -> 'D' Char -> 'C' Bool -> 'B' Ptr -> 'P' (FunPtr _) -> 'P' StablePtr -> 'p' ForeignPtr -> 'f' Addr -> error "wExternalArg: Addr is no longer supported" ForeignObj -> error "wExternalArg: ForeignObj is no longer supported" PackedString -> 'u' Integer -> 'N' (HaskellFun _) -> 'H' (Unknown _) -> 'u' Unit -> 'U' wReference :: BCDecl -> Writer wReference (Fun name _ _ _ _ _ _ _ _ _) = wIdRef name wReference (Con name _ _ _) = wIdRef name wStringTable :: Map.Map String StringId -> Writer wStringTable st = wUShort (length st') >>> wMap wString st' where st' = sortListAT st -} {- wModuleTable :: Map.Map Module ModuleId -> Writer wModuleTable mt = wMap wModule (sortListAT mt) -} {- wModule :: String -> Writer wModule m = do is <- mapM addString ms wUByte (length ms) >>> wMap wUShort is where ms = splitModule ';' m [] splitModule :: Char -> String -> String -> [String] splitModule sep [] [] = [] splitModule sep [] acc = [reverse acc] splitModule sep (c:cs) acc | c == sep = (reverse acc) : splitModule sep cs [] | otherwise = splitModule sep cs (c:acc) wCode :: Code -> Writer wCode (CWrites ws) = do is <- wMap wWrite ws let is' = is [] len <- wUShort (length is') return $ len . (is'++) wWrite :: Write -> Writer wWrite (WUByte n) = wUByte n wWrite (WUShort n) = wUShort n wWrite (WByte n) = wByte n wWrite (WShort n) = wShort n wIdRef :: String -> Writer wIdRef i = do state <- readState wsState let tid = tidIS state i (md,id) = splitM tid wModule id wStringRef :: String -> Writer wStringRef s = do i <- addString s wUShort i wId :: Id -> Writer wId i = do state <- readState wsState let tid = tidIS state i (md,id) = splitM tid md' = if md == "" then sepM $ strIS state $ miIS state else md wModule md' >>> wModule id -------------------------------------------------------------------------------------------------------- addString :: String -> State WState Int addString x = writeState (\s -> case Map.lookup x (wsStrings s) of Just i -> (s, i) _ -> let (i:is) = wsFreeS s s' = s { wsFreeS = is, wsStrings = Map.insert x i (wsStrings s) } in (s', i) ) addModule :: [Int] -> State WState Int addModule x = writeState (\s -> case Map.lookup x (wsModules s) of Just i -> (s, i) _ -> let (i:is) = wsFreeM s s' = s { wsFreeM = is, wsModules = Map.insert x i (wsModules s) } in (s', i) ) sortListAT :: Ord v => Map.Map k v -> [k] sortListAT st = map fst $ sortBy (\(_,x) (_,y) -> compare x y) $ Map.toList st wMap :: (a -> Writer) -> [a] -> Writer wMap f [] = wNil wMap f (x:xs) = f x >>> wMap f xs (>>>) :: Writer -> Writer -> Writer x >>> y = do a <- x b <- y return $ a . b wChar :: Char -> Writer wChar c = return (c:) wNil :: Writer wNil = return id wByte :: Int -> Writer wByte i = if isByte i then let i' = (i + 256) `mod` 256 in wChar (chr i') else error $ "wByte of "++show i wUByte :: Int -> Writer wUByte i = if isUByte i then wChar $ chr i else error $ "wUByte of "++show i wIntGeneric :: Integer -> Int -> Writer wIntGeneric i 0 | i == 0 || i == (-1) = wNil | otherwise = error $ "wIntGeneric "++show i ++" 0" wIntGeneric i n = wIntGeneric hi (n-1) >>> wUByte (fromInteger lo) where (hi,lo) = i `divMod` 256 wShort :: Int -> Writer wShort i = if isShort i then wIntGeneric (toInteger i) 2 else error $ "wShort of "++show i wUShort :: Int -> Writer wUShort i = if isUShort i then wIntGeneric (toInteger i) 2 else error $ "wUShort of "++show i wInt :: Int -> Writer wInt i = if isInt i then wIntGeneric (toInteger i) 4 else error $ "wInt of "++show i wInteger :: Integer -> Writer wInteger i | i == 0 = wByte 0 | i > 0 = do bs <- wInteger' i let len = length $ bs [] wByte len >>> return bs | i < 0 = do bs <- wInteger' (-i) let len = length $ bs [] wByte (-len) >>> return bs where wInteger' :: Integer -> Writer wInteger' 0 = wNil wInteger' i = wInteger' hi >>> wUByte (fromInteger lo) where (hi,lo) = i `divMod` 256 wString :: String -> Writer wString s = wUShort (length s) >>> wMap wChar s wFloat :: Float -> Writer wFloat f = wInteger mant >>> wShort exp where (mant,exp) = decodeFloat f wDouble :: Double -> Writer wDouble f = wInteger mant >>> wShort exp where (mant,exp) = decodeFloat f getWIntState :: State WState IntState getWIntState = get >>= return . wsState -}