{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Rank2Types #-} -- | -- Module : Text.LLVM.PP -- Copyright : Trevor Elliott 2011-2016 -- License : BSD3 -- -- Maintainer : awesomelyawesome@gmail.com -- Stability : experimental -- Portability : unknown -- -- This is the pretty-printer for llvm assembly versions 3.6 and lower. -- module Text.LLVM.PP where import Text.LLVM.AST import Control.Applicative ((<|>)) import Data.Bits ( shiftR ) import Data.Char (isAlphaNum,isAscii,isDigit,isPrint,ord,toUpper) import Data.List (intersperse) import qualified Data.Map as Map import Data.Maybe (catMaybes,fromMaybe,isJust) import Numeric (showHex) import Text.PrettyPrint.HughesPJ import Data.Int import Prelude hiding ((<>)) -- Pretty-printer Config ------------------------------------------------------- type LLVM = ?config :: Config -- | The differences between various versions of the llvm textual AST. data Config = Config { cfgLoadImplicitType :: Bool -- ^ True when the type of the result of a load is -- derived from its pointer argument, or supplied -- implicitly. , cfgGEPImplicitType :: Bool -- ^ True when the type of the result of the GEP -- instruction is implied. , cfgUseDILocation :: Bool } withConfig :: Config -> (LLVM => a) -> a withConfig cfg body = let ?config = cfg in body ppLLVM, ppLLVM35, ppLLVM36, ppLLVM37, ppLLVM38 :: (LLVM => a) -> a ppLLVM = ppLLVM38 ppLLVM35 = ppLLVM36 ppLLVM36 = withConfig Config { cfgLoadImplicitType = True , cfgGEPImplicitType = True , cfgUseDILocation = False } ppLLVM37 = withConfig Config { cfgLoadImplicitType = False , cfgGEPImplicitType = False , cfgUseDILocation = True } ppLLVM38 = withConfig Config { cfgLoadImplicitType = False , cfgGEPImplicitType = False , cfgUseDILocation = True } checkConfig :: LLVM => (Config -> Bool) -> Bool checkConfig p = p ?config -- Modules --------------------------------------------------------------------- ppModule :: LLVM => Module -> Doc ppModule m = foldr ($+$) empty $ ppSourceName (modSourceName m) : 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) , map ppComdat (Map.toList (modComdat m)) ] -- Source filename ------------------------------------------------------------- ppSourceName :: Maybe String -> Doc ppSourceName Nothing = empty ppSourceName (Just sn) = "source_filename" <+> char '=' <+> doubleQuotes (text sn) -- Metadata -------------------------------------------------------------------- ppNamedMd :: NamedMd -> Doc ppNamedMd nm = sep [ ppMetadata (text (nmName nm)) <+> char '=' , ppMetadata (braces (commas (map (ppMetadata . int) (nmValues nm)))) ] ppUnnamedMd :: LLVM => UnnamedMd -> Doc ppUnnamedMd um = sep [ ppMetadata (int (umIndex um)) <+> char '=' , distinct <+> ppValMd (umValues um) ] where distinct | umDistinct um = "distinct" | otherwise = empty -- Aliases --------------------------------------------------------------------- ppGlobalAlias :: LLVM => 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 ----------------------------------------------------------------- -- | Pretty print a data layout specification. ppDataLayout :: DataLayout -> Doc ppDataLayout [] = empty ppDataLayout ls = "target" <+> "datalayout" <+> char '=' <+> doubleQuotes (hcat (intersperse (char '-') (map ppLayoutSpec ls))) -- | Pretty print a single layout specification. ppLayoutSpec :: LayoutSpec -> Doc ppLayoutSpec ls = case ls of BigEndian -> char 'E' LittleEndian -> char 'e' PointerSize 0 sz abi pref -> char 'p' <> char ':' <> ppLayoutBody sz abi pref PointerSize n sz abi pref -> char 'p' <> int n <> char ':' <> ppLayoutBody sz abi pref IntegerSize sz abi pref -> char 'i' <> ppLayoutBody sz abi pref VectorSize sz abi pref -> char 'v' <> ppLayoutBody sz abi pref FloatSize sz abi pref -> char 'f' <> ppLayoutBody sz abi pref StackObjSize sz abi pref -> char 's' <> ppLayoutBody sz abi pref AggregateSize sz abi pref -> char 'a' <> ppLayoutBody sz abi pref NativeIntSize szs -> char 'n' <> hcat (punctuate (char ':') (map int szs)) StackAlign a -> char 'S' <> int a 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' -- Inline Assembly ------------------------------------------------------------- -- | Pretty-print the inline assembly block. ppInlineAsm :: InlineAsm -> Doc ppInlineAsm = foldr ($+$) empty . map ppLine where ppLine l = "module asm" <+> doubleQuotes (text l) -- Identifiers ----------------------------------------------------------------- ppIdent :: Ident -> Doc ppIdent (Ident n) | validIdentifier n = char '%' <> text n | otherwise = char '%' <> ppStringLiteral n -- | According to the LLVM Language Reference Manual, the regular -- expression for LLVM identifiers is "[-a-zA-Z$._][-a-zA-Z$._0-9]". -- Identifiers may also be strings of one or more decimal digits. validIdentifier :: String -> Bool validIdentifier [] = False validIdentifier s@(c0 : cs) | isDigit c0 = all isDigit cs | otherwise = all isIdentChar s where isIdentChar :: Char -> Bool isIdentChar c = isAlphaNum c || c `elem` ("-$._" :: [Char]) -- Symbols --------------------------------------------------------------------- ppSymbol :: Symbol -> Doc ppSymbol (Symbol n) | validIdentifier n = char '@' <> text n | otherwise = char '@' <> ppStringLiteral n -- Types ----------------------------------------------------------------------- ppPrimType :: PrimType -> Doc ppPrimType Label = "label" ppPrimType Void = "void" ppPrimType (Integer i) = char 'i' <> integer (toInteger i) ppPrimType (FloatType ft) = ppFloatType ft ppPrimType X86mmx = "x86mmx" ppPrimType Metadata = "metadata" ppFloatType :: FloatType -> Doc ppFloatType Half = "half" ppFloatType Float = "float" ppFloatType Double = "double" ppFloatType Fp128 = "fp128" ppFloatType X86_fp80 = "x86_fp80" ppFloatType PPC_fp128 = "ppc_fp128" ppType :: Type -> Doc ppType (PrimType pt) = ppPrimType pt ppType (Alias i) = ppIdent i ppType (Array len ty) = brackets (integral 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 (integral len <+> char 'x' <+> ppType pt) ppType Opaque = "opaque" ppTypeDecl :: TypeDecl -> Doc ppTypeDecl td = ppIdent (typeName td) <+> char '=' <+> "type" <+> ppType (typeValue td) -- Declarations ---------------------------------------------------------------- ppGlobal :: LLVM => Global -> Doc ppGlobal g = ppSymbol (globalSym g) <+> char '=' <+> ppTheGlobalAttrs (globalAttrs g) <+> ppType (globalType g) <+> ppMaybe ppValue (globalValue g) <> ppAlign (globalAlign g) <> ppAttachedMetadata (Map.toList (globalMetadata g)) where isStruct | Just (ValStruct {}) <- globalValue g = True | otherwise = False ppTheGlobalAttrs | isStruct = ppStructGlobalAttrs | otherwise = ppGlobalAttrs ppGlobalAttrs :: GlobalAttrs -> Doc ppGlobalAttrs ga -- LLVM 3.8 does not emit or parse linkage information w/ hidden visibility | Just HiddenVisibility <- gaVisibility ga = ppVisibility HiddenVisibility <+> constant | otherwise = ppMaybe ppLinkage (gaLinkage ga) <+> ppMaybe ppVisibility (gaVisibility ga) <+> constant where constant | gaConstant ga = "constant" | otherwise = "global" ppStructGlobalAttrs :: GlobalAttrs -> Doc ppStructGlobalAttrs ga -- LLVM 3.8 does not emit or parse external linkage for -- global structs | Just External <- gaLinkage ga, Just DefaultVisibility <- gaVisibility ga = constant | otherwise = ppGlobalAttrs ga where constant | gaConstant ga = "constant" | otherwise = "global" ppDeclare :: Declare -> Doc ppDeclare d = "declare" <+> ppType (decRetType d) <+> ppSymbol (decName d) <> ppArgList (decVarArgs d) (map ppType (decArgs d)) <+> hsep (ppFunAttr <$> decAttrs d) <> maybe empty ((char ' ' <>) . ppComdatName) (decComdat d) ppComdatName :: String -> Doc ppComdatName s = "comdat" <> parens (char '$' <> text s) ppComdat :: (String,SelectionKind) -> Doc ppComdat (n,k) = ppComdatName n <+> char '=' <+> text "comdat" <+> ppSelectionKind k ppSelectionKind :: SelectionKind -> Doc ppSelectionKind k = case k of ComdatAny -> "any" ComdatExactMatch -> "exactmatch" ComdatLargest -> "largest" ComdatNoDuplicates -> "noduplicates" ComdatSameSize -> "samesize" ppDefine :: LLVM => Define -> Doc ppDefine d = "define" <+> ppMaybe ppLinkage (defLinkage d) <+> ppType (defRetType d) <+> ppSymbol (defName d) <> ppArgList (defVarArgs d) (map (ppTyped ppIdent) (defArgs d)) <+> hsep (ppFunAttr <$> defAttrs d) <+> ppMaybe (\s -> "section" <+> doubleQuotes (text s)) (defSection d) <+> ppMaybe (\gc -> "gc" <+> ppGC gc) (defGC d) <+> ppMds (defMetadata d) <+> char '{' $+$ vcat (map ppBasicBlock (defBody d)) $+$ char '}' where ppMds mdm = case Map.toList mdm of [] -> empty mds -> hsep [ "!" <> text k <+> ppValMd md | (k, md) <- mds ] -- FunAttr --------------------------------------------------------------------- ppFunAttr :: FunAttr -> Doc ppFunAttr a = case a of AlignStack w -> text "alignstack" <> parens (int w) Alwaysinline -> text "alwaysinline" Builtin -> text "builtin" Cold -> text "cold" Inlinehint -> text "inlinehint" Jumptable -> text "jumptable" Minsize -> text "minsize" Naked -> text "naked" Nobuiltin -> text "nobuiltin" Noduplicate -> text "noduplicate" Noimplicitfloat -> text "noimplicitfloat" Noinline -> text "noinline" Nonlazybind -> text "nonlazybind" Noredzone -> text "noredzone" Noreturn -> text "noreturn" Nounwind -> text "nounwind" Optnone -> text "optnone" Optsize -> text "optsize" Readnone -> text "readnone" Readonly -> text "readonly" ReturnsTwice -> text "returns_twice" SanitizeAddress -> text "sanitize_address" SanitizeMemory -> text "sanitize_memory" SanitizeThread -> text "sanitize_thread" SSP -> text "ssp" SSPreq -> text "sspreq" SSPstrong -> text "sspstrong" UWTable -> text "uwtable" -- Basic Blocks ---------------------------------------------------------------- ppLabelDef :: BlockLabel -> Doc ppLabelDef (Named (Ident l)) = text l <> char ':' ppLabelDef (Anon i) = char ';' <+> "