{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE TypeSynonymInstances #-} {-# OPTIONS_GHC -fwarn-incomplete-patterns #-} {-# OPTIONS_GHC -fwarn-incomplete-uni-patterns #-} {-# OPTIONS_GHC -Wno-incomplete-patterns -Wno-incomplete-uni-patterns #-} ----------------------------------------------------------------------------- -- | -- Copyright : (c) Edward Kmett 2018, (c) Stephen Diehl 2014-2017, (c) Cedric Shoc 2015 -- License : BSD-2-Clause OR Apache-2.0 -- Maintainer : ekmett@gmail.com -- Stability : experimental -- Portability : portable -- ----------------------------------------------------------------------------- module Console.Pretty.LLVM ( PP(..), ppll, ppll_ ) where import Control.Monad (guard) import Control.Monad.IO.Class import Control.Monad.ST import Data.Array.MArray hiding (index) import Data.Array.ST hiding (index) import Data.Array.Unsafe import qualified Data.ByteString.Char8 as BL import Data.ByteString.Lazy (fromStrict) import qualified Data.ByteString.Short as BS import qualified Data.ByteString.Short as SBF import Data.Char (chr, ord, isAscii, isControl, isLetter, isDigit) import Data.Default.Class import Data.Foldable (toList) import Data.List (intersperse) import Data.Maybe (isJust) import Data.String import Data.Text.Lazy (Text, pack) import Data.Text.Lazy.Encoding import Data.Text.Prettyprint.Doc import Data.Text.Prettyprint.Doc.Render.Terminal as RenderTerminal import GHC.Word import LLVM.AST import qualified LLVM.AST.AddrSpace as AS import LLVM.AST.Attribute import qualified LLVM.AST.CallingConvention as CC import LLVM.AST.COMDAT import qualified LLVM.AST.Constant as C import LLVM.AST.DataLayout import qualified LLVM.AST.Float as F import qualified LLVM.AST.FloatingPointPredicate as FP import LLVM.AST.FunctionAttribute as FA import LLVM.AST.Global import qualified LLVM.AST.IntegerPredicate as IP import qualified LLVM.AST.Linkage as L import LLVM.AST.ParameterAttribute as PA import qualified LLVM.AST.RMWOperation as RMW -- import LLVM.AST.Type import LLVM.AST.Typed -- import qualified LLVM.AST.Visibility as V import LLVM.DataLayout import Numeric (showHex) import Prelude hiding ((<$>)) import Text.Printf import Console.Pretty ------------------------------------------------------------------------------- -- Utils ------------------------------------------------------------------------------- -- parensIf :: Bool -> Doc a -> Doc a -- parensIf True = parens -- parensIf False = id commas :: [Doc a] -> Doc a commas = hsep . punctuate (pretty ',') -- colons :: [Doc a] -> Doc a -- colons = hcat . intersperse (pretty ':') hlinecat :: [Doc a] -> Doc a hlinecat = vcat . intersperse softline wrapbraces :: Doc a -> Doc a -> Doc a wrapbraces leadIn x = (leadIn <+> pretty '{') <$> x <$> pretty '}' (<$>) :: Doc a -> Doc a -> Doc a x <$> y = x <> line <> y angleBrackets :: Doc a -> Doc a angleBrackets x = pretty '<' <> x <> pretty '>' spacedbraces :: Doc a -> Doc a spacedbraces x = pretty '{' <+> x <+> pretty '}' local :: Ann a => Doc a -> Doc a local a = annotate annIdentifier $ "%" <> a global :: Ann a => Doc a -> Doc a global a = annotate annIdentifier $ "@" <> a label :: Ann a => Doc a -> Doc a label a = annotate annType "label" <+> annotate annIdentifier ("%" <> a) cma :: Doc a -> Doc a -> Doc a -- <,> does not work :( a `cma` b = a <> "," <+> b ------------------------------------------------------------------------------- -- Classes ------------------------------------------------------------------------------- class Ann a where annIdentifier, annType, annStmt, annKeyword, annComment, annString :: a instance Ann () where annIdentifier = () annType = () annStmt = () annKeyword = () annComment = () annString = () instance Ann AnsiStyle where annIdentifier = color Green annType = color Blue annStmt = colorDull Yellow annKeyword = underlined annComment = color Magenta annString = color Cyan astmt :: Ann a => Doc a -> Doc a astmt = annotate annStmt atype :: Ann a => Doc a -> Doc a atype = annotate annType akw :: Ann a => Doc a -> Doc a akw = annotate annKeyword acomment :: Ann a => Doc a -> Doc a acomment = annotate annComment dquoted :: Ann a => Doc a -> Doc a dquoted = annotate annString . dquotes class PP p where pp :: Ann a => p -> Doc a default pp :: Pretty p => p -> Doc a pp = pretty ppMaybe :: (PP a, Ann b) => Maybe a -> Doc b ppMaybe (Just x) = pp x ppMaybe Nothing = mempty ppBool :: Doc a -> Bool -> Doc a ppBool x True = x ppBool _ False = mempty -- XXX: horrible hack unShort :: BS.ShortByteString -> [Char] unShort xs = fmap (toEnum . fromIntegral) $ BS.unpack xs short :: Ann a => BS.ShortByteString -> Doc a short x = pretty (unShort x) decodeShortUtf8 :: SBF.ShortByteString -> Text decodeShortUtf8 = decodeUtf8 . fromStrict . SBF.fromShort instance PP Word32 instance PP Word64 instance PP Integer where instance PP BS.ShortByteString where pp = pp . unShort instance PP [Char] instance PP Name where pp (Name nm) | BS.null nm = dquoted mempty | isFirst first && all isRest name = pretty (pack name) | otherwise = dquoted . 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) = pretty x instance PP Parameter where pp (Parameter ty (UnName _) attrs) = hsep (pp ty : fmap pp attrs) pp (Parameter ty name attrs) = hsep (pp ty : fmap 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 w) = atype $ "i" <> pp w pp (FloatingPointType HalfFP) = atype "half" pp (FloatingPointType FloatFP ) = atype "float" pp (FloatingPointType DoubleFP) = atype "double" pp (FloatingPointType FP128FP) = atype "fp128" pp (FloatingPointType X86_FP80FP) = atype "x86_fp80" pp (FloatingPointType PPC_FP128FP) = atype "ppc_fp128" pp VoidType = atype "void" pp (PointerType ref (AS.AddrSpace addr)) | addr == 0 = atype $ pp ref <> "*" | otherwise = atype $ pp ref <+> akw "addrspace" <> parens (pp addr) <> "*" pp ft@(FunctionType {..}) = atype $ pp resultType <+> ppFunctionArgumentTypes ft pp (VectorType {..}) = atype $ "<" <> pp nVectorElements <+> "x" <+> pp elementType <> ">" pp (StructureType {..}) = atype $ if isPacked then "<{" <> (commas $ fmap pp elementTypes ) <> "}>" else "{" <> (commas $ fmap pp elementTypes ) <> "}" pp (ArrayType {..}) = atype $ brackets $ pp nArrayElements <+> "x" <+> pp elementType pp (NamedTypeReference name) = atype ("%" <> pp name) pp MetadataType = atype "metadata" pp TokenType = atype "token" pp LabelType = atype "label" instance PP Global where pp Function {..} = case basicBlocks of [] -> sep $ pre "declare" ++ [pp returnType, global (pp name) <> ppParams (pp . typeOf) parameters] ++ post -- single unnamed block is special cased, and won't parse otherwise... yeah good times [b@(BasicBlock (UnName _) _ _)] -> sep ( pre "define" ++ [pp returnType, global (pp name) <> ppParams pp parameters] ++ post ) `wrapbraces` (indent 2 $ ppSingleBlock b) bs -> sep ( pre "define" ++ [pp returnType, global (pp name) <> ppParams pp parameters] ++ post ) `wrapbraces` (vcat $ fmap pp bs) where pre kw = akw kw : pp linkage : pp callingConvention : fmap pp returnAttributes post = fmap pp functionAttributes ++ align' ++ gcName ++ foldMap (\con -> [ akw "prefix", ppTyped con ]) prefix align' = guard (alignment /= 0) *> [akw "align", pp alignment] gcName = foldMap (\n -> [akw "gc", dquoted (pretty $ unShort n)]) 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 -> akw "addrspace" <> parens (pp addr) kind | isConstant = akw "constant" | otherwise = akw "global" pp GlobalAlias {..} = global (pp name) <+> "=" <+> pp linkage <+> ppMaybe unnamedAddr <+> "alias" <+> pp typ `cma` ppTyped aliasee where typ = getElementType type' ppMetadata :: Ann a => Maybe Metadata -> Doc a 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) = akw "module" <+> akw "asm" <+> dquoted (pretty (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 :: Ann a => FunctionAttribute -> Doc a ppAttrInGroup = \case StackAlignment n -> "alignstack=" <> pp n attr -> pp attr instance PP FunctionAttribute where pp = \case NoReturn -> akw "noreturn" NoUnwind -> akw "nounwind" FA.ReadNone -> akw "readnone" FA.ReadOnly -> akw "readonly" FA.WriteOnly -> akw "writeonly" NoInline -> akw "noinline" AlwaysInline -> akw "alwaysinline" MinimizeSize -> akw "minsize" OptimizeForSize -> akw "optsize" OptimizeNone -> akw "optnone" SafeStack -> akw "safestack" StackProtect -> akw "ssp" StackProtectReq -> akw "sspreq" StackProtectStrong -> akw "sspstrong" NoRedZone -> akw "noredzone" NoImplicitFloat -> akw "noimplicitfloat" Naked -> akw "naked" InlineHint -> akw "inlinehint" StackAlignment n -> akw "alignstack" <> parens (pp n) ReturnsTwice -> akw "returns_twice" UWTable -> akw "uwtable" NonLazyBind -> akw "nonlazybind" Builtin -> akw "builtin" NoBuiltin -> akw "nobuiltin" Cold -> akw "cold" JumpTable -> akw "jumptable" NoDuplicate -> akw "noduplicate" SanitizeAddress -> akw "sanitize_address" SanitizeThread -> akw "sanitize_thread" SanitizeMemory -> akw "sanitize_memory" NoRecurse -> akw "norecurse" Convergent -> akw "convergent" ArgMemOnly -> akw "argmemonly" InaccessibleMemOnly -> akw "inaccessiblememonly" AllocSize a Nothing -> akw "allocsize" <> parens (pp a) AllocSize a (Just b) -> akw "allocsize" <> parens (commas [pp a, pp b]) InaccessibleMemOrArgMemOnly -> akw "inaccessiblemem_or_argmemonly" FA.StringAttribute k v -> dquoted (short k) <> "=" <> dquoted (short v) Speculatable -> akw "speculatable" instance PP ParameterAttribute where pp = \case ZeroExt -> akw "zeroext" SignExt -> akw "signext" InReg -> akw "inreg" SRet -> akw "sret" Alignment word -> akw "align" <+> pp word NoAlias -> akw "noalias" ByVal -> akw "byval" NoCapture -> akw "nocapture" Nest -> akw "nest" PA.ReadNone -> akw "readnone" PA.ReadOnly -> akw "readonly" PA.WriteOnly -> akw "writeonly" InAlloca -> akw "inalloca" NonNull -> akw "nonnull" Dereferenceable word -> akw "dereferenceable" <> parens (pp word) DereferenceableOrNull word -> akw "dereferenceable_or_null" <> parens (pp word) Returned -> akw "returned" SwiftSelf -> akw "swiftself" SwiftError -> akw "swifterror" PA.StringAttribute k v -> dquoted (short k) <> "=" <> dquoted (short v) instance PP CC.CallingConvention where pp = \case CC.Numbered word -> cc word CC.C -> akw "ccc" CC.Fast -> akw "fastcc" CC.Cold -> akw "coldcc" CC.GHC -> cc 10 CC.HiPE -> cc 11 CC.WebKit_JS -> akw "webkit_jscc" CC.AnyReg -> akw "anyregcc" CC.PreserveMost -> akw "preserve_mostcc" CC.PreserveAll -> akw "preserve_allcc" CC.Swift -> akw "swiftcc" CC.CXX_FastTLS -> akw "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 -> akw "x86_intrcc" CC.X86_RegCall -> akw "x86_regcallcc" CC.X86_VectorCall -> akw "x86_vectorcallcc" CC.AVR_Intr -> akw "avr_intrcc" CC.AVR_Signal -> akw "avr_signalcc" CC.AVR_Builtin -> cc 86 CC.HHVM -> akw "hhvmcc" CC.HHVM_C -> akw "hhvm_ccc" CC.AMDGPU_VS -> akw "amdgpu_vs" CC.AMDGPU_GS -> akw "amdgpu_gs" CC.AMDGPU_PS -> akw "amdgpu_ps" CC.AMDGPU_CS -> akw "amdgpu_cs" CC.AMDGPU_HS -> akw "amdgpu_hs" CC.AMDGPU_Kernel -> akw "amdgpu_kernel" CC.MSP430_Builtin -> akw "msp430" where cc :: Ann a => Word32 -> Doc a cc n = akw "cc" <+> pp n instance PP L.Linkage where pp = ppLinkage False ppLinkage :: Ann a => Bool -> L.Linkage -> Doc a ppLinkage omitExternal = \case L.External | omitExternal -> mempty | otherwise -> akw "external" L.Private -> akw "private" L.Internal -> akw "internal" L.ExternWeak -> akw "extern_weak" L.AvailableExternally -> akw "available_externally" L.LinkOnce -> akw "linkonce" L.Weak -> akw "weak" L.Common -> akw "common" L.Appending -> akw "appending" L.LinkOnceODR -> akw "linkonce_odr" L.WeakODR -> akw "weak_odr" instance PP InstructionMetadata where pp meta = commas ["!" <> pp x <> "!" <> ("{" <> pp y <> "}") | (x,y) <- meta] instance PP MetadataNodeID where pp (MetadataNodeID x) = "!" <> pretty x instance PP GroupID where pp (GroupID x) = "#" <> pretty x instance PP BasicBlock where pp (BasicBlock nm instrs term) = lbl <$> indent 2 (vcat $ (fmap pp instrs) ++ [pp term]) where lbl = case nm of UnName _ -> acomment $ ";