{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeSynonymInstances #-} {-# OPTIONS_GHC -fwarn-incomplete-patterns #-} {-# OPTIONS_GHC -fwarn-incomplete-uni-patterns #-} module LLVM.Pretty ( ppllvm, ppll, ) where import Prelude hiding ((<$>)) import GHC.Word import LLVM.AST.Typed import LLVM.AST import LLVM.AST.Global import LLVM.AST.Type import LLVM.DataLayout import LLVM.AST.Attribute import LLVM.AST.DataLayout import LLVM.AST.COMDAT import qualified LLVM.AST.Linkage as L import qualified LLVM.AST.Visibility as V import qualified LLVM.AST.CallingConvention as CC import qualified LLVM.AST.Constant as C import qualified LLVM.AST.FloatingPointPredicate as FP import qualified LLVM.AST.IntegerPredicate as IP import qualified LLVM.AST.AddrSpace as AS import qualified LLVM.AST.Float as F import qualified LLVM.AST.RMWOperation as RMW import LLVM.AST.ParameterAttribute as PA import LLVM.AST.FunctionAttribute as FA import Data.String import Text.Printf import Data.Text.Lazy.Encoding import Data.Text.Lazy (Text, pack, unpack) import qualified Data.ByteString.Short as SBF import qualified Data.ByteString.Lazy.Char8 as BF import Data.ByteString.Lazy (fromStrict) import Data.ByteString.Internal (w2c) import Text.PrettyPrint.Leijen.Text import qualified Data.ByteString.Char8 as BL import qualified Data.ByteString.Short as BS import Data.Char (chr, ord, isAscii, isControl, isLetter, isDigit) import Data.Foldable (toList) import Data.List (intersperse) import Data.Maybe (isJust) import Numeric (showHex) import Data.Array.Unsafe import Data.Array.MArray import Data.Array.ST import Control.Monad.ST ------------------------------------------------------------------------------- -- Utils ------------------------------------------------------------------------------- parensIf :: Bool -> Doc -> Doc parensIf True = parens parensIf False = id commas :: [Doc] -> Doc commas = hsep . punctuate (char ',') colons :: [Doc] -> Doc colons = hcat . intersperse (char ':') hlinecat :: [Doc] -> Doc hlinecat = vcat . intersperse softbreak wrapbraces :: Doc -> Doc -> Doc wrapbraces leadIn x = (leadIn <> char '{') <$> x <$> char '}' angleBrackets :: Doc -> Doc angleBrackets x = char '<' <> x <> char '>' spacedbraces :: Doc -> Doc spacedbraces x = char '{' <+> x <+> char '}' local :: Doc -> Doc local a = "%" <> a global :: Doc -> Doc global a = "@" <> a label :: Doc -> Doc label a = "label" <+> "%" <> a cma :: Doc -> Doc -> Doc -- <,> does not work :( a `cma` b = a <> "," <+> b ------------------------------------------------------------------------------- -- Classes ------------------------------------------------------------------------------- class PP p where pp :: p -> Doc ppMaybe :: PP a => Maybe a -> Doc ppMaybe (Just x) = pp x ppMaybe Nothing = empty ppBool :: Doc -> Bool -> Doc ppBool x True = x ppBool x False = empty -- XXX: horrible hack unShort :: BS.ShortByteString -> [Char] unShort xs = fmap (toEnum . fromIntegral) $ BS.unpack xs short :: BS.ShortByteString -> Doc short x = string (pack (unShort x)) decodeShortUtf8 :: SBF.ShortByteString -> Text decodeShortUtf8 = decodeUtf8 . fromStrict . SBF.fromShort instance PP Word32 where pp x = int (fromIntegral x) instance PP Word64 where pp x = int (fromIntegral x) instance PP Integer where pp = integer instance PP BS.ShortByteString where pp = pp . unShort instance PP [Char] where pp = text . pack instance PP Name where pp (Name nm) | BS.null nm = dquotes empty | isFirst first && all isRest name = text (pack name) | otherwise = dquotes . hcat . map escape $ name where name = unShort nm first = head name isFirst c = isLetter c || c == '-' || c == '_' || c == '$' || c == '.' isRest c = isDigit c || isFirst c pp (UnName x) = int (fromIntegral x) instance PP Parameter where pp (Parameter ty (UnName _) attrs) = pp ty <+> pp attrs pp (Parameter ty name attrs) = pp ty <+> pp attrs <+> local (pp name) instance PP [ParameterAttribute] where pp x = hsep $ fmap pp x instance PP ([Parameter], Bool) where pp (params, False) = commas (fmap pp params) pp (params, True) = "TODO" -- XXX: variadic case instance PP (Operand, [ParameterAttribute]) where pp (op, attrs) = pp (typeOf op) <+> pp attrs <+> pp op instance PP UnnamedAddr where pp LocalAddr = "local_unnamed_addr" pp GlobalAddr = "unnamed_addr" instance PP Type where pp (IntegerType width) = "i" <> pp width pp (FloatingPointType HalfFP) = "half" pp (FloatingPointType FloatFP ) = "float" pp (FloatingPointType DoubleFP) = "double" pp (FloatingPointType FP128FP) = "fp128" pp (FloatingPointType X86_FP80FP) = "x86_fp80" pp (FloatingPointType PPC_FP128FP) = "ppc_fp128" pp VoidType = "void" pp (PointerType ref (AS.AddrSpace addr)) | addr == 0 = pp ref <> "*" | otherwise = pp ref <+> "addrspace" <> parens (pp addr) <> "*" pp ft@(FunctionType {..}) = pp resultType <+> ppFunctionArgumentTypes ft pp (VectorType {..}) = "<" <> pp nVectorElements <+> "x" <+> pp elementType <> ">" pp (StructureType {..}) = if isPacked then "<{" <> (commas $ fmap pp elementTypes ) <> "}>" else "{" <> (commas $ fmap pp elementTypes ) <> "}" pp (ArrayType {..}) = brackets $ pp nArrayElements <+> "x" <+> pp elementType pp (NamedTypeReference name) = "%" <> pp name pp MetadataType = "metadata" pp TokenType = "token" pp LabelType = "label" instance PP Global where pp Function {..} = case basicBlocks of [] -> ("declare" <+> pp linkage <+> pp callingConvention <+> pp returnAttributes <+> pp returnType <+> global (pp name) <> ppParams (pp . typeOf) parameters <+> pp functionAttributes <+> align <+> gcName) -- single unnamed block is special cased, and won't parse otherwise... yeah good times [b@(BasicBlock (UnName _) _ _)] -> ("define" <+> pp linkage <+> pp callingConvention <+> pp returnAttributes <+> pp returnType <+> global (pp name) <> ppParams pp parameters <+> pp functionAttributes <+> align <+> gcName) `wrapbraces` (indent 2 $ ppSingleBlock b) bs -> ("define" <+> pp linkage <+> pp callingConvention <+> pp returnAttributes <+> pp returnType <+> global (pp name) <> ppParams pp parameters <+> pp functionAttributes <+> align <+> gcName) `wrapbraces` (vcat $ fmap pp bs) where align | alignment == 0 = empty | otherwise = "align" <+> pp alignment gcName = maybe empty (\n -> "gc" <+> dquotes (text $ pack n)) (fmap unShort garbageCollectorName) pp GlobalVariable {..} = global (pp name) <+> "=" <+> ppLinkage hasInitializer linkage <+> ppMaybe unnamedAddr <+> addrSpace' <+> kind <+> pp type' <+> ppMaybe initializer <> ppAlign alignment where hasInitializer = isJust initializer addrSpace' = case addrSpace of AS.AddrSpace addr | addr == 0 -> mempty | otherwise -> "addrspace" <> parens (pp addr) kind | isConstant = "constant" | otherwise = "global" pp GlobalAlias {..} = global (pp name) <+> "=" <+> pp linkage <+> ppMaybe unnamedAddr <+> "alias" <+> pp typ `cma` ppTyped aliasee where typ = getElementType type' ppMetadata :: Maybe Metadata -> Doc ppMetadata Nothing = "null" ppMetadata (Just m) = pp m instance PP Definition where pp (GlobalDefinition x) = pp x pp (TypeDefinition nm ty) = local (pp nm) <+> "=" <+> "type" <+> maybe "opaque" pp ty pp (FunctionAttributes gid attrs) = "attributes" <+> pp gid <+> "=" <+> braces (hsep (fmap ppAttrInGroup attrs)) pp (NamedMetadataDefinition nm meta) = "!" <> short nm <+> "=" <+> "!" <> braces (commas (fmap pp meta)) pp (MetadataNodeDefinition node meta) = pp node <+> "=" <+> "!" <> braces (commas (fmap ppMetadata meta)) pp (ModuleInlineAssembly asm) = "module asm" <+> dquotes (text (pack (BL.unpack asm))) pp (COMDAT name selKind) = "$" <> short name <+> "=" <+> "comdat" <+> pp selKind instance PP SelectionKind where pp Any = "any" pp ExactMatch = "exactmatch" pp Largest = "largest" pp NoDuplicates = "noduplicates" pp SameSize = "samesize" ppAttrInGroup :: FunctionAttribute -> Doc ppAttrInGroup = \case StackAlignment n -> "alignstack=" <> pp n attr -> pp attr instance PP FunctionAttribute where pp = \case NoReturn -> "noreturn" NoUnwind -> "nounwind" FA.ReadNone -> "readnone" FA.ReadOnly -> "readonly" FA.WriteOnly -> "writeonly" NoInline -> "noinline" AlwaysInline -> "alwaysinline" MinimizeSize -> "minsize" OptimizeForSize -> "optsize" OptimizeNone -> "optnone" SafeStack -> "safestack" StackProtect -> "ssp" StackProtectReq -> "sspreq" StackProtectStrong -> "sspstrong" NoRedZone -> "noredzone" NoImplicitFloat -> "noimplicitfloat" Naked -> "naked" InlineHint -> "inlinehint" StackAlignment n -> "alignstack" <> parens (pp n) ReturnsTwice -> "returns_twice" UWTable -> "uwtable" NonLazyBind -> "nonlazybind" Builtin -> "builtin" NoBuiltin -> "nobuiltin" Cold -> "cold" JumpTable -> "jumptable" NoDuplicate -> "noduplicate" SanitizeAddress -> "sanitize_address" SanitizeThread -> "sanitize_thread" SanitizeMemory -> "sanitize_memory" NoRecurse -> "norecurse" Convergent -> "convergent" ArgMemOnly -> "argmemonly" InaccessibleMemOnly -> "inaccessiblememonly" AllocSize a Nothing -> "allocsize" <> parens (pp a) AllocSize a (Just b) -> "allocsize" <> parens (commas [pp a, pp b]) InaccessibleMemOrArgMemOnly -> "inaccessiblemem_or_argmemonly" FA.StringAttribute k v -> dquotes (short k) <> "=" <> dquotes (short v) Speculatable -> "speculatable" instance PP ParameterAttribute where pp = \case ZeroExt -> "zeroext" SignExt -> "signext" InReg -> "inreg" SRet -> "sret" Alignment word -> "align" <+> pp word NoAlias -> "noalias" ByVal -> "byval" NoCapture -> "nocapture" Nest -> "nest" PA.ReadNone -> "readnone" PA.ReadOnly -> "readonly" PA.WriteOnly -> "writeonly" InAlloca -> "inalloca" NonNull -> "nonnull" Dereferenceable word -> "dereferenceable" <> parens (pp word) DereferenceableOrNull word -> "dereferenceable_or_null" <> parens (pp word) Returned -> "returned" SwiftSelf -> "swiftself" SwiftError -> "swifterror" PA.StringAttribute k v -> dquotes (short k) <> "=" <> dquotes (short v) instance PP CC.CallingConvention where pp = \case CC.Numbered word -> "cc" <+> pp word CC.C -> "ccc" CC.Fast -> "fastcc" CC.Cold -> "coldcc" CC.GHC -> "cc 10" CC.HiPE -> "cc 11" CC.WebKit_JS -> "webkit_jscc" CC.AnyReg -> "anyregcc" CC.PreserveMost -> "preserve_mostcc" CC.PreserveAll -> "preserve_allcc" CC.Swift -> "swiftcc" CC.CXX_FastTLS -> "cxx_fast_tlscc" CC.X86_StdCall -> "cc 64" CC.X86_FastCall -> "cc 65" CC.ARM_APCS -> "cc 66" CC.ARM_AAPCS -> "cc 67" CC.ARM_AAPCS_VFP -> "cc 68" CC.MSP430_INTR -> "cc 69" CC.X86_ThisCall -> "cc 70" CC.PTX_Kernel -> "cc 71" CC.PTX_Device -> "cc 72" CC.SPIR_FUNC -> "cc 75" CC.SPIR_KERNEL -> "cc 76" CC.Intel_OCL_BI -> "cc 77" CC.X86_64_SysV -> "cc 78" CC.Win64 -> "cc 79" CC.X86_Intr -> "x86_intrcc" CC.X86_RegCall -> "x86_regcallcc" CC.X86_VectorCall -> "x86_vectorcallcc" CC.AVR_Intr -> "avr_intrcc" CC.AVR_Signal -> "avr_signalcc" CC.AVR_Builtin -> "cc 86" CC.HHVM -> "hhvmcc" CC.HHVM_C -> "hhvm_ccc" CC.AMDGPU_VS -> "amdgpu_vs" CC.AMDGPU_GS -> "amdgpu_gs" CC.AMDGPU_PS -> "amdgpu_ps" CC.AMDGPU_CS -> "amdgpu_cs" CC.AMDGPU_HS -> "amdgpu_hs" CC.AMDGPU_Kernel -> "amdgpu_kernel" CC.MSP430_Builtin -> "msp430" instance PP L.Linkage where pp = ppLinkage False ppLinkage :: Bool -> L.Linkage -> Doc ppLinkage omitExternal = \case L.External | omitExternal -> empty | otherwise -> "external" L.Private -> "private" L.Internal -> "internal" L.ExternWeak -> "extern_weak" L.AvailableExternally -> "available_externally" L.LinkOnce -> "linkonce" L.Weak -> "weak" L.Common -> "common" L.Appending -> "appending" L.LinkOnceODR -> "linkonce_odr" L.WeakODR -> "weak_odr" instance PP InstructionMetadata where pp meta = commas ["!" <> pp x <> "!" <> ("{" <> pp y <> "}") | (x,y) <- meta] instance PP MetadataNodeID where pp (MetadataNodeID x) = "!" <> int (fromIntegral x) instance PP GroupID where pp (GroupID x) = "#" <> int (fromIntegral x) instance PP BasicBlock where pp (BasicBlock nm instrs term) = label <$> indent 2 (vcat $ (fmap pp instrs) ++ [pp term]) where label = case nm of UnName _ -> ";