Created
December 20, 2018 08:16
-
-
Save iamahuman/06b7d7142bc5f2364abea010f380af94 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Main where | |
import Control.Applicative | |
import Control.Exception | |
import Data.List | |
import Data.Char | |
import qualified Data.Map as Map | |
import System.IO | |
import System.IO.Error | |
import System.Environment | |
import System.Process | |
data Typ = | |
V | Opaque | I1 | I8 | I16 | I32 | I64 | F32 | F64 | Ref String | | |
Arr { arrCount :: Word, arrElemTyp :: Typ } | | |
Stru { struMembers :: [Typ] } | PStru { struMembers :: [Typ] } | | |
Ptr Typ | Fun { retTyp :: Typ, argTyp :: [Typ], isVarArg :: Bool } | |
deriving (Read, Show, Eq) | |
data SzAl = SzAl { saSize :: Word, saAlign :: Word } | |
deriving (Read, Show, Eq, Ord) | |
isIdChar :: Char -> Bool | |
isIdChar = (`elem` "._"++['0'..'9']++['a'..'z']++['A'..'Z']) | |
stE :: String -> String | |
stE = dropWhile isSpace | |
literal :: String -> String -> [((), String)] | |
literal p i = | |
case stripPrefix p i of | |
Just x -> return ((), stE x) | |
Nothing -> fail "literal match fail" | |
ident :: String -> [(String, String)] | |
ident (x:xs) | |
| isIdChar x = | |
let (v, r) = span isIdChar xs | |
in return ((x:v), stE r) | |
| otherwise = fail "ident is empty" | |
ident [] = fail "unexpected EOF for ident" | |
typeAtom :: String -> [(Typ, String)] | |
typeAtom ('o':'p':'a':'q':'u':'e':xs) = return (Opaque, stE xs) | |
typeAtom ('v':'o':'i':'d':xs) = return (V, stE xs) | |
typeAtom ('i':'8':xs) = return (I8, stE xs) | |
typeAtom ('i':'1':'6':xs) = return (I16, stE xs) | |
typeAtom ('i':'3':'2':xs) = return (I32, stE xs) | |
typeAtom ('i':'6':'4':xs) = return (I64, stE xs) | |
typeAtom ('f':'l':'o':'a':'t':xs) = return (F32, stE xs) | |
typeAtom ('d':'o':'u':'b':'l':'e':xs) = return (F64, stE xs) | |
typeAtom ('i':'1':xs) = return (I1, stE xs) | |
typeAtom ('%':xs) = do | |
(n, r) <- ident (stE xs) | |
return (Ref n, r) | |
typeAtom ('(':xs) = do | |
(v, r) <- typeExpr (stE xs) | |
(_, r) <- literal ")" r | |
return (v, r) | |
typeAtom ('[':xs) = do | |
(c, r) <- reads (stE xs) | |
(_, r) <- literal "x" (stE r) | |
(v, r) <- typeExpr r | |
(_, r) <- literal "]" r | |
return (Arr c v, r) | |
typeAtom ('<':xs) = do | |
(_, r) <- literal "{" (stE xs) | |
let m0 ('}':xs) = mf (stE xs) id | |
m0 r = m r id | |
mf r vs = do | |
(_, r) <- literal ">" r | |
let vf = vs [] | |
vf `seq` return (PStru vf, r) | |
m r vs = do | |
(v, r) <- typeExpr r | |
let vs' = vs . (v:) | |
case r of | |
(',':xs) -> m (stE xs) vs' | |
('}':xs) -> mf (stE xs) vs' | |
_ -> fail "malformed packed product type" | |
m0 r | |
typeAtom ('{':xs) = m0 (stE xs) where | |
m0 ('}':xs) = return (Stru [], stE xs) | |
m0 r = m r id | |
m r vs = do | |
(v, r) <- typeExpr r | |
let vs' = vs . (v:) | |
case r of | |
(',':xs) -> m (stE xs) vs' | |
('}':xs) -> | |
let vf = vs' [] | |
in vf `seq` return (Stru vf, stE xs) | |
_ -> fail "malformed product type" | |
typeAtom _ = fail "not a type" | |
typeExpr :: String -> [(Typ, String)] | |
typeExpr inp = do | |
(v, r) <- typeAtom inp | |
let m k ('*':xs) = m (Ptr k) (stE xs) | |
m k ('(':xs) = w (stE xs) id where | |
fin r vs b = | |
let vf = vs [] | |
in vf `seq` m (Fun k vf b) (stE r) | |
fin0 r vs = fin r vs False | |
finV r vs = do | |
(_, r) <- literal ")" r | |
fin r vs True | |
nxt ('.':'.':'.':xs) vs = finV (stE xs) vs | |
nxt r vs = do | |
(v, r) <- typeExpr r | |
let vs' = vs . (v:) | |
case r of | |
(',':xs) -> nxt (stE xs) vs' | |
(')':xs) -> fin0 (stE xs) vs' | |
_ -> fail "expected ',' or ')'" | |
w ('.':'.':'.':xs) vs = finV (stE xs) vs | |
w (')':xs) vs = fin0 (stE xs) vs | |
w r vs = nxt r vs | |
m k r = return (k, r) | |
m v r | |
unref :: Applicative f => (String -> f Typ) -> Typ -> f Typ | |
unref f (Ref x) = f x | |
unref f (Arr c e) = Arr c <$> unref f e | |
unref f (Stru x) = Stru <$> traverse (unref f) x | |
unref f (PStru x) = PStru <$> traverse (unref f) x | |
unref f (Ptr t) = Ptr <$> unref f t | |
unref f (Fun rt ats b) = liftA2 (\x y -> Fun x y b) (unref f rt) (traverse (unref f) ats) | |
unref _ t = pure t | |
typeDecl :: String -> [(String, Typ, String)] | |
typeDecl ('%':xs) = do | |
(n, r) <- ident xs | |
(_, r) <- literal "=" r | |
(_, r) <- literal "type" r | |
(t, r) <- typeExpr r | |
return (n, t, r) | |
typeDecl _ = fail "not a type declaration" | |
varDecl :: String -> [(String, Typ, String)] | |
varDecl ('@':xs) = do | |
(n, r) <- ident xs | |
(_, r) <- literal "=" r | |
let m r@(_:_) = m0 r <|> m (stK r) | |
m _ = fail "unexpected EOF" | |
stK = stE . dropWhile (not . isSpace) | |
m0 = fmap (\(t, r) -> (n, t, r)) . typeExpr | |
m (stK r) | |
varDecl _ = fail "not a var declaration" | |
parseType :: String -> Either String Typ | |
parseType inp = | |
case [ x | (x, "") <- typeExpr (stE inp) ] of | |
[x] -> Right x | |
[] -> Left "parseType: no parse" | |
_ -> Left "parseType: ambiguous parse" | |
sizeOf :: SzAl -> Typ -> SzAl | |
sizeOf _ V = SzAl 1 1 | |
sizeOf _ I1 = SzAl 1 1 | |
sizeOf _ I8 = SzAl 1 1 | |
sizeOf _ I16 = SzAl 2 2 | |
sizeOf _ I32 = SzAl 4 4 | |
sizeOf _ I64 = SzAl 8 8 | |
sizeOf _ F32 = SzAl 4 4 | |
sizeOf _ F64 = SzAl 8 8 | |
sizeOf p (Arr c e) = SzAl (c * s) a where SzAl s a = sizeOf p e | |
sizeOf p (Stru m) = | |
let sa = map (sizeOf p) m | |
f (SzAl s0 a0) (SzAl s a) = | |
let k = (s0 + a - 1) `quot` a | |
in SzAl (a * k + s) (lcm a0 a) | |
SzAl s' a' = foldl' f (SzAl 0 1) sa | |
in f (SzAl s' 1) (SzAl 0 a') | |
sizeOf p (PStru m) = SzAl s 1 where | |
s = foldr ((+) . saSize . sizeOf p) 0 m | |
sizeOf p (Ptr _) = p | |
sizeOf _ _ = SzAl 0 1 | |
sizeOf32 :: Typ -> SzAl | |
sizeOf32 = sizeOf (SzAl 4 4) | |
sizeOf64 :: Typ -> SzAl | |
sizeOf64 = sizeOf (SzAl 8 8) | |
type TypMap = Map.Map String Typ | |
data Action = NewVar String Typ | UpdateReg String Typ | Failure String | Nop | |
deriving (Read, Show, Eq) | |
procLine :: String -> Action | |
procLine r = | |
case r of | |
'%':_ -> | |
case typeDecl r of | |
(n, t, _):_ -> UpdateReg n t | |
_ -> Failure "unknown type decl" | |
'@':_ -> | |
case varDecl r of | |
(n, t, _):_ -> NewVar n t | |
_ -> Failure "unknown var decl" | |
_ -> Nop | |
procLLFile :: String -> Handle -> IO () | |
procLLFile fn fh = procIter (1 :: Word) Map.empty `catch` handleIt where | |
sizeOf' = saSize . sizeOf32 | |
fail' = Left | |
resolve tm hist n | |
| n `elem` hist = | |
fail' $ "Cycle! " ++ show hist | |
| otherwise = | |
case Map.lookup n tm of | |
Just (Ref x) -> resolve tm (n:hist) x | |
Just t -> return t | |
_ -> fail' $ "Unknown type " ++ n | |
handleIt :: IOError -> IO () | |
handleIt e | |
| isEOFError e = return () | |
| otherwise = throwIO e | |
rptRoot = hPutStr stderr . ((fn ++ ": ") ++) | |
procIter lno tm = do | |
line <- hGetLine fh | |
let report str = | |
rptRoot $ shows lno $ ": " ++ str ++ ": " ++ line ++ "\n" | |
case procLine line of | |
NewVar _n t -> do | |
case unref (resolve tm []) t of | |
Left e -> report e | |
Right t' -> putStrLn v where | |
v = shows (sizeOf' t') (' ':fn ++ (':':line)) | |
procIter (lno+1) tm | |
UpdateReg n t -> procIter (lno+1) (Map.insert n t tm) | |
Failure e -> report e *> procIter (lno+1) tm | |
Nop -> procIter (lno+1) tm | |
procFile :: String -> Handle -> IO () | |
procFile fn fh = | |
withCreateProcess (proc "llvm-dis" ["-o=-"]) { | |
std_in = UseHandle fh, | |
std_out = CreatePipe | |
} $ \_ ofhm _ _ -> do | |
let Just ofh = ofhm | |
procLLFile fn ofh | |
main :: IO () | |
main = getArgs >>= \args -> | |
case args of | |
[] -> procFile "-" stdin | |
_ -> mapM_ (\fn -> withFile fn ReadMode (procFile fn)) args |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment