{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE PatternGuards #-} module Text.LLVM.AST where import Control.Applicative ((<$)) import Control.Monad (MonadPlus(mzero),(<=<),msum,guard,liftM,liftM3) import Data.Char (isAscii,isPrint,ord,toUpper) import Data.Foldable (Foldable(foldMap)) import Data.Int (Int32) import Data.List (intersperse,genericIndex,genericLength,unfoldr) import Data.Maybe (fromMaybe) import Data.Monoid (Monoid(..)) import Data.String (IsString(fromString)) import Data.Traversable (Traversable(sequenceA)) import Numeric (showHex) import Text.PrettyPrint.HughesPJ commas :: [Doc] -> Doc commas = hsep . punctuate (char ',') colons :: [Doc] -> Doc colons = hcat . intersperse (char ':') breaks :: (a -> Bool) -> [a] -> [[a]] breaks p = unfoldr step where step [] = Nothing step xs = case break p xs of (as,_:bs) -> Just (as,bs) (as, []) -> Just (as,[]) uncons :: MonadPlus m => [a] -> m (a,[a]) uncons (a:as) = return (a,as) uncons _ = mzero int32 :: Int32 -> Doc int32 = integer . fromIntegral angles :: Doc -> Doc angles d = char '<' <> d <> char '>' structBraces :: Doc -> Doc structBraces body = char '{' <+> body <+> char '}' ppMaybe :: (a -> Doc) -> Maybe a -> Doc ppMaybe = maybe empty opt :: Bool -> Doc -> Doc opt True d = d opt False _ = empty -- Modules --------------------------------------------------------------------- data Module = Module { modDataLayout :: DataLayout , modTypes :: [TypeDecl] , modNamedMd :: [NamedMd] , modUnnamedMd :: [UnnamedMd] , modGlobals :: [Global] , modDeclares :: [Declare] , modDefines :: [Define] , modInlineAsm :: InlineAsm , modAliases :: [GlobalAlias] } deriving (Show) instance Monoid Module where mempty = emptyModule mappend m1 m2 = Module { modDataLayout = modDataLayout m1 `mappend` modDataLayout m2 , modTypes = modTypes m1 `mappend` modTypes m2 , modUnnamedMd = modUnnamedMd m1 `mappend` modUnnamedMd m2 , modNamedMd = modNamedMd m1 `mappend` modNamedMd m2 , modGlobals = modGlobals m1 `mappend` modGlobals m2 , modDeclares = modDeclares m1 `mappend` modDeclares m2 , modDefines = modDefines m1 `mappend` modDefines m2 , modInlineAsm = modInlineAsm m1 `mappend` modInlineAsm m2 , modAliases = modAliases m1 `mappend` modAliases m2 } emptyModule :: Module emptyModule = Module { modDataLayout = mempty , modTypes = mempty , modNamedMd = mempty , modUnnamedMd = mempty , modGlobals = mempty , modDeclares = mempty , modDefines = mempty , modInlineAsm = mempty , modAliases = mempty } ppModule :: Module -> Doc ppModule m = foldr ($+$) empty $ ppDataLayout (modDataLayout m) : ppInlineAsm (modInlineAsm m) : concat [ map ppTypeDecl (modTypes m) , map ppGlobal (modGlobals m) , map ppGlobalAlias (modAliases m) , map ppDeclare (modDeclares m) , map ppDefine (modDefines m) , map ppNamedMd (modNamedMd m) , map ppUnnamedMd (modUnnamedMd m) ] -- Named Metadata -------------------------------------------------------------- data NamedMd = NamedMd { nmName :: String , nmValues :: [Int] } deriving (Show) ppNamedMd :: NamedMd -> Doc ppNamedMd nm = ppMetadata (text (nmName nm)) <+> char '=' <+> ppMetadata (braces (commas (map (ppMetadata . int) (nmValues nm)))) -- Unnamed Metadata ------------------------------------------------------------ data UnnamedMd = UnnamedMd { umIndex :: !Int , umValues :: [Typed Value] } deriving (Show) ppUnnamedMd :: UnnamedMd -> Doc ppUnnamedMd um = ppMetadata (int (umIndex um)) <+> char '=' <+> text "metadata" <+> ppMetadataNode (umValues um) -- Aliases --------------------------------------------------------------------- data GlobalAlias = GlobalAlias { aliasName :: Symbol , aliasType :: Type , aliasTarget :: Value } deriving (Show) ppGlobalAlias :: GlobalAlias -> Doc ppGlobalAlias g = ppSymbol (aliasName g) <+> char '=' <+> body where val = aliasTarget g body = case val of ValSymbol _sym -> ppType (aliasType g) <+> ppValue val _ -> ppValue val -- Data Layout ----------------------------------------------------------------- type DataLayout = [LayoutSpec] -- | Pretty print a data layout specification. ppDataLayout :: DataLayout -> Doc ppDataLayout [] = empty ppDataLayout ls = text "target" <+> text "datalayout" <+> char '=' <+> doubleQuotes (hcat (intersperse (char '-') (map ppLayoutSpec ls))) data LayoutSpec = BigEndian | LittleEndian | PointerSize !Int !Int (Maybe Int) | IntegerSize !Int !Int (Maybe Int) | VectorSize !Int !Int (Maybe Int) | FloatSize !Int !Int (Maybe Int) | AggregateSize !Int !Int (Maybe Int) | StackObjSize !Int !Int (Maybe Int) | NativeIntSize [Int] | StackAlign !Int | Mangling Mangling deriving (Show) data Mangling = ElfMangling | MipsMangling | MachOMangling | WindowsCoffMangling deriving (Show,Eq) -- | Pretty print a single layout specification. ppLayoutSpec :: LayoutSpec -> Doc ppLayoutSpec BigEndian = char 'E' ppLayoutSpec LittleEndian = char 'e' ppLayoutSpec (PointerSize sz abi pref) = text "p:" <> ppLayoutBody sz abi pref ppLayoutSpec (IntegerSize sz abi pref) = char 'i' <> ppLayoutBody sz abi pref ppLayoutSpec (VectorSize sz abi pref) = char 'v' <> ppLayoutBody sz abi pref ppLayoutSpec (FloatSize sz abi pref) = char 'f' <> ppLayoutBody sz abi pref ppLayoutSpec (AggregateSize sz abi pref) = char 'a' <> ppLayoutBody sz abi pref ppLayoutSpec (StackObjSize sz abi pref) = char 's' <> ppLayoutBody sz abi pref ppLayoutSpec (NativeIntSize szs) = char 'n' <> colons (map int szs) ppLayoutSpec (StackAlign a) = char 'S' <> int a ppLayoutSpec (Mangling m) = char 'm' <> char ':' <> ppMangling m -- | Pretty-print the common case for data layout specifications. ppLayoutBody :: Int -> Int -> Maybe Int -> Doc ppLayoutBody size abi mb = int size <> char ':' <> int abi <> pref where pref = case mb of Nothing -> empty Just p -> char ':' <> int p ppMangling :: Mangling -> Doc ppMangling ElfMangling = char 'e' ppMangling MipsMangling = char 'm' ppMangling MachOMangling = char 'o' ppMangling WindowsCoffMangling = char 'w' -- | Parse the data layout string. parseDataLayout :: MonadPlus m => String -> m DataLayout parseDataLayout = mapM parseLayoutSpec . breaks (== '-') -- | Parse a single layout specification from a string. parseLayoutSpec :: MonadPlus m => String -> m LayoutSpec parseLayoutSpec str = msum [ guard (str == "E") >> return BigEndian , guard (str == "e") >> return LittleEndian , do (i,rest) <- uncons str let body = breaks (== ':') rest case i of 'S' -> do align <- parseInt rest return (StackAlign align) 'p' -> build PointerSize (tail body) 'i' -> build IntegerSize body 'v' -> build VectorSize body 'f' -> build FloatSize body 'a' -> build AggregateSize body 's' -> build StackObjSize body 'n' -> do ints <- mapM parseInt body return (NativeIntSize ints) 'm' -> case tail body of ["e"] -> return (Mangling ElfMangling) ["m"] -> return (Mangling MipsMangling) ["o"] -> return (Mangling MachOMangling) ["w"] -> return (Mangling WindowsCoffMangling) _ -> mzero _ -> mzero ] where build f lst = case lst of [sz,abi,pref] -> liftM3 f (parseInt sz) (parseInt abi) (parsePref pref) [sz,abi] -> liftM3 f (parseInt sz) (parseInt abi) (return Nothing) _ -> mzero parsePref = liftM Just . parseInt parseInt s = case reads s of [(i,[])] -> return i _ -> mzero -- Inline Assembly ------------------------------------------------------------- type InlineAsm = [String] -- | Pretty-print the inline assembly block. ppInlineAsm :: InlineAsm -> Doc ppInlineAsm = foldr ($+$) empty . map ppLine where ppLine l = text "module asm" <+> doubleQuotes (text l) -- Identifiers ----------------------------------------------------------------- newtype Ident = Ident String deriving (Show,Eq,Ord) instance IsString Ident where fromString = Ident ppIdent :: Ident -> Doc ppIdent (Ident n) = char '%' <> text n -- Symbols --------------------------------------------------------------------- newtype Symbol = Symbol String deriving (Show,Eq,Ord) instance IsString Symbol where fromString = Symbol ppSymbol :: Symbol -> Doc ppSymbol (Symbol n) = char '@' <> text n -- Types ----------------------------------------------------------------------- data PrimType = Label | Void | Integer Int32 | FloatType FloatType | X86mmx | Metadata deriving (Eq, Ord, Show) ppPrimType :: PrimType -> Doc ppPrimType Label = text "label" ppPrimType Void = text "void" ppPrimType (Integer i) = char 'i' <> integer (toInteger i) ppPrimType (FloatType ft) = ppFloatType ft ppPrimType X86mmx = text "x86mmx" ppPrimType Metadata = text "metadata" data FloatType = Float | Double | Fp128 | X86_fp80 | PPC_fp128 deriving (Eq, Ord, Show) ppFloatType :: FloatType -> Doc ppFloatType Float = text "float" ppFloatType Double = text "double" ppFloatType Fp128 = text "fp128" ppFloatType X86_fp80 = text "x86_fp80" ppFloatType PPC_fp128 = text "ppc_fp128" type Type = Type' Ident data Type' ident = PrimType PrimType | Alias ident | Array Int32 (Type' ident) | FunTy (Type' ident) [Type' ident] Bool | PtrTo (Type' ident) | Struct [Type' ident] | PackedStruct [Type' ident] | Vector Int32 (Type' ident) | Opaque deriving (Eq, Ord, Show, Functor) -- | Traverse a type, updating or removing aliases. updateAliases :: (a -> Type' b) -> (Type' a -> Type' b) updateAliases f = loop where loop ty = case ty of Array len ety -> Array len (loop ety) FunTy res ps var -> FunTy (loop res) (map loop ps) var PtrTo pty -> PtrTo (loop pty) Struct fs -> Struct (map loop fs) PackedStruct fs -> PackedStruct (map loop fs) Alias lab -> f lab PrimType pty -> PrimType pty Vector len ety -> Vector len (loop ety) Opaque -> Opaque ppType :: Type -> Doc ppType (PrimType pt) = ppPrimType pt ppType (Alias i) = ppIdent i ppType (Array len ty) = brackets (int32 len <+> char 'x' <+> ppType ty) ppType (PtrTo ty) = ppType ty <> char '*' ppType (Struct ts) = structBraces (commas (map ppType ts)) ppType (PackedStruct ts) = angles (structBraces (commas (map ppType ts))) ppType (FunTy r as va) = ppType r <> ppArgList va (map ppType as) ppType (Vector len pt) = angles (int32 len <+> char 'x' <+> ppType pt) ppType Opaque = text "opaque" isFloatingPoint :: PrimType -> Bool isFloatingPoint (FloatType _) = True isFloatingPoint _ = False isAlias :: Type -> Bool isAlias Alias{} = True isAlias _ = False isPrimTypeOf :: (PrimType -> Bool) -> Type -> Bool isPrimTypeOf p (PrimType pt) = p pt isPrimTypeOf _ _ = False isLabel :: PrimType -> Bool isLabel Label = True isLabel _ = False isInteger :: PrimType -> Bool isInteger Integer{} = True isInteger _ = False isVector :: Type -> Bool isVector Vector{} = True isVector _ = False isVectorOf :: (Type -> Bool) -> Type -> Bool isVectorOf p (Vector _ e) = p e isVectorOf _ _ = False isArray :: Type -> Bool isArray ty = case ty of Array _ _ -> True _ -> False isPointer :: Type -> Bool isPointer (PtrTo _) = True isPointer _ = False -- | Build a variable-argument argument list. ppArgList :: Bool -> [Doc] -> Doc ppArgList True ds = parens (commas (ds ++ [text "..."])) ppArgList False ds = parens (commas ds) -- Null Values ----------------------------------------------------------------- data NullResult lab = HasNull (Value' lab) | ResolveNull Ident primTypeNull :: PrimType -> Value' lab primTypeNull (Integer 1) = ValBool False primTypeNull (Integer _) = ValInteger 0 primTypeNull (FloatType ft) = floatTypeNull ft primTypeNull _ = ValZeroInit floatTypeNull :: FloatType -> Value' lab floatTypeNull Float = ValFloat 0 floatTypeNull _ = ValDouble 0 -- XXX not sure about this typeNull :: Type -> NullResult lab typeNull (PrimType pt) = HasNull (primTypeNull pt) typeNull PtrTo{} = HasNull ValNull typeNull (Alias i) = ResolveNull i typeNull _ = HasNull ValZeroInit -- Type Elimination ------------------------------------------------------------ elimFunTy :: MonadPlus m => Type -> m (Type,[Type],Bool) elimFunTy (FunTy ret args va) = return (ret,args,va) elimFunTy _ = mzero elimAlias :: MonadPlus m => Type -> m Ident elimAlias (Alias i) = return i elimAlias _ = mzero elimPtrTo :: MonadPlus m => Type -> m Type elimPtrTo (PtrTo ty) = return ty elimPtrTo _ = mzero elimVector :: MonadPlus m => Type -> m (Int32,Type) elimVector (Vector n pty) = return (n,pty) elimVector _ = mzero elimArray :: MonadPlus m => Type -> m (Int32, Type) elimArray (Array n ety) = return (n, ety) elimArray _ = mzero elimFunPtr :: MonadPlus m => Type -> m (Type,[Type],Bool) elimFunPtr = elimFunTy <=< elimPtrTo elimPrimType :: MonadPlus m => Type -> m PrimType elimPrimType (PrimType pt) = return pt elimPrimType _ = mzero elimFloatType :: MonadPlus m => PrimType -> m FloatType elimFloatType (FloatType ft) = return ft elimFloatType _ = mzero -- | Eliminator for array, pointer and vector types. elimSequentialType :: MonadPlus m => Type -> m Type elimSequentialType ty = case ty of Array _ elTy -> return elTy PtrTo elTy -> return elTy Vector _ pty -> return pty _ -> mzero -- Top-level Type Aliases ------------------------------------------------------ data TypeDecl = TypeDecl { typeName :: Ident , typeValue :: Type } deriving (Show) ppTypeDecl :: TypeDecl -> Doc ppTypeDecl td = ppIdent (typeName td) <+> char '=' <+> text "type" <+> ppType (typeValue td) -- Globals --------------------------------------------------------------------- data Global = Global { globalSym :: Symbol , globalAttrs :: GlobalAttrs , globalType :: Type , globalValue :: Value , globalAlign :: Maybe Align } deriving Show ppGlobal :: Global -> Doc ppGlobal g = ppSymbol (globalSym g) <+> char '=' <+> ppGlobalAttrs (globalAttrs g) <+> ppType (globalType g) <+> ppValue (globalValue g) <> ppAlign (globalAlign g) addGlobal :: Global -> Module -> Module addGlobal g m = m { modGlobals = g : modGlobals m } data GlobalAttrs = GlobalAttrs { gaLinkage :: Maybe Linkage , gaConstant :: Bool } deriving (Show) ppGlobalAttrs :: GlobalAttrs -> Doc ppGlobalAttrs ga = ppMaybe ppLinkage (gaLinkage ga) <+> constant where constant | gaConstant ga = text "constant" | otherwise = text "global" -- Declarations ---------------------------------------------------------------- data Declare = Declare { decRetType :: Type , decName :: Symbol , decArgs :: [Type] , decVarArgs :: Bool } deriving (Show) ppDeclare :: Declare -> Doc ppDeclare d = text "declare" <+> ppType (decRetType d) <+> ppSymbol (decName d) <> ppArgList (decVarArgs d) (map ppType (decArgs d)) -- Function Definitions -------------------------------------------------------- data Define = Define { defAttrs :: FunAttrs , defRetType :: Type , defName :: Symbol , defArgs :: [Typed Ident] , defVarArgs :: Bool , defBody :: [BasicBlock] } deriving (Show) ppDefine :: Define -> Doc ppDefine d = text "define" <+> ppMaybe ppLinkage (funLinkage (defAttrs d)) <+> ppType (defRetType d) <+> ppSymbol (defName d) <> ppArgList (defVarArgs d) (map (ppTyped ppIdent) (defArgs d)) <+> ppMaybe (\gc -> text "gc" <+> ppGC gc) (funGC (defAttrs d)) <+> char '{' $+$ vcat (map ppBasicBlock (defBody d)) $+$ char '}' addDefine :: Define -> Module -> Module addDefine d m = m { modDefines = d : modDefines m } data FunAttrs = FunAttrs { funLinkage :: Maybe Linkage , funGC :: Maybe GC } deriving (Show) emptyFunAttrs :: FunAttrs emptyFunAttrs = FunAttrs { funLinkage = Nothing , funGC = Nothing } -- Basic Block Labels ---------------------------------------------------------- data BlockLabel = Named Ident | Anon Int deriving (Eq,Ord,Show) instance IsString BlockLabel where fromString str = Named (fromString str) ppLabelDef :: BlockLabel -> Doc ppLabelDef (Named (Ident l)) = text l <> char ':' ppLabelDef (Anon i) = char ';' <+> text "