{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE LambdaCase #-} {-# 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 Text.LLVM.Triple.AST (TargetTriple) import Text.LLVM.Triple.Print (printTriple) 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 ------------------------------------------------------- -- | The value used to specify the LLVM major version. The LLVM text format -- (i.e. assembly code) changes with different versions of LLVM, so this value is -- used to select the version the output should be generated for. -- -- At the current time, changes primarily occur when the LLVM major version -- changes, and this is expected to be the case going forward, so it is -- sufficient to reference the LLVM version by the single major version number. -- There is one exception and one possible future exception to this approach: -- -- 1. During LLVM v3, there were changes in 3.5, 3.6, 3.7, and 3.8. There are -- explicit @ppLLVMnn@ function entry points for those versions, but in the -- event that a numerical value is needed, we note the serendipitous fact -- that prior to LLVM 4, there are exactly 4 versions we need to -- differentiate and can therefore assign the values of 0, 1, 2, and 3 to -- those versions (and we have no intention of supporting any other pre-4.0 -- versions at this point). -- -- 2. If at some future date, there are text format changes associated with a -- minor version, then the LLVM version designation here will need to be -- enhanced and made more sophisticated. At the present time, the likelihood -- of that is small enough that the current simple implementation is a -- benefit over a more complex mechanism that might not be needed. -- type LLVMVer = Int -- | Helpers for specifying the LLVM versions prior to v4 llvmV3_5, llvmV3_6, llvmV3_7, llvmV3_8 :: LLVMVer llvmV3_5 = 0 llvmV3_6 = 1 llvmV3_7 = 2 llvmV3_8 = 3 -- | This value should be updated when support is added for new LLVM versions; -- this is used for defaulting and otherwise reporting the maximum LLVM version -- known to be supported. llvmVlatest :: LLVMVer llvmVlatest = 17 -- | The differences between various versions of the llvm textual AST. newtype Config = Config { cfgVer :: LLVMVer } withConfig :: Config -> ((?config :: Config) => a) -> a withConfig cfg body = let ?config = cfg in body ppLLVM :: LLVMVer -> ((?config :: Config) => a) -> a ppLLVM llvmver = withConfig Config { cfgVer = llvmver } ppLLVM35, ppLLVM36, ppLLVM37, ppLLVM38 :: ((?config :: Config) => a) -> a ppLLVM35 = withConfig Config { cfgVer = llvmV3_5 } ppLLVM36 = withConfig Config { cfgVer = llvmV3_6 } ppLLVM37 = withConfig Config { cfgVer = llvmV3_7 } ppLLVM38 = withConfig Config { cfgVer = llvmV3_8 } llvmVer :: (?config :: Config) => LLVMVer llvmVer = cfgVer ?config -- | This is a helper function for when a list of parameters is gated by a -- condition (usually the llvmVer value). when' :: Monoid a => Bool -> a -> a when' c l = if c then l else mempty -- | This type encapsulates the ability to convert an object into Doc -- format. Using this abstraction allows for a consolidated representation of the -- declaration. Most pretty-printing for LLVM elements will have a @'Fmt' a@ -- function signature for that element. type Fmt a = (?config :: Config) => a -> Doc -- | The LLVMPretty class has instances for most AST elements. It allows the -- conversion of an AST element (and its sub-elements) into a Doc assembly format -- by simply using the 'llvmPP' method rather than needing to explicitly invoke -- the specific pretty-printing function for that element. class LLVMPretty a where llvmPP :: Fmt a instance LLVMPretty Module where llvmPP = ppModule instance LLVMPretty Symbol where llvmPP = ppSymbol instance LLVMPretty Ident where llvmPP = ppIdent -- Modules --------------------------------------------------------------------- ppModule :: Fmt Module ppModule m = foldr ($+$) empty $ ppSourceName (modSourceName m) : ppTargetTriple (modTriple 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 :: Fmt (Maybe String) ppSourceName Nothing = empty ppSourceName (Just sn) = "source_filename" <+> char '=' <+> doubleQuotes (text sn) -- Metadata -------------------------------------------------------------------- ppNamedMd :: Fmt NamedMd ppNamedMd nm = sep [ ppMetadata (text (nmName nm)) <+> char '=' , ppMetadata (braces (commas (map (ppMetadata . int) (nmValues nm)))) ] ppUnnamedMd :: Fmt UnnamedMd ppUnnamedMd um = sep [ ppMetadata (int (umIndex um)) <+> char '=' , distinct <+> ppValMd (umValues um) ] where distinct | umDistinct um = "distinct" | otherwise = empty -- Aliases --------------------------------------------------------------------- ppGlobalAlias :: Fmt GlobalAlias ppGlobalAlias g = ppSymbol (aliasName g) <+> char '=' <+> ppMaybe ppLinkage (aliasLinkage g) <+> ppMaybe ppVisibility (aliasVisibility g) <+> body where val = aliasTarget g body = case val of ValSymbol _sym -> ppType (aliasType g) <+> ppValue val _ -> ppValue val -- Target triple --------------------------------------------------------------- -- | Pretty print a 'TargetTriple' ppTargetTriple :: Fmt TargetTriple ppTargetTriple triple = "target" <+> "triple" <+> char '=' <+> doubleQuotes (text (printTriple triple)) -- Data Layout ----------------------------------------------------------------- -- | Pretty print a data layout specification. ppDataLayout :: Fmt DataLayout ppDataLayout [] = empty ppDataLayout ls = "target" <+> "datalayout" <+> char '=' <+> doubleQuotes (hcat (intersperse (char '-') (map ppLayoutSpec ls))) -- | Pretty print a single layout specification. ppLayoutSpec :: Fmt LayoutSpec 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 -> Fmt (Maybe Int) ppLayoutBody size abi mb = int size <> char ':' <> int abi <> pref where pref = case mb of Nothing -> empty Just p -> char ':' <> int p ppMangling :: Fmt Mangling 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 :: Fmt InlineAsm ppInlineAsm = foldr ($+$) empty . map ppLine where ppLine l = "module asm" <+> doubleQuotes (text l) -- Identifiers ----------------------------------------------------------------- ppIdent :: Fmt Ident 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 :: Fmt Symbol ppSymbol (Symbol n) | validIdentifier n = char '@' <> text n | otherwise = char '@' <> ppStringLiteral n -- Types ----------------------------------------------------------------------- ppPrimType :: Fmt PrimType 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 :: Fmt FloatType ppFloatType Half = "half" ppFloatType Float = "float" ppFloatType Double = "double" ppFloatType Fp128 = "fp128" ppFloatType X86_fp80 = "x86_fp80" ppFloatType PPC_fp128 = "ppc_fp128" ppType :: Fmt Type 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 PtrOpaque = "ptr" 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 :: Fmt TypeDecl ppTypeDecl td = ppIdent (typeName td) <+> char '=' <+> "type" <+> ppType (typeValue td) -- Declarations ---------------------------------------------------------------- ppGlobal :: Fmt Global ppGlobal g = ppSymbol (globalSym g) <+> char '=' <+> ppGlobalAttrs (isJust $ globalValue g) (globalAttrs g) <+> ppType (globalType g) <+> ppMaybe ppValue (globalValue g) <> ppAlign (globalAlign g) <> ppAttachedMetadata (Map.toList (globalMetadata g)) -- | Pretty-print Global Attributes (usually associated with a global variable -- declaration). The first argument to ppGlobalAttrs indicates whether there is a -- value associated with this global declaration: a global declaration with a -- value should not be identified as \"external\" and \"default\" visibility, -- whereas one without a value may have those attributes. ppGlobalAttrs :: Bool -> Fmt GlobalAttrs ppGlobalAttrs hasValue ga -- LLVM 3.8 does not emit or parse linkage information w/ hidden visibility | Just HiddenVisibility <- gaVisibility ga = ppVisibility HiddenVisibility <+> constant | Just External <- gaLinkage ga , Just DefaultVisibility <- gaVisibility ga , hasValue = -- Just show the value, no "external" or "default". This is based on -- empirical testing as described in the comment above (testing the -- following 6 configurations: -- * uninitialized scalar -- * uninitialized structure -- * initialized scalar -- * initialized structure -- * external scalar -- * external structure constant | otherwise = ppMaybe ppLinkage (gaLinkage ga) <+> ppMaybe ppVisibility (gaVisibility ga) <+> constant where constant | gaConstant ga = "constant" | otherwise = "global" ppDeclare :: Fmt Declare ppDeclare d = "declare" <+> ppMaybe ppLinkage (decLinkage d) <+> ppMaybe ppVisibility (decVisibility d) <+> ppType (decRetType d) <+> ppSymbol (decName d) <> ppArgList (decVarArgs d) (map ppType (decArgs d)) <+> hsep (ppFunAttr <$> decAttrs d) <> maybe empty ((char ' ' <>) . ppComdatName) (decComdat d) ppComdatName :: Fmt String ppComdatName s = "comdat" <> parens (char '$' <> text s) ppComdat :: Fmt (String,SelectionKind) ppComdat (n,k) = ppComdatName n <+> char '=' <+> text "comdat" <+> ppSelectionKind k ppSelectionKind :: Fmt SelectionKind ppSelectionKind k = case k of ComdatAny -> "any" ComdatExactMatch -> "exactmatch" ComdatLargest -> "largest" ComdatNoDuplicates -> "noduplicates" ComdatSameSize -> "samesize" ppDefine :: Fmt Define ppDefine d = "define" <+> ppMaybe ppLinkage (defLinkage d) <+> ppMaybe ppVisibility (defVisibility 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 :: Fmt FunAttr 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 :: Fmt BlockLabel ppLabelDef (Named (Ident l)) = text l <> char ':' ppLabelDef (Anon i) = char ';' <+> "