packages feed

llvm-pretty 0.12.1.0 → 0.15.0.0

raw patch · 16 files changed

Files

CHANGELOG.md view
@@ -1,5 +1,119 @@ # Revision history for llvm-pretty +## 0.15.0.0 -- 2026-08-27++* Add `LLVM.Combine` module with `llvmModuleCombine` function.  This is the+  proper way to combine two LLVM `Module` definitions and maintain integrity+  (e.g. roughly equivalent to `llvm-link`).  The `Module` Semigroup instance is+  unsafe and is deprecated (along with the Monoid instance) and scheduled for+  removal.  Note that `llvmModuleCombine` does not do significant error detection+  and it's up to the caller to determine that the modules should be combined.+  See the documentation for `llvmModuleCombine` for more details.++* Support LLVM 22:+  * `DICompileUnit'` now has an additional `dicuSourceLanguageVersion :: Word64`+    field.+  * `DIBasicType'` now has an additional `dibtDataSize :: Word32` field.++* Support LLVM 21:+  * Added support for the `DISubrangeType` metadata.+  * Added support for the `DIFixedPointType` metadata.++* Corrected the printing of non-normal single-precision floating point+  constants.++* Added missing `FloatType` case `BFloat` for 16-bit "Brain" floats.++* Added missing `Value'` cases for floating point types:+  * `ValHalf` containing an `FPHalfValue` (a wrapper around `Word16`)+  * `ValBFloat` containing an `FPBFloatValue` (a wrapper around `Word16`)+  * `ValFP128` containing an `FP128Value` (a wrapper around two `Word64`)+  * `ValFP128_PPC` containing an `FP128_PPCValue`+    (a wrapper around two `Double`s)++* Added the `atFileLines` function to `DebugUtils.hs` which retrieves the+  statements associated with a specific source file and line number for user+  processing.++* Added the `ppModuleAtLine` which can be used to pretty-print only the portion+  of the bitcode that is associated with a specific source file and line number+  (this uses the new `atFileLines` function internally).++* Breaking change: Unnamed metadata indexes are in an `UnnamedMdIdx` newtype+  wrapper now instead of being an undecorated `Int`.++## 0.14.0.0 -- 2026-01-23++* Changes to support LLVM 19 (some of these changes are not backward-compatible):+  * Changes to `LayoutSpec` for DataLayout:+    * Size specification fields use a common sub-structure `Storage` which itself+      contains an `Alignment` common sub-structure: `IntegerSize`, `VectorSize`,+      `FloatSize`, and `StackObjSize`.+    * The pointer size specification field uses a `PointerSize` sub-structure+      that itself contains a `Storage` sub-structure.+    * Updated `AggregateSize` to make first field optional (it was dropped in+      LLVM 4) and the remaining fields are now provided via the `Alignment`+      sub-structure.+    * Added `ProgramAddrSpace`, `GlobalAddrSpace`, and `AllocaAddrSpace`+      constructors, each defined via an `AddressSpace` sub-structure.+    * Added `NonIntegralPointerSpaces` to record address spaces with an+      unspecified bitwise representation.+    * Added `GoffMangling`, `WindowsX86CoffMangling`, and `XCoffMangling` forms+      of Mangling.+  * Added `GEPAttr` flags for `GEP` instruction and constant expression, with+    `RangeSpec` for the latter.+  * Added `numExtraInhabitants` to `DIBasicType`.+  * Added support for `DebugRecord` parsing, specifically for:++    * `FUNC_CODE_DEBUG_RECORD_VALUE`+    * `FUNC_CODE_DEBUG_RECORD_DECLARE`+    * `FUNC_CODE_DEBUG_RECORD_ASSIGN`+    * `FUNC_CODE_DEBUG_RECORD_VALUE_SIMPLE`+    * `FUNC_CODE_DEBUG_RECORD_LABEL`++    This adds a new field to both the `Result` and `Effect` constructors of the `Stmt` type.+  * Pretty-printing with an LLVM version >= 19 now generates an error for `icmp`,+    `fcmp`, and `shl` constant expressions that are no longer supported as of+    LLVM 19.+* Changes to cast-related instructions:+  * Add a `Bool` field to `ZExt` which, if `True`, indicates that the+    argument must be non-negative. This is used by LLVM 18 and up.+  * Add a `Bool` field to `UiToFp` which, if `True`, indicates that the+    argument must be non-negative. This is used by LLVM 19 and up.+  * Add `Bool` fields to `Trunc` to check if the truncation would cause+    unsigned or signed overflow. These are used by LLVM 20 and up.+* Add a `Bool` field to `ICmp`, which indicates that the arguments must have+  the same sign. This is used by LLVM 20 and up.+* Add `dlAtomGroup` and `dlAtomRank` fields to `DebugLoc'`, which were+  introduced in LLVM 21.+* Add `dilColumn`, `dilIsArtificial`, and `dilCoroSuspendIdx` fields to+  `DILabel'`, which were introduced in LLVM 21.+* The following debug-related fields have had their types changed from `Word64`+  to `Maybe (ValMd' lab)`:++  * `DIBasicType'`: `dibtSize`+  * `DICompositeType'`: `dictSize` and `dictOffset`+  * `DIDerivedType'`: `didtSize` and `didtOffset`++  This allows them to encode non-constant sizes and offsets (a capability used+  by Ada, for instance) in LLVM 21 or later.+* Added the `bbStmtModifier` which can be used to modify individual statements as+  they are emitted from the top-level instruction generator functions (e.g to add+  Debug Metadata to each statement).+* Fix a bug that would cause `indirectbr` statements to be pretty-printed+  incorrectly.++## 0.13.1.0 (October 2025)++* Add a `FunctionPointerAlign` constructor to `LayoutSpec`.++## 0.13.0.0 (March 2025)++* Changed some of the signatures of helper functions in the AST to make them more+  flexible by using `Type' ident` rather than `Type` in their signatures (the+  latter fixes `ident` to be `Ident`). Changed functions: `isAlias`,+  `isPrimTypeOf`, `isVector`, `isVectorOf`, `isArray`, and `isPointer`.+ ## 0.12.1.0 (August 2024)  * Fix for printing NaN and infinite floating point values.
README.md view
@@ -7,7 +7,12 @@  ## LLVM language feature support -Currently, `llvm-pretty` supports LLVM versions up through 17. As a result of+<!--+If you update the latest LLVM version mentioned below, make sure to also update+the definition of `llvmVlatest` in `Text.LLVM.PP`.+-->++Currently, `llvm-pretty` supports LLVM versions up through 22. As a result of the broad version coverage, the `llvm-pretty` AST is a superset of all versions of the LLVM AST. This means that the manner in which certain information is presented in the `llvm-pretty` AST (e.g., during pretty printing) will be
llvm-pretty.cabal view
@@ -1,6 +1,7 @@ Cabal-version:       2.2 Name:                llvm-pretty-Version:             0.12.1.0+Version:             0.15.0.0+                     -- TODO: needs to be 0.15 due to UnnamedMdIdx newtype wrapper License:             BSD-3-Clause License-file:        LICENSE Author:              Trevor Elliott@@ -13,7 +14,7 @@   Augustsson.  The library provides a monadic interface to a pretty printer,   that allows functions to be defined and called, generating the corresponding   LLVM assembly when run.-tested-with:         GHC==8.4.3, GHC==8.2.2, GHC==8.0.2+tested-with:         GHC==9.12.2, GHC==9.10.1, GHC==9.8.4 extra-doc-files:     CHANGELOG.md, README.md  @@ -25,6 +26,7 @@   Default-language:    Haskell2010   Ghc-options:     -Wall+    -fhide-source-paths  Library   Import:              common@@ -37,6 +39,7 @@                        Text.LLVM.Lens                        Text.LLVM.Parser                        Text.LLVM.PP+                       Text.LLVM.Combine                        Text.LLVM.DebugUtils                        Text.LLVM.Triple                        Text.LLVM.Triple.AST@@ -48,20 +51,26 @@    Build-depends:       base             >= 4.11 && < 5,                        containers       >= 0.4,+                       filepath         >= 1.4,                        parsec           >= 3,                        pretty           >= 1.0.1,                        monadLib         >= 3.6.1,                        microlens        >= 0.4,                        microlens-th     >= 0.4,+                       microlens-platform >= 0.4,+                       -- microlens-platform provides the IxValue instance for IntMap                        syb              >= 0.7,                        template-haskell >= 2.7,-                       th-abstraction   >= 0.3.1 && <0.7+                       th-abstraction   >= 0.3.1 && <0.8  Test-suite llvm-pretty-test   Import: common   Type: exitcode-stdio-1.0   Main-is: Main.hs   Other-modules:+    CombineTests+    DataLayout+    Metadata     Output     Triple     TQQDefs@@ -71,6 +80,7 @@   Build-depends:     llvm-pretty,     base,+    microlens,     pretty,     tasty,     tasty-hunit,
src/Text/LLVM.hs view
@@ -40,6 +40,8 @@      -- * Basic Blocks   , BB()+  , runBB+  , bbStmtModifier   , freshLabel   , label   , comment@@ -139,9 +141,41 @@ -- LLVM Monad ------------------------------------------------------------------  newtype LLVM a = LLVM-  { unLLVM :: WriterT Module (StateT Names Id) a+  { unLLVM :: WriterT ModuleBuilder (StateT Names Id) a   } deriving (Functor,Applicative,Monad,MonadFix) ++-- | This is an internal object used to provide the Monoid/Semigroup building+-- context for the WriterT.  There is no Semigroup instance for Module itself,+-- because combining modules is not a trivial operation and it can fail+-- (e.g. duplicate symbols/definitions); see the 'LLVM.Combine' module for a+-- proper link-like combining function.  However, the functionality here is not+-- really combining two modules, but instead is constructing a single module from+-- discrete operations and thus we can use the ModuleBuilder newtype wrapper to+-- allow Monoid/Semigroup functionality under this LLVM monad.++newtype ModuleBuilder = ModuleBuilder { getModule :: Module }++instance Semigroup ModuleBuilder where+  (ModuleBuilder m1) <> (ModuleBuilder m2) = ModuleBuilder $ Module+    { modSourceName = modSourceName m1 `mplus` modSourceName m2+    , modTriple = modTriple m1 <> modTriple m2+    , modDataLayout = modDataLayout m1 <> modDataLayout m2+    , modTypes = modTypes m1 <> modTypes m2+    , modUnnamedMd = modUnnamedMd m1 <> modUnnamedMd m2+    , modNamedMd = modNamedMd m1 <> modNamedMd m2+    , modGlobals = modGlobals m1 <> modGlobals m2+    , modDeclares = modDeclares m1 <> modDeclares m2+    , modDefines = modDefines m1 <> modDefines m2+    , modInlineAsm = modInlineAsm m1 <> modInlineAsm m2+    , modAliases = modAliases m1 <> modAliases m2+    , modComdat = modComdat m1 <> modComdat m2+    }++instance Monoid ModuleBuilder where+  mempty = ModuleBuilder emptyModule++ freshNameLLVM :: String -> LLVM String freshNameLLVM pfx = LLVM $ do   ns <- get@@ -150,24 +184,24 @@   return n  runLLVM :: LLVM a -> (a,Module)-runLLVM  = fst . runId . runStateT Map.empty . runWriterT . unLLVM+runLLVM  = fmap getModule . fst . runId . runStateT Map.empty . runWriterT . unLLVM  emitTypeDecl :: TypeDecl -> LLVM ()-emitTypeDecl td = LLVM (put emptyModule { modTypes = [td] })+emitTypeDecl td = LLVM (put $ ModuleBuilder $ emptyModule { modTypes = [td] })  emitGlobal :: Global -> LLVM (Typed Value) emitGlobal g =-  do LLVM (put emptyModule { modGlobals = [g] })+  do LLVM (put $ ModuleBuilder $ emptyModule { modGlobals = [g] })      return (ptrT (globalType g) -: globalSym g)  emitDefine :: Define -> LLVM (Typed Value) emitDefine d =-  do LLVM (put emptyModule { modDefines = [d] })+  do LLVM (put $ ModuleBuilder $ emptyModule { modDefines = [d] })      return (defFunType d -: defName d)  emitDeclare :: Declare -> LLVM (Typed Value) emitDeclare d =-  do LLVM (put emptyModule { modDeclares = [d] })+  do LLVM (put $ ModuleBuilder $ emptyModule { modDeclares = [d] })      return (decFunType d -: decName d)  alias :: Ident -> Type -> LLVM ()@@ -313,9 +347,36 @@ -- Basic Block Monad -----------------------------------------------------------  newtype BB a = BB-  { unBB :: WriterT [BasicBlock] (StateT RW Id) a+  { unBB :: ReaderT (Stmt -> Stmt) (WriterT [BasicBlock] (StateT RW Id)) a   } deriving (Functor,Applicative,Monad,MonadFix) ++-- | The 'bbStmtModifier' function can be used to register a function that can+-- modify the subsequent statements generated into this block.+--+-- For example, the following 'BB' monad code segment will emit a couple of LLVM+-- statements:+--+-- > v <- load (iT 8) globalVar Nothing+-- > call fooFunc [v]+-- > jump end+--+-- But these statements will be \"plain\" in the resulting 'BasicBlock'.  If the+-- caller wishes to add debug Metadata for location, they could instead write:+--+-- > bbStmtModifier (extendMetadata ("dbg", ValMdRef i))+-- >  v <- load (iT 8) globalVar Nothing+-- > bbStmtModifier (extendMetadata ("dbg", ValMdRef j))+-- > call fooFunc [v]+-- > jump end+--+-- Where @i@ and @j@ are the metadata index values of the 'DebugLoc' entries+-- describing the source location of the \"load\" and \"call\"+\"jump\" statements,+-- respectively.++bbStmtModifier :: (Stmt -> Stmt) -> BB a -> BB a+bbStmtModifier stmtModifier = BB . local stmtModifier . unBB+ avoidName :: String -> BB () avoidName name = BB $ do   rw <- get@@ -332,7 +393,7 @@  runBB :: BB a -> (a,[BasicBlock]) runBB m =-  case runId (runStateT emptyRW (runWriterT (unBB body))) of+  case runId (runStateT emptyRW (runWriterT (runReaderT id (unBB body)))) of     ((a,bbs),_rw) -> (a,bbs)   where   -- make sure that the last block is terminated@@ -366,17 +427,18 @@ emitStmt stmt = do   BB $ do     rw <- get-    set $! rw { rwStmts = rwStmts rw Seq.|> stmt }+    smod <- ask+    set $! rw { rwStmts = rwStmts rw Seq.|> smod stmt }   when (isTerminator (stmtInstr stmt)) terminateBasicBlock  effect :: Instr -> BB ()-effect i = emitStmt (Effect i [])+effect i = emitStmt (Effect i mempty [])  observe :: Type -> Instr -> BB (Typed Value) observe ty i = do   name <- freshNameBB "r"   let res = Ident name-  emitStmt (Result res i [])+  emitStmt (Result res i mempty [])   return (Typed ty (ValIdent res))  @@ -507,8 +569,8 @@   rw <- BB get   case Seq.viewr (rwStmts rw) of -    stmts Seq.:> Result _ i m ->-      do BB (set rw { rwStmts = stmts Seq.|> Result r i m })+    stmts Seq.:> Result _ i d m ->+      do BB (set rw { rwStmts = stmts Seq.|> Result r i d m })          return (const (ValIdent r) `fmap` tv)      _ -> error "assign: invalid argument"@@ -639,10 +701,10 @@ convop k a ty = observe ty (k (toValue `fmap` a) ty)  trunc :: IsValue a => Typed a -> Type -> BB (Typed Value)-trunc  = convop (Conv Trunc)+trunc  = convop (Conv (Trunc False False))  zext :: IsValue a => Typed a -> Type -> BB (Typed Value)-zext  = convop (Conv ZExt)+zext  = convop (Conv (ZExt False))  sext :: IsValue a => Typed a -> Type -> BB (Typed Value) sext  = convop (Conv SExt)@@ -660,7 +722,7 @@ fptosi  = convop (Conv FpToSi)  uitofp :: IsValue a => Typed a -> Type -> BB (Typed Value)-uitofp  = convop (Conv UiToFp)+uitofp  = convop (Conv (UiToFp False))  sitofp :: IsValue a => Typed a -> Type -> BB (Typed Value) sitofp  = convop (Conv SiToFp)@@ -675,7 +737,7 @@ bitcast  = convop (Conv BitCast)  icmp :: (IsValue a, IsValue b) => ICmpOp -> Typed a -> b -> BB (Typed Value)-icmp op l r = observe (iT 1) (ICmp op (toValue `fmap` l) (toValue r))+icmp op l r = observe (iT 1) (ICmp False op (toValue `fmap` l) (toValue r))  fcmp :: (IsValue a, IsValue b) => FCmpOp -> Typed a -> b -> BB (Typed Value) fcmp op l r = observe (iT 1) (FCmp op (toValue `fmap` l) (toValue r))@@ -695,13 +757,13 @@  getelementptr :: IsValue a               => Type -> Typed a -> [Typed Value] -> BB (Typed Value)-getelementptr ty ptr ixs = observe ty (GEP False ty (toValue `fmap` ptr) ixs)+getelementptr ty ptr ixs = observe ty (GEP [] ty (toValue `fmap` ptr) ixs)  -- | Emit a call instruction, and generate a new variable for its result. call :: IsValue a => Typed a -> [Typed Value] -> BB (Typed Value) call sym vs = case typedType sym of-  ty@(PtrTo (FunTy rty _ _)) -> observe rty (Call False ty (toValue sym) vs)-  _                          -> error "invalid function type given to call"+  PtrTo ty@(FunTy rty _ _) -> observe rty (Call False ty (toValue sym) vs)+  _                        -> error "invalid function type given to call"  -- | Emit a call instruction, but don't generate a new variable for its result. call_ :: IsValue a => Typed a -> [Typed Value] -> BB ()
src/Text/LLVM/AST.hs view
@@ -9,12 +9,17 @@ incomplete: there are some values that new LLVM versions would accept but are not yet represented here. -}++{-# LANGUAGE CPP #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE DeriveDataTypeable, DeriveFunctor, DeriveGeneric #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveLift #-}+ module Text.LLVM.AST   ( -- * Modules     Module(..)@@ -22,12 +27,20 @@     -- * Named Metadata   , NamedMd(..)     -- * Unnamed Metadata-  , UnnamedMd(..)+  , UnnamedMd(..), UnnamedMdIdx(UnnamedMdIdx, unnamedMdIdx)+  , nonNullUnnamedMdIdx+  , nextUnnamedMdIdx     -- * Aliases   , GlobalAlias(..)     -- * Data Layout   , DataLayout   , LayoutSpec(..)+  , Alignment(..)+  , FunctionPointerAlignType(..)+  , Storage(..)+  , PointerSize(..)+  , AddressSpace+  , NumBits   , Mangling(..)   , parseDataLayout     -- * Inline Assembly@@ -116,7 +129,11 @@   , FCmpOp(..)     -- * Values   , Value'(..), Value+  , FPHalfValue(..)+  , FPBFloatValue(..)   , FP80Value(..)+  , FP128Value(..)+  , FP128_PPCValue(..)   , ValMd'(..), ValMd   , KindMd   , FnMdAttachments@@ -131,8 +148,12 @@   , stmtInstr   , stmtMetadata   , extendMetadata+  , addDebugRecord     -- * Constant Expressions   , ConstExpr'(..), ConstExpr+  , GEPAttr(..)+  , orderedGEPAttrs+  , RangeSpec(RangeIndex, Range)     -- * DWARF Debug Info   , DebugInfo'(..), DebugInfo   , DILabel, DILabel'(..)@@ -146,10 +167,12 @@   , DwarfVirtuality   , DIFlags   , DIEmissionKind-  , DIBasicType(..)+  , DIBasicType'(..), DIBasicType   , DICompileUnit'(..), DICompileUnit   , DICompositeType'(..), DICompositeType   , DIDerivedType'(..), DIDerivedType+  , DIFixedPointType'(..), DIFixedPointType+  , DIFixedPointKind'(..), DIFixedPointKind   , DIExpression(..)   , DIFile(..)   , DIGlobalVariable'(..), DIGlobalVariable@@ -159,8 +182,16 @@   , DILocalVariable'(..), DILocalVariable   , DISubprogram'(..), DISubprogram   , DISubrange'(..), DISubrange+  , DISubrangeType'(..), DISubrangeType   , DISubroutineType'(..), DISubroutineType   , DIArgList'(..), DIArgList+  , dwarf_DW_APPLE_ENUM_KIND_invalid+  , DebugRecord, DebugRecord'(..)+  , DbgRecAssign, DbgRecAssign'(..)+  , DbgRecDeclare, DbgRecDeclare'(..)+  , DbgRecLabel, DbgRecLabel'(..)+  , DbgRecValueSimple, DbgRecValueSimple'(..)+  , DbgRecValue, DbgRecValue'(..)     -- * Aggregate Utilities   , IndexResult(..)   , isInvalid@@ -172,18 +203,19 @@   , resolveValueIndex   ) where -import Data.Functor.Identity (Identity(..))+import Control.Monad (MonadPlus(mzero,mplus),(<=<),guard)+import Data.Bits ( complement ) import Data.Coerce (coerce) import Data.Data (Data)-import Data.Typeable (Typeable)-import Control.Monad (MonadPlus(mzero,mplus),(<=<),guard)-import Data.Int (Int32,Int64)+import Data.Functor.Identity (Identity(..)) import Data.Generics (everywhere, extQ, mkT, something)+import Data.Int (Int32,Int64) import Data.List (genericIndex,genericLength) import qualified Data.Map as Map import Data.Maybe (isJust) import Data.Semigroup as Sem import Data.String (IsString(fromString))+import Data.Typeable (Typeable) import Data.Word (Word8,Word16,Word32,Word64) import GHC.Generics (Generic, Generic1) import Language.Haskell.TH.Syntax (Lift)@@ -209,10 +241,15 @@   , modDefines    :: [Define]      -- ^ internal function declarations (with definitions)   , modInlineAsm  :: InlineAsm   , modAliases    :: [GlobalAlias]-  } deriving (Data, Eq, Ord, Generic, Show, Typeable)+  } deriving (Data, Eq, Ord, Generic, Show)  -- | Combines fields pointwise.-instance Sem.Semigroup Module where+instance+#if __GLASGOW_HASKELL__ >= 910+  -- Deprecation of instances was added in GHC 9.10+ {-# DEPRECATED "Unsafe! Scheduled for removal: use llvmModuleCombine instead" #-}+#endif+  Sem.Semigroup Module where   m1 <> m2 = Module     { modSourceName = modSourceName m1 `mplus`   modSourceName m2     , modTriple     = modTriple m1     <> modTriple     m2@@ -228,9 +265,13 @@     , modComdat     = modComdat     m1 <> modComdat     m2     } -instance Monoid Module where+instance+#if __GLASGOW_HASKELL__ >= 910+  -- Deprecation of instances was added in GHC 9.10+  {-# DEPRECATED "Scheduled for removal: use emptyModule instead" #-}+#endif+  Monoid Module where   mempty = emptyModule-  mappend = (<>)  emptyModule :: Module emptyModule  = Module@@ -253,17 +294,46 @@  data NamedMd = NamedMd   { nmName   :: String-  , nmValues :: [Int]-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  , nmValues :: [UnnamedMdIdx]+  } deriving (Data, Eq, Generic, Ord, Show)   -- Unnamed Metadata ------------------------------------------------------------ +-- | This is the type used to represent an unnamed metadata index.  A newtype+-- wrapper is used to distinguish the specific use of this value as this+-- particular type of index.+--+-- The Ord instance is provided to allow these indices to be used as Map keys.+-- Although Num and Enum instances are provided to enable manipulation, care+-- should be taken that these are all very carefully used only where needed and+-- appropriate.  In general, the `nextUnnamedMdIdx` function is preferred.+newtype UnnamedMdIdx = UnnamedMdIdx { unnamedMdIdx :: Int }+  deriving (Data, Eq, Generic, Ord, Enum, Num, Show)++-- | This is used when constructing an AST and a new unnamed metadata element is+-- to be added.  It should be passed the current maximum known index and will+-- return the new, unused index that should be used.+nextUnnamedMdIdx :: UnnamedMdIdx -> UnnamedMdIdx+nextUnnamedMdIdx (UnnamedMdIdx i) = UnnamedMdIdx $ i + 1++-- | In the bitcode, an "optional" unnamed metadata index is indicated by 0 (not+-- present) or the actual index + 1.  The parsing should treat these optional as+-- a different type than an UnnamedMdIdx, but this is not presently detected at+-- the parsing level, so this function is used to convert a parsed UnnamedMdIdx+-- to the option of the correct index.+--+-- Note that it is NOT valid to call this twice (or not at all in the event you+-- are starting with an optional form).+nonNullUnnamedMdIdx :: UnnamedMdIdx -> Maybe UnnamedMdIdx+nonNullUnnamedMdIdx (UnnamedMdIdx i) =+  if i == 0 then Nothing else Just $ UnnamedMdIdx $ i - 1+ data UnnamedMd = UnnamedMd-  { umIndex    :: !Int+  { umIndex    :: !UnnamedMdIdx   , umValues   :: ValMd   , umDistinct :: Bool-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  } deriving (Data, Eq, Generic, Ord, Show)   -- Aliases ---------------------------------------------------------------------@@ -274,38 +344,79 @@   , aliasName       :: Symbol   , aliasType       :: Type   , aliasTarget     :: Value-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  } deriving (Data, Eq, Generic, Ord, Show)   -- Data Layout -----------------------------------------------------------------+-- https://releases.llvm.org/19.1.0/docs/LangRef.html#data-layout  type DataLayout = [LayoutSpec]  data LayoutSpec   = BigEndian   | LittleEndian-  | PointerSize   !Int !Int !Int (Maybe Int) -- ^ address space, size, abi, pref-  | IntegerSize   !Int !Int (Maybe Int) -- ^ size, abi, pref-  | VectorSize    !Int !Int (Maybe Int) -- ^ size, abi, pref-  | FloatSize     !Int !Int (Maybe Int) -- ^ size, abi, pref-  | StackObjSize  !Int !Int (Maybe Int) -- ^ size, abi, pref-  | AggregateSize !Int !Int (Maybe Int) -- ^ size, abi, pref-  | NativeIntSize [Int]-  | StackAlign    !Int -- ^ size+  | PointerSize PointerSize+  | IntegerSize Storage+  | VectorSize Storage+  | FloatSize Storage+  | StackObjSize  Storage+  | AggregateSize (Maybe Int) !Alignment -- n.b. first Int present pre-LLVM4+  | NativeIntSize [NumBits]+  | StackAlign    !NumBits -- ^ size+  | ProgramAddrSpace !AddressSpace+  | GlobalAddrSpace !AddressSpace+  | AllocaAddrSpace !AddressSpace+  | FunctionPointerAlign !FunctionPointerAlignType !NumBits -- ^ type, abi   | Mangling Mangling-    deriving (Data, Eq, Generic, Ord, Show, Typeable)+  | NonIntegralPointerSpaces [AddressSpace]+    deriving (Data, Eq, Generic, Ord, Show) +data Alignment = Alignment+  { alignABI :: !NumBits+  , alignPreferred :: Maybe NumBits  -- ^ default = alignABI+  }+  deriving (Data, Eq, Generic, Ord, Show)++-- | How should a function pointer be aligned?+data FunctionPointerAlignType+  = IndependentOfFunctionAlign+    -- ^ The alignment of function pointers is independent of the alignment of+    -- functions.+  | MultipleOfFunctionAlign+    -- ^ The alignment of function pointers is a multiple of the explicit+    -- alignment specified on the function.+  deriving (Data, Eq, Enum, Generic, Ord, Show)++data Storage = Storage+  { storageSize :: !NumBits  -- ^ valid range [1,2^24)+  , storageAlignment :: Alignment+  }+  deriving (Data, Eq, Generic, Ord, Show)++data PointerSize = PtrSize+  { ptrAddrSpace :: !AddressSpace+  , ptrStorage :: Storage+  , ptrAddrIndexSize :: Maybe NumBits  -- ^ m.b. <= ptrSize, default = ptrSize+  }+  deriving (Data, Eq, Generic, Ord, Show)++type AddressSpace = Int+type NumBits = Int+ data Mangling = ElfMangling+              | GoffMangling               | MipsMangling               | MachOMangling               | WindowsCoffMangling-                deriving (Data, Eq, Enum, Generic, Ord, Show, Typeable)+              | WindowsX86CoffMangling+              | XCoffMangling+                deriving (Data, Eq, Enum, Generic, Ord, Show)  -- | Parse the data layout string. parseDataLayout :: MonadPlus m => String -> m DataLayout parseDataLayout str =   case parse (pDataLayout <* eof) "<internal>" str of-    Left _err -> mzero+    Left _err -> {- debugging: trace (show err) -} mzero     Right specs -> return specs   where     pDataLayout :: Parser DataLayout@@ -317,11 +428,17 @@          case c of            'E' -> return BigEndian            'e' -> return LittleEndian-           'S' -> StackAlign    <$> pInt-           'p' -> PointerSize   <$> pInt0 <*> pCInt <*> pCInt <*> pPref-           'i' -> IntegerSize   <$> pInt <*> pCInt <*> pPref-           'v' -> VectorSize    <$> pInt <*> pCInt <*> pPref-           'f' -> FloatSize     <$> pInt <*> pCInt <*> pPref  -- size of float, abi-align, pref-align+           'S' -> StackAlign <$> pInt+           'P' -> ProgramAddrSpace <$> pInt -- Added in LLVM7+           'G' -> GlobalAddrSpace <$> pInt -- Added in LLVM11+           'A' -> AllocaAddrSpace <$> pInt -- Added in LLVM11+           'p' -> do as <- pInt <|> return 0+                     st <- char ':' >> pStorage+                     idx <- pCOInt -- Added in LLVM7+                     return $ PointerSize $ PtrSize as st idx+           'i' -> IntegerSize <$> pStorage+           'v' -> VectorSize <$> pStorage+           'f' -> FloatSize <$> pStorage                   -- Note that the data layout specified in the LLVM                   -- BC/IR file is not a directive to the backend, but                   -- is instead an indication of what the particular@@ -336,33 +453,59 @@                   -- (for example) references to LongDoubleWidth and                   -- LongDoubleFormat in                   -- https://github.com/llvm/llvm-project/blob/release_60/clang/lib/Basic/Targets/X86.h-           's' -> StackObjSize  <$> pInt <*> pCInt <*> pPref-           'a' -> AggregateSize <$> pInt <*> pCInt <*> pPref-           'n' -> NativeIntSize <$> sepBy pInt (char ':')-           'm' -> Mangling      <$> (char ':' >> pMangling)+           's' -> StackObjSize <$> pStorage -- Obsoleted in LLVM4+           'a' -> alphaNum >>= \case+             ':' -> AggregateSize Nothing <$> pAlignment+             d   -> AggregateSize <$> (Just <$> pIntWithFirstDigit d)+                    <* char ':' <*> pAlignment+           'F' -> FunctionPointerAlign <$> pFunctionPointerAlignType <*> pInt -- Added in LLVM9+           'm' -> Mangling <$ char ':' <*> pMangling+           'n' -> alphaNum >>= \case+             'i' -> char ':'+                    >> (NonIntegralPointerSpaces <$> sepBy pInt (char ':'))+             d -> do fs <- pIntWithFirstDigit d+                     ss <- char ':' *> sepBy pInt (char ':')+                     return $ NativeIntSize $ fs : ss            _   -> mzero +    pFunctionPointerAlignType :: Parser FunctionPointerAlignType+    pFunctionPointerAlignType =+      do c <- letter+         case c of+           'i' -> return IndependentOfFunctionAlign+           'n' -> return MultipleOfFunctionAlign+           _   -> mzero+     pMangling :: Parser Mangling     pMangling =       do c <- letter          case c of            'e' -> return ElfMangling+           'l' -> return GoffMangling            'm' -> return MipsMangling            'o' -> return MachOMangling            'w' -> return WindowsCoffMangling+           'x' -> return WindowsX86CoffMangling+           'a' -> return XCoffMangling            _   -> mzero +    pAlignment :: Parser Alignment+    pAlignment = Alignment <$> pInt <*> pCOInt++    pStorage :: Parser Storage+    pStorage = Storage <$> pInt <* char ':' <*> pAlignment+     pInt :: Parser Int     pInt = read <$> many1 digit -    pInt0 :: Parser Int-    pInt0 = pInt <|> return 0+    pIntWithFirstDigit :: Char -> Parser Int+    pIntWithFirstDigit d0 = read . (d0:) <$> many digit      pCInt :: Parser Int     pCInt = char ':' >> pInt -    pPref :: Parser (Maybe Int)-    pPref = optionMaybe pCInt+    pCOInt :: Parser (Maybe Int)+    pCOInt = optionMaybe pCInt  -- Inline Assembly ------------------------------------------------------------- @@ -375,12 +518,12 @@                    | ComdatLargest                    | ComdatNoDuplicates                    | ComdatSameSize-    deriving (Data, Eq, Enum, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Enum, Generic, Ord, Show)  -- Identifiers -----------------------------------------------------------------  newtype Ident = Ident String-    deriving (Data, Eq, Generic, Ord, Show, Typeable, Lift)+    deriving (Data, Eq, Generic, Ord, Show, Lift)  instance IsString Ident where   fromString = Ident@@ -388,7 +531,7 @@ -- Symbols ---------------------------------------------------------------------  newtype Symbol = Symbol String-    deriving (Data, Eq, Generic, Ord, Show, Typeable, Lift)+    deriving (Data, Eq, Generic, Ord, Show, Lift)  instance Sem.Semigroup Symbol where   Symbol a <> Symbol b = Symbol (a <> b)@@ -409,16 +552,17 @@   | FloatType FloatType   | X86mmx   | Metadata-    deriving (Data, Eq, Generic, Ord, Show, Typeable, Lift)+    deriving (Data, Eq, Generic, Ord, Show, Lift)  data FloatType   = Half+  | BFloat -- ^ Introduced in LLVM 11   | Float   | Double   | Fp128   | X86_fp80   | PPC_fp128-    deriving (Data, Eq, Enum, Generic, Ord, Show, Typeable, Lift)+    deriving (Data, Eq, Enum, Generic, Ord, Show, Lift)  type Type = Type' Ident @@ -454,7 +598,7 @@     --     -- 'Opaque' should not be confused with 'PtrOpaque', which is a completely     -- separate type with a similar-sounding name.-    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  -- | Applicatively traverse a type, updating or removing aliases. updateAliasesA :: (Applicative f) => (a -> f (Type' b)) -> Type' a -> f (Type' b)@@ -480,11 +624,11 @@ isFloatingPoint (FloatType _) = True isFloatingPoint _             = False -isAlias :: Type -> Bool+isAlias :: Type' ident -> Bool isAlias Alias{} = True isAlias _       = False -isPrimTypeOf :: (PrimType -> Bool) -> Type -> Bool+isPrimTypeOf :: (PrimType -> Bool) -> Type' ident -> Bool isPrimTypeOf p (PrimType pt) = p pt isPrimTypeOf _ _             = False @@ -496,20 +640,20 @@ isInteger Integer{} = True isInteger _         = False -isVector :: Type -> Bool+isVector :: Type' ident -> Bool isVector Vector{} = True isVector _        = False -isVectorOf :: (Type -> Bool) -> Type -> Bool+isVectorOf :: (Type' ident -> Bool) -> Type' ident -> Bool isVectorOf p (Vector _ e) = p e isVectorOf _ _            = False -isArray :: Type -> Bool+isArray :: Type' ident -> Bool isArray ty = case ty of   Array _ _ -> True   _         -> False -isPointer :: Type -> Bool+isPointer :: Type' ident -> Bool isPointer (PtrTo _) = True isPointer PtrOpaque = True isPointer _         = False@@ -610,7 +754,7 @@ data NullResult lab   = HasNull (Value' lab)   | ResolveNull Ident-    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  primTypeNull :: PrimType -> Value' lab primTypeNull (Integer 1)    = ValBool False@@ -619,10 +763,13 @@ primTypeNull _              = ValZeroInit  floatTypeNull :: FloatType -> Value' lab+floatTypeNull Half     = ValHalf $ FPHalf 0+floatTypeNull BFloat   = ValBFloat $ FPBFloat 0 floatTypeNull Float    = ValFloat 0-floatTypeNull Double   = ValDouble 0 -- XXX not sure about this+floatTypeNull Double   = ValDouble 0+floatTypeNull Fp128    = ValFP128 $ FP128_LongDouble 0 0 floatTypeNull X86_fp80 = ValFP80 $ FP80_LongDouble 0 0-floatTypeNull _        = error "must be a float type"+floatTypeNull PPC_fp128 = ValFP128_PPC $ FP128_PPC_DoubleDouble 0 0  typeNull :: Type -> NullResult lab typeNull (PrimType pt) = HasNull (primTypeNull pt)@@ -678,7 +825,7 @@ data TypeDecl = TypeDecl   { typeName  :: Ident   , typeValue :: Type-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  } deriving (Data, Eq, Generic, Ord, Show)   -- Globals ---------------------------------------------------------------------@@ -690,7 +837,7 @@   , globalValue    :: Maybe Value   , globalAlign    :: Maybe Align   , globalMetadata :: GlobalMdAttachments-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  } deriving (Data, Eq, Generic, Ord, Show)  addGlobal :: Global -> Module -> Module addGlobal g m = m { modGlobals = g : modGlobals m }@@ -699,7 +846,7 @@   { gaLinkage    :: Maybe Linkage   , gaVisibility :: Maybe Visibility   , gaConstant   :: Bool-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  } deriving (Data, Eq, Generic, Ord, Show)  emptyGlobalAttrs :: GlobalAttrs emptyGlobalAttrs  = GlobalAttrs@@ -720,7 +867,7 @@   , decVarArgs    :: Bool   , decAttrs      :: [FunAttr]   , decComdat     :: Maybe String-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  } deriving (Data, Eq, Generic, Ord, Show)  -- | The function type of this declaration decFunType :: Declare -> Type@@ -742,7 +889,7 @@   , defBody       :: [BasicBlock]   , defMetadata   :: FnMdAttachments   , defComdat     :: Maybe String-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  } deriving (Data, Eq, Generic, Ord, Show)  defFunType :: Define -> Type defFunType Define { .. } =@@ -783,14 +930,14 @@    | SSPreq    | SSPstrong    | UWTable-  deriving (Data, Eq, Generic, Ord, Show, Typeable)+  deriving (Data, Eq, Generic, Ord, Show)  -- Basic Block Labels ----------------------------------------------------------  data BlockLabel   = Named Ident   | Anon Int-    deriving (Data, Eq, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Generic, Ord, Show)  instance IsString BlockLabel where   fromString str = Named (fromString str)@@ -800,7 +947,7 @@ data BasicBlock' lab = BasicBlock   { bbLabel :: Maybe lab   , bbStmts :: [Stmt' lab]-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type BasicBlock = BasicBlock' BlockLabel @@ -816,41 +963,66 @@  -- Attributes ------------------------------------------------------------------ --- | Symbol Linkage+-- | Symbol 'Linkage' provides information on how the symbol should be handled+-- during linking operations.  See https://llvm.org/docs/LangRef.html for more+-- details on the meanings of these flags. data Linkage   = Private+    -- ^ Only accessible by objects in the current module.  May be renamed during+    -- linking to avoid collisions. Not visible in the object file's symbol table.   | LinkerPrivate   | LinkerPrivateWeak   | LinkerPrivateWeakDefAuto   | Internal+    -- ^ Similar to private but shows up as a local symbol (e.g. C @static@)   | AvailableExternally+    -- ^ External declaration, never defined in the object file. Allows inlining+    -- and other optimizations knowing the symbol exists externally.  May be+    -- discarded at will.  Only allowed on 'Declare', not on 'Define'.   | Linkonce+    -- ^ Merged with globals of the same name during linkage.  Useful for common+    -- inlines, templates, or generated code from translation units that may be+    -- overridden with a more definitive definition later.  May be discarded if+    -- unreferenced.   | Weak+    -- ^ Same as Linkonce but may not be discarded (e.g. C @weak@).   | Common+    -- ^ Similar to "weak", but must have a zero initializer and may not be+    -- marked "constant". Not valid for functions and aliases.   | Appending+    -- ^ Only valid for global variables of "pointer to array" type.  Similar to+    -- section concatenation during linking.  No correspondence to an object file+    -- feature.   | ExternWeak+    -- ^ Semantics follows ELF: object is weak until linked.  If not linked, it+    -- becomes null instead of being undefined.   | LinkonceODR+    -- ^ Like Linkonce, with C++ "one definition rule", meaning it can be inlined and+    -- constants can be folded.   | WeakODR+    -- ^ Like Weak, with C++ "one definition rule", meaning it can be inlined and+    -- constants can be folded.   | External+    -- ^ If none of the others applies, this is externally visible.   | DLLImport   | DLLExport-    deriving (Data, Eq, Enum, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Enum, Generic, Ord, Show)  data Visibility = DefaultVisibility                 | HiddenVisibility                 | ProtectedVisibility-    deriving (Data, Eq, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Generic, Ord, Show)  newtype GC = GC   { getGC :: String-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  } deriving (Data, Eq, Generic, Ord, Show)  -- Typed Things ----------------------------------------------------------------  data Typed a = Typed   { typedType  :: Type   , typedValue :: a-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  instance Foldable Typed where   foldMap f t = f (typedValue t)@@ -920,7 +1092,7 @@     -- ^ * Floating point reminder resulting from floating point division.     --   * The reminder has the same sign as the divident (first parameter). -    deriving (Data, Eq, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Generic, Ord, Show)  isIArith :: ArithOp -> Bool isIArith Add{}  = True@@ -938,7 +1110,7 @@ data UnaryArithOp   = FNeg     -- ^ Floating point negation.-    deriving (Data, Eq, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Generic, Ord, Show)  -- | Binary bitwise operators. data BitOp@@ -975,23 +1147,55 @@   | And   | Or   | Xor-    deriving (Data, Eq, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Generic, Ord, Show)  -- | Conversions from one type to another. data ConvOp-  = Trunc-  | ZExt+  = Trunc Bool Bool+    -- ^ Truncate an integer value to a smaller integer type.+    --+    -- The 'Bool' fields (added in in LLVM 20) encode whether to perform+    -- overflow-related checks:+    --+    -- * First 'Bool': check for unsigned overflow.+    -- * Second 'Bool': check for signed overflow.+    --+    -- If the checks fail, then the result is poisoned.+    --+    -- These fields can only ever 'True' in 'Conv' instructions in LLVM 20 or+    -- later. These fields are always 'False' in 'ConstConv' constant+    -- expressions or if the LLVM version is older than 20.+  | ZExt Bool+    -- ^ Zero extension.+    --+    -- The 'Bool' field (added in LLVM 18) encodes whether to enforce that the+    -- argument is non-negative. If the 'Bool' is 'True' and the argument is+    -- negative, then the result is poisoned.+    --+    -- This field can only ever 'True' in 'Conv' instructions in LLVM 18 or+    -- later. This field is always 'False' in 'ConstConv' constant expressions+    -- or if the LLVM version is older than 18.   | SExt   | FpTrunc   | FpExt   | FpToUi   | FpToSi-  | UiToFp+  | UiToFp Bool+    -- ^ Convert the argument from an unsigned integer to a floating-point+    -- value.+    --+    -- The 'Bool' field (added in LLVM 19) encodes whether to enforce that the+    -- argument is non-negative. If the 'Bool' is 'True' and the argument is+    -- negative, then the result is poisoned.+    --+    -- This field can only ever 'True' in 'Conv' instructions in LLVM 19 or+    -- later. This field is always 'False' in 'ConstConv' constant expressions+    -- or if the LLVM version is older than 19.   | SiToFp   | PtrToInt   | IntToPtr   | BitCast-    deriving (Data, Eq, Enum, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Generic, Ord, Show)  data AtomicRWOp   = AtomicXchg@@ -1011,7 +1215,7 @@   | AtomicFMin  -- ^ Introduced in LLVM 15   | AtomicUIncWrap  -- ^ Introduced in LLVM 16   | AtomicUDecWrap  -- ^ Introduced in LLVM 16-    deriving (Data, Eq, Enum, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Enum, Generic, Ord, Show)  data AtomicOrdering   = Unordered@@ -1020,7 +1224,7 @@   | Release   | AcqRel   | SeqCst-    deriving (Data, Eq, Enum, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Enum, Generic, Ord, Show)  type Align = Int @@ -1129,10 +1333,14 @@          * Middle of basic block.          * Effect. -} -  | ICmp ICmpOp (Typed (Value' lab)) (Value' lab)+  | ICmp Bool ICmpOp (Typed (Value' lab)) (Value' lab)     {- ^ * Compare two integral values.          * Middle of basic block.-         * Returns a boolean value. -}+         * Returns a boolean value.+         * The 'Bool' field (added in LLVM 20) encodes whether to enforce that+           the arguments have the same sign. If the 'Bool' is 'True' and the+           arguments have mismatched signs, then the result is poisoned. This+           field is always 'False' if the LLVM version is older than 20. -}    | FCmp FCmpOp (Typed (Value' lab)) (Value' lab)     {- ^ * Compare two floating point values.@@ -1145,16 +1353,19 @@          * Middle of basic block.          * Returns a value of the specified type. -} -  | GEP Bool Type (Typed (Value' lab)) [Typed (Value' lab)]+  | GEP [GEPAttr] Type (Typed (Value' lab)) [Typed (Value' lab)]     {- ^ * "Get element pointer",             compute the address of a field in a structure:-            inbounds check (value poisoned if this fails);+            inbounds check attr (value poisoned if this fails);             type to use as a basis for calculations;             pointer to parent structure;             path to a sub-component of a structure.          * Middle of basic block.          * Returns the address of the requested member. +    It's recommended that the GEPAttr list should be normalized (i.e. only one of+    each entry).+     The types in path are the types of the index, not the fields.      The indexes are in units of fields (i.e., the first element in@@ -1196,7 +1407,16 @@     | ShuffleVector (Typed (Value' lab)) (Value' lab) (Typed (Value' lab))-+    {- ^ * Constructs a fixed permutation of two input vectors: the first+           and second arguments are input vectors, and the third argument+           is the mask.+         * Middle of basic block.+         * Returns the permuted vector. For each element, the mask selects+           an element from one of the input vectors to copy to the result:+            * non-negative mask values represent an index into the concatenated+              pair of input vectors, and+            * -1 mask value indicates that the output element is poison.+    -}    | Jump lab     {- ^ * Jump to the given basic block.@@ -1208,6 +1428,16 @@          * Ends basic block. -}    | Invoke Type (Value' lab) [Typed (Value' lab)] lab lab+    {- ^ * Calls the specified target function, then branches to the success+           label.  If an exception occurs during the call, the exception unwind+           handling branches to the second label.+         * Arguments:+           1. The function's return type+           2. The function target itself (to be called)+           3. arguments to the function+           4. successful return target label+           5. on-exception unwind target label+         * Ends basic block. -}    | Comment String     -- ^ Comment@@ -1217,31 +1447,58 @@    | Unwind   | VaArg (Typed (Value' lab)) Type+    -- ^ Accesses arguments passed through \"varargs\" areas of a function call.+    -- The argument is a @va_list*@; this instruction returns the value of the+    -- specified type located at the target and increments the pointer.+   | IndirectBr (Typed (Value' lab)) [lab]+    -- ^ Branch via pointer indirection.  The argument is the address of the+    -- label to jump to.  (All) Possible destination targets are provided.    | Switch (Typed (Value' lab)) lab [(Integer,lab)]-    {- ^ * Multi-way branch: the first value determines the direction-           of the branch, the label is a default direction, if the value-           does not appear in the jump table, the last argument is the-           jump table.+    {- ^ * Multi-way branch: the first value determines the target index+           for the jump, which is looked up in the third argument table+           (key values are unique).  The second argument is the default+           destination if the target is not found in the table.          * Ends basic block. -}    | LandingPad Type (Maybe (Typed (Value' lab))) Bool [Clause' lab]+    {- ^ Target of an exception (from the 'Invoke' instruction).+         * Arguments:+           1. The result type (the values set by the personality function+              on re-entry to the function).+           2. The second argument may be the personality function, which defines+              values on re-entry. This is used in older LLVM versions and is+              not supplied for recent LLVM versions.+           3. True if this block is a "cleanup".+           4. The list of clauses to handle the exception;  the clauses are+              used to match the exception thrown.+         * If no clause matches and cleanup not set, continue unwinding up+           the stack (see 'Resume').+         * If cleanup is false, there must be at least one clause+     -}    | Resume (Typed (Value' lab))+    {- ^ Resumes propagation of an in-flight exception whose unwinding was+         interrupted by a 'LandingPad' instruction.+         * Argument: the value of the exception to propagate.+    -}    | Freeze (Typed (Value' lab))     {- ^ * Used to stop propagation of @undef@ and @poison@ values.+         * If the argument is @undef@ or @poison@, returns an arbitrary+           (but fixed) value of that type instead, otherwise a no-op and+           returns its argument.          * Middle of basic block. -} -    deriving (Data, Eq, Functor, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Functor, Generic, Ord, Show)  type Instr = Instr' BlockLabel  data Clause' lab   = Catch  (Typed (Value' lab))   | Filter (Typed (Value' lab))-    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type Clause = Clause' BlockLabel @@ -1271,23 +1528,101 @@  -- | Integer comparison operators. data ICmpOp = Ieq | Ine | Iugt | Iuge | Iult | Iule | Isgt | Isge | Islt | Isle-    deriving (Data, Eq, Enum, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Enum, Generic, Ord, Show)  -- | Floating-point comparison operators. data FCmpOp = Ffalse  | Foeq | Fogt | Foge | Folt | Fole | Fone             | Ford    | Fueq | Fugt | Fuge | Fult | Fule | Fune             | Funo    | Ftrue-    deriving (Data, Eq, Enum, Generic, Ord, Show, Typeable)+    deriving (Data, Eq, Enum, Generic, Ord, Show)  +-- Debug Instructions ----------------------------------------------------------++-- | Debug Instructions+--+-- In LLVM 19, debug instructions were added as a replacement for the intrinsic+-- functions previously used.  This addition is described in+-- llvm-project/llvm/docs/RemoveDIsDebugInfo.md in the LLVM repository.+data DebugRecord' lab+  = DebugRecordValue (DbgRecValue' lab)+  | DebugRecordDeclare (DbgRecDeclare' lab)+  | DebugRecordAssign (DbgRecAssign' lab)+  | DebugRecordValueSimple (DbgRecValueSimple' lab)+  | DebugRecordLabel (DbgRecLabel' lab)+  deriving (Data, Eq, Functor, Generic, Ord, Show)++type DebugRecord = DebugRecord' BlockLabel++data DbgRecValue' lab = DbgRecValue+  {+    drvLocation :: ValMd' lab -- ^ Expected to be a DILocation+  , drvLocalVariable :: ValMd' lab -- ^ Expected to be a DILocalVariable+  , drvExpression :: ValMd' lab -- ^ Expected to be a DIExpression+  , drvValAsMetadata :: ValMd' lab+  }+  deriving (Data, Eq, Functor, Generic, Ord, Show)++type DbgRecValue = DbgRecValue' BlockLabel++data DbgRecValueSimple' lab = DbgRecValueSimple+  {+    drvsLocation :: ValMd' lab -- ^ Expected to be a DILocation+  , drvsLocalVariable :: ValMd' lab -- ^ Expected to be a DILocalVariable+  , drvsExpression :: ValMd' lab -- ^ Expected to be a DIExpression+  , drvsValue :: Typed (Value' lab)+  }+  deriving (Data, Eq, Functor, Generic, Ord, Show)++type DbgRecValueSimple = DbgRecValueSimple' BlockLabel++data DbgRecDeclare' lab = DbgRecDeclare+  {+    drdLocation :: ValMd' lab -- ^ Expected to be a DILocation+  , drdLocalVariable :: ValMd' lab -- ^ Expected to be a DILocalVariable+  , drdExpression :: ValMd' lab -- ^ Expected to be a DIExpression+  , drdValAsMetadata :: ValMd' lab+  }+  deriving (Data, Eq, Functor, Generic, Ord, Show)++type DbgRecDeclare = DbgRecDeclare' BlockLabel++data DbgRecAssign' lab = DbgRecAssign+  {+    draLocation :: ValMd' lab -- ^ Expected to be a DILocation+  , draLocalVariable :: ValMd' lab -- ^ Expected to be a DILocalVariable+  , draExpression :: ValMd' lab -- ^ Expected to be a DIExpression+  , draValAsMetadata :: ValMd' lab+  , draAssignID :: ValMd' lab -- ^ Expected to be a DIAssignID+  , draExpressionAddr :: ValMd' lab -- ^ Expected to be a DIExpression+  , draValAsMetadataAddr :: ValMd' lab+  }+  deriving (Data, Eq, Functor, Generic, Ord, Show)++type DbgRecAssign = DbgRecAssign' BlockLabel++data DbgRecLabel' lab = DbgRecLabel+  {+    drlLocation :: ValMd' lab -- ^ Expected to be a DILocation+  , drlLabel :: ValMd' lab -- ^ Expected to be a DILabel+  }+  deriving (Data, Eq, Functor, Generic, Ord, Show)++type DbgRecLabel = DbgRecLabel' BlockLabel++ -- Values ----------------------------------------------------------------------  data Value' lab   = ValInteger Integer   | ValBool Bool+  | ValHalf FPHalfValue+  | ValBFloat FPBFloatValue   | ValFloat Float   | ValDouble Double   | ValFP80 FP80Value+  | ValFP128 FP128Value+  | ValFP128_PPC FP128_PPCValue   | ValIdent Ident   | ValSymbol Symbol   | ValNull@@ -1303,21 +1638,44 @@   | ValAsm Bool Bool String String   | ValMd (ValMd' lab)   | ValPoison-    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type Value = Value' BlockLabel +-- | 16-bit half-precision floating point value (IEEE half)+data FPHalfValue = FPHalf Word16+    deriving (Data, Eq, Ord, Generic, Show)++-- | Different 16-bit half-precision floating point value+--   ("Brain" or "bfloat16")+data FPBFloatValue = FPBFloat Word16+    deriving (Data, Eq, Ord, Generic, Show)++-- | x86 80-bit long double floating point value+--   (note that there's also an m86k 80-bit float that's almost but+--   not quite the same) data FP80Value = FP80_LongDouble Word16 Word64-               deriving (Data, Eq, Ord, Generic, Show, Typeable)+    deriving (Data, Eq, Ord, Generic, Show) +-- | IEEE quad-precision long-double floating point value+data FP128Value = FP128_LongDouble Word64 Word64+    deriving (Data, Eq, Ord, Generic, Show)++-- | PowerPC pair-of-doubles floating point value+--   (The value represented is the sum of the two doubles, which+--   normally but not necessarily have exponents chosen so this makes+--   sense.)+data FP128_PPCValue = FP128_PPC_DoubleDouble Double Double+    deriving (Data, Eq, Ord, Generic, Show)+ data ValMd' lab   = ValMdString String   | ValMdValue (Typed (Value' lab))-  | ValMdRef Int+  | ValMdRef UnnamedMdIdx   | ValMdNode [Maybe (ValMd' lab)]   | ValMdLoc (DebugLoc' lab)   | ValMdDebugInfo (DebugInfo' lab)-    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type ValMd = ValMd' BlockLabel @@ -1331,16 +1689,22 @@   , dlScope :: ValMd' lab   , dlIA    :: Maybe (ValMd' lab)   , dlImplicit :: Bool-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  , dlAtomGroup :: Word64 -- ^ Introduced in LLVM 21+  , dlAtomRank :: Word64 -- ^ Introduced in LLVM 21+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DebugLoc = DebugLoc' BlockLabel  isConst :: Value' lab -> Bool isConst ValInteger{}   = True isConst ValBool{}      = True+isConst ValBFloat{}    = True+isConst ValHalf{}      = True isConst ValFloat{}     = True isConst ValDouble{}    = True isConst ValFP80{}      = True+isConst ValFP128{}     = True+isConst ValFP128_PPC{} = True isConst ValConstExpr{} = True isConst ValZeroInit    = True isConst ValNull        = True@@ -1358,36 +1722,66 @@  -- Statements ------------------------------------------------------------------ +-- | Each statement, which can return a value (`Result`) referenced by the+-- `Ident` or else it has no return value (`Effect`).  The statement has a single+-- Instruction, followed by any Debug Records or associated metadata.+--+-- See llvm-project/llvm/docs/RemoveDIsDebugInfo.md for discussion on the+-- [DebugRecord] fields.  Note that DebugRecords and debug intrinsics may not be+-- mixed in a module; the former is new and preferred over the latter.+--+-- Technically, DebugRecords are attached to Instructions, but since there's a+-- 1:1 correspondence between Stmt and Instr, it is cleaner to attach the+-- DebugRecords to the Stmt to keep the Instrs from getting additional+-- complications.+--+-- Each statement may have both Debug Records (2nd-to-last field) and a list of+-- metadata attributes (last field).  As noted above, bitcode file should not mix+-- Debug Records and intrinsics; if Debug Records are used, the metadata+-- attribute list should not contain intrinsics (although it may contain other+-- metadata associated with this statement).+ data Stmt' lab-  = Result Ident (Instr' lab) [(String,ValMd' lab)]-  | Effect (Instr' lab) [(String,ValMd' lab)]-    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  = Result Ident (Instr' lab) [DebugRecord' lab] [(String, ValMd' lab)]+  | Effect (Instr' lab) [DebugRecord' lab] [(String, ValMd' lab)]+    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type Stmt = Stmt' BlockLabel -stmtInstr :: Stmt' lab -> Instr' lab-stmtInstr (Result _ i _) = i-stmtInstr (Effect i _)   = i+stmtMetadata :: Stmt' lab -> [(String, ValMd' lab)]+stmtMetadata = \case+  Result _ _ _ mds -> mds+  Effect _ _ mds   -> mds -stmtMetadata :: Stmt' lab -> [(String,ValMd' lab)]-stmtMetadata stmt = case stmt of-  Result _ _ mds -> mds-  Effect _ mds   -> mds+stmtInstr :: Stmt' lab -> Instr' lab+stmtInstr (Result _ i _ _) = i+stmtInstr (Effect i _ _)   = i -extendMetadata :: (String,ValMd' lab) -> Stmt' lab -> Stmt' lab+extendMetadata :: Show lab => (String, ValMd' lab) -> Stmt' lab -> Stmt' lab extendMetadata md stmt = case stmt of-  Result r i mds -> Result r i (md:mds)-  Effect i mds   -> Effect i (md:mds)+  Result r i [] mds -> Result r i [] (md:mds)+  Result _ _ _ _ -> error $ "Adding MD " <> show md <> " after DebugRecord"+  Effect i drs mds   -> Effect i drs (md:mds) +addDebugRecord :: DebugRecord' lab -> Stmt' lab -> Stmt' lab+addDebugRecord dr = \case+  Result r i drs mds -> Result r i (snoc dr drs) mds+  Effect i drs mds   -> Effect i (snoc dr drs) mds+  where+    snoc e ls = ls <> [e]  -- Constant Expressions --------------------------------------------------------  data ConstExpr' lab-  = ConstGEP Bool (Maybe Word64) Type (Typed (Value' lab)) [Typed (Value' lab)]+  = ConstGEP [GEPAttr] (Maybe RangeSpec) Type (Typed (Value' lab)) [Typed (Value' lab)]   -- ^ Since LLVM 3.7, constant @getelementptr@ expressions include an explicit   -- type to use as a basis for calculations. For older versions of LLVM, this   -- type can be reconstructed by inspecting the pointee type of the parent   -- pointer value.+  --+  -- Since LLVM 19, the bool "inbounds" is now [GEPAttr] and range is via+  -- RangeSpec instead of just Word64.  It's recommended that the GEPAttr list+  -- should be normalized (i.e. only one of each entry).   | ConstConv ConvOp (Typed (Value' lab)) Type   | ConstSelect (Typed (Value' lab)) (Typed (Value' lab)) (Typed (Value' lab))   | ConstBlockAddr (Typed (Value' lab)) lab@@ -1396,14 +1790,67 @@   | ConstArith ArithOp (Typed (Value' lab)) (Value' lab)   | ConstUnaryArith UnaryArithOp (Typed (Value' lab))   | ConstBit BitOp (Typed (Value' lab)) (Value' lab)-    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type ConstExpr = ConstExpr' BlockLabel +-- | Attributes imposing rules on the GEP; violating any rule results in a poison+-- value.  If the base is a vector of pointers, the attributes apply to each+-- computation element-wise.  See+-- https://llvm.org/docs/LangRef.html#getelementptr-instruction for more+-- information.+data GEPAttr+  = GEP_Inbounds+    -- ^ Rules:+    -- * Base pointer has an inbounds (but not necessarily live) address of the+    --   allocated object it is based on (i.e. points into that allocation or to+    --   its end.  Size for a growable allocated object is the max size, not the+    --   current size.+    -- * Pointer must remain inbounds at all times when adding the offsets+    -- * Implies 'GEP_NUSW'+  | GEP_NUSW+    -- ^ No unsigned signed wrap.+    -- Rules:+    -- * If type of index is larger than ptr index type, truncation preserves+    --   the signed value.+    -- * Multiplication of an index by the type size does not wrap in a+    --   signed sense.+    -- * Offset additions (excluding base address) does not wrap in a+    --   signed sense+    -- * Addition of the current address (as unsigned, truncated to ptr+    --   index type) and each offset (as signed) does not wrap the ptr+    --   index type.+  | GEP_NUW+    -- ^ No unsigned wrap+    -- Rules:+    -- * If type of index is larger than ptr index type, truncation preserves+    --   the unsigned value.+    -- * Multiplication of an index by the type size does not wrap in an+    --   unsigned sense.+    -- * Offset additions (excluding base address) does not wrap in an+    --   unsigned sense+    -- * Addition of the current address (as unsigned, truncated to ptr+    --   index type) and each offset (as unsigned) does not wrap the ptr+    --   index type.+    deriving (Data, Eq, Generic, Ord, Show)++orderedGEPAttrs :: [GEPAttr]+orderedGEPAttrs = [GEP_Inbounds, GEP_NUSW, GEP_NUW] -- bit0, bit1, ...++data RangeSpec+  = RangeIndex Word64+    -- ^ index of valid range as used in pre-LLVM19 for when "inbounds" as a+    -- boolean was True.  Deprecated.+  | Range Int Integer Integer+    -- ^ width of arbitrary-precision integer (in bits) and lower and upper+    -- arbitrary-precision integer bounds of that size as [lower, upper).+  deriving (Data, Eq, Generic, Ord, Show)++ -- DWARF Debug Info ------------------------------------------------------------  data DebugInfo' lab-  = DebugInfoBasicType DIBasicType+  = DebugInfoBasicType (DIBasicType' lab)   | DebugInfoCompileUnit (DICompileUnit' lab)   | DebugInfoCompositeType (DICompositeType' lab)   | DebugInfoDerivedType (DIDerivedType' lab)@@ -1425,9 +1872,10 @@   | DebugInfoImportedEntity (DIImportedEntity' lab)   | DebugInfoLabel (DILabel' lab)   | DebugInfoArgList (DIArgList' lab)-  | DebugInfoAssignID-    -- ^ Introduced in LLVM 17.-    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  | DebugInfoAssignID -- ^ Introduced in LLVM 17.+  | DebugInfoSubrangeType (DISubrangeType' lab)+  | DebugInfoFixedPointType (DIFixedPointType' lab)+    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DebugInfo = DebugInfo' BlockLabel @@ -1437,7 +1885,10 @@     , dilName  :: String     , dilFile  :: Maybe (ValMd' lab)     , dilLine  :: Word32-    } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+    , dilColumn :: Word32 -- ^ Introduced in LLVM 21.+    , dilIsArtificial :: Bool -- ^ Introduced in LLVM 21.+    , dilCoroSuspendIdx :: Maybe Word32 -- ^ Introduced in LLVM 21.+    } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DIImportedEntity = DIImportedEntity' BlockLabel data DIImportedEntity' lab = DIImportedEntity@@ -1447,14 +1898,14 @@     , diieFile   :: Maybe (ValMd' lab)     , diieLine   :: Word32     , diieName   :: Maybe String-    } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+    } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DITemplateTypeParameter = DITemplateTypeParameter' BlockLabel data DITemplateTypeParameter' lab = DITemplateTypeParameter     { dittpName      :: Maybe String     , dittpType      :: Maybe (ValMd' lab)     , dittpIsDefault :: Maybe Bool         -- since LLVM 11-    } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+    } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DITemplateValueParameter = DITemplateValueParameter' BlockLabel data DITemplateValueParameter' lab = DITemplateValueParameter@@ -1463,7 +1914,7 @@     , ditvpType      :: Maybe (ValMd' lab)     , ditvpIsDefault :: Maybe Bool         -- since LLVM 11     , ditvpValue     :: ValMd' lab-    } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+    } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DINameSpace = DINameSpace' BlockLabel data DINameSpace' lab = DINameSpace@@ -1471,7 +1922,7 @@     , dinsScope :: ValMd' lab     , dinsFile  :: ValMd' lab     , dinsLine  :: Word32-    } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+    } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  -- TODO: Turn these into sum types -- See https://github.com/llvm-mirror/llvm/blob/release_38/include/llvm/Support/Dwarf.def@@ -1486,15 +1937,45 @@ -- it stabilizes. type DIEmissionKind = Word8 -data DIBasicType = DIBasicType+-- See https://github.com/llvm/llvm-project/commit/eb8901bda11fd55deeecd067fc4c9dcc0fb89984+dwarf_DW_APPLE_ENUM_KIND_invalid :: Word32+dwarf_DW_APPLE_ENUM_KIND_invalid = complement (0 :: Word32) -- ~ LLVM 19++data DIBasicType' lab = DIBasicType   { dibtTag      :: DwarfTag   , dibtName     :: String-  , dibtSize     :: Word64+  , dibtSize     :: Maybe (ValMd' lab)+    -- ^ If using LLVM 20 or older, this will always be @Just@ an 'ValMdValue',+    -- where the underlying value is a 64-bit 'ValInteger'. If using LLVM 21 or+    -- later, this can also be a null reference (i.e., 'Nothing'), a variable+    -- (i.e., @Just@ a 'DIGlobalVariable' or 'DILocalVariable'), or an+    -- expression (i.e., @Just@ a 'DIExpression').   , dibtAlign    :: Word64   , dibtEncoding :: DwarfAttrEncoding   , dibtFlags    :: Maybe DIFlags-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  , dibtNumExtraInhabitants :: Word64 -- ^ added in LLVM 20.+  , dibtDataSize :: Word32 -- ^ added in LLVM 22.+  } deriving (Data, Eq, Functor, Generic, Ord, Show) +type DIBasicType = DIBasicType' BlockLabel++data DISubrangeType' lab = DISubrangeType -- Added in LLVM 21+  { disrtName       :: Maybe String+  , disrtFile       :: Maybe (ValMd' lab)+  , disrtLine       :: Word32+  , disrtScope      :: Maybe (ValMd' lab)+  , disrtBaseType   :: Maybe (ValMd' lab) -- ^ a type+  , disrtSize       :: Maybe (ValMd' lab) -- ^ in bits. signed constant, DIVariable, DIGlobalVariable, or DIExpression+  , disrtAlign      :: Word64 -- ^ in bits+  , disrtFlags      :: DIFlags+  , disrtLowerBound :: Maybe (ValMd' lab) -- ^ signed constant, DIVariable, DIGlobalVariable, or DIExpression+  , disrtUpperBound :: Maybe (ValMd' lab) -- ^ signed constant, DIVariable, DIGlobalVariable, or DIExpression+  , disrtStride     :: Maybe (ValMd' lab) -- ^ signed constant, DIVariable, DIGlobalVariable, or DIExpression+  , disrtBias       :: Maybe (ValMd' lab) -- ^ signed constant, DIVariable, DIGlobalVariable, or DIExpression+  } deriving (Data, Eq, Functor, Generic, Ord, Show)++type DISubrangeType = DISubrangeType' BlockLabel+ data DICompileUnit' lab = DICompileUnit   { dicuLanguage           :: DwarfLang   , dicuFile               :: Maybe (ValMd' lab)@@ -1518,7 +1999,9 @@   , dicuRangesBaseAddress  :: Bool   , dicuSysRoot            :: Maybe String   , dicuSDK                :: Maybe String-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  , dicuSourceLanguageVersion :: Word32+    -- ^ added in LLVM 22+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DICompileUnit = DICompileUnit' BlockLabel @@ -1529,9 +2012,19 @@   , dictLine           :: Word32   , dictScope          :: Maybe (ValMd' lab)   , dictBaseType       :: Maybe (ValMd' lab)-  , dictSize           :: Word64+  , dictSize           :: Maybe (ValMd' lab)+    -- ^ If using LLVM 20 or older, this will always be @Just@ an 'ValMdValue',+    -- where the underlying value is a 64-bit 'ValInteger'. If using LLVM 21 or+    -- later, this can also be a null reference (i.e., 'Nothing'), a variable+    -- (i.e., @Just@ a 'DIGlobalVariable' or 'DILocalVariable'), or an+    -- expression (i.e., @Just@ a 'DIExpression').   , dictAlign          :: Word64-  , dictOffset         :: Word64+  , dictOffset         :: Maybe (ValMd' lab)+    -- ^ If using LLVM 20 or older, this will always be @Just@ an 'ValMdValue',+    -- where the underlying value is a 64-bit 'ValInteger'. If using LLVM 21 or+    -- later, this can also be a null reference (i.e., 'Nothing'), a variable+    -- (i.e., @Just@ a 'DIGlobalVariable' or 'DILocalVariable'), or an+    -- expression (i.e., @Just@ a 'DIExpression').   , dictFlags          :: DIFlags   , dictElements       :: Maybe (ValMd' lab)   , dictRuntimeLang    :: DwarfLang@@ -1543,9 +2036,12 @@   , dictAssociated     :: Maybe (ValMd' lab)   , dictAllocated      :: Maybe (ValMd' lab)   , dictRank           :: Maybe (ValMd' lab)-  , dictAnnotations    :: Maybe (ValMd' lab)-    -- ^ Introduced in LLVM 14.-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  , dictAnnotations    :: Maybe (ValMd' lab) -- ^ Introduced in LLVM 14.+  , dictNumExtraInhabitants :: Word64        -- ^ added in LLVM 20.+  , dictSpecification  :: Maybe (ValMd' lab) -- ^ added in LLVM 20.+  , dictEnumKind       :: Maybe Word32       -- ^ added in LLVM 20.+  , dictBitStride      :: Maybe (ValMd' lab) -- ^ added in LLVM 20.+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DICompositeType = DICompositeType' BlockLabel @@ -1556,9 +2052,19 @@   , didtLine :: Word32   , didtScope :: Maybe (ValMd' lab)   , didtBaseType :: Maybe (ValMd' lab)-  , didtSize :: Word64+  , didtSize :: Maybe (ValMd' lab)+    -- ^ If using LLVM 20 or older, this will always be @Just@ an 'ValMdValue',+    -- where the underlying value is a 64-bit 'ValInteger'. If using LLVM 21 or+    -- later, this can also be a null reference (i.e., 'Nothing'), a variable+    -- (i.e., @Just@ a 'DIGlobalVariable' or 'DILocalVariable'), or an+    -- expression (i.e., @Just@ a 'DIExpression').   , didtAlign :: Word64-  , didtOffset :: Word64+  , didtOffset :: Maybe (ValMd' lab)+    -- ^ If using LLVM 20 or older, this will always be @Just@ an 'ValMdValue',+    -- where the underlying value is a 64-bit 'ValInteger'. If using LLVM 21 or+    -- later, this can also be a null reference (i.e., 'Nothing'), a variable+    -- (i.e., @Just@ a 'DIGlobalVariable' or 'DILocalVariable'), or an+    -- expression (i.e., @Just@ a 'DIExpression').   , didtFlags :: DIFlags   , didtExtraData :: Maybe (ValMd' lab)   , didtDwarfAddressSpace :: Maybe Word32@@ -1568,18 +2074,48 @@   -- space (in LLVM, the sentinel value @0@ is used for this).   , didtAnnotations :: Maybe (ValMd' lab)   -- ^ Introduced in LLVM 14-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DIDerivedType = DIDerivedType' BlockLabel +-- | The DIFixedPointType represents fixed-point types as an integer with a scale+-- factor.  It's a derivation of the DIBaseType, although only two encodings are+-- permitted: DW_ATE_signed_fixed and DW_ATE_unsigned_fixed.+data DIFixedPointType' lab = DIFixedPointType -- Added in LLVM 21+  { difptTag      :: DwarfTag+  , difptName     :: Maybe String+  , difptSize     :: Maybe (ValMd' lab) -- ^ in bits. signed constant, DIVariable, DIGlobalVariable, or DIExpression+  , difptAlign    :: Word64 -- ^ in bits+  , difptEncoding :: DwarfAttrEncoding -- ^ only DW_ATE_signed_fixed or DW_ATE_unsigned_fixed+  , difptFlags    :: DIFlags+  -- n.b. in the bitcode representation, kind, factor, numerator, and denominator+  -- are all present, and kind controls which are actually used.+  , difptKind     :: DIFixedPointKind' lab+  } deriving (Data, Eq, Functor, Generic, Ord, Show)++data DIFixedPointKind' lab = FixedPointBinary Integer+                             -- ^ A binary fixed point type where the (signed)+                             -- scale factor is a power of 2.+                           | FixedPointDecimal Integer+                             -- ^ A decimal fixed point type where the (signed)+                             -- scale factor is a power of 10.+                           | FixedPointRational Integer Integer+                             -- ^ The scale factor is an arbitrary rational+                             -- number, specified by these numerator and+                             -- denominator values.+  deriving (Data, Eq, Functor, Generic, Ord, Show)++type DIFixedPointType = DIFixedPointType' BlockLabel+type DIFixedPointKind = DIFixedPointKind' BlockLabel+ data DIExpression = DIExpression   { dieElements :: [Word64]-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  } deriving (Data, Eq, Generic, Ord, Show)  data DIFile = DIFile   { difFilename  :: FilePath   , difDirectory :: FilePath-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)+  } deriving (Data, Eq, Generic, Ord, Show)  data DIGlobalVariable' lab = DIGlobalVariable   { digvScope                :: Maybe (ValMd' lab)@@ -1595,14 +2131,14 @@   , digvAlignment            :: Maybe Word32   , digvAnnotations          :: Maybe (ValMd' lab)     -- ^ Introduced in LLVM 14.-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DIGlobalVariable = DIGlobalVariable' BlockLabel  data DIGlobalVariableExpression' lab = DIGlobalVariableExpression   { digveVariable   :: Maybe (ValMd' lab)   , digveExpression :: Maybe (ValMd' lab)-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DIGlobalVariableExpression = DIGlobalVariableExpression' BlockLabel @@ -1611,7 +2147,7 @@   , dilbFile   :: Maybe (ValMd' lab)   , dilbLine   :: Word32   , dilbColumn :: Word16-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DILexicalBlock = DILexicalBlock' BlockLabel @@ -1619,7 +2155,7 @@   { dilbfScope         :: ValMd' lab   , dilbfFile          :: Maybe (ValMd' lab)   , dilbfDiscriminator :: Word32-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DILexicalBlockFile = DILexicalBlockFile' BlockLabel @@ -1635,7 +2171,7 @@     -- ^ Introduced in LLVM 4.   , dilvAnnotations :: Maybe (ValMd' lab)     -- ^ Introduced in LLVM 14.-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DILocalVariable = DILocalVariable' BlockLabel @@ -1662,7 +2198,7 @@   , dispThrownTypes    :: Maybe (ValMd' lab)   , dispAnnotations    :: Maybe (ValMd' lab)     -- ^ Introduced in LLVM 14.-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DISubprogram = DISubprogram' BlockLabel @@ -1694,21 +2230,21 @@   , disrLowerBound :: Maybe (ValMd' lab)   , disrUpperBound :: Maybe (ValMd' lab)   , disrStride     :: Maybe (ValMd' lab)-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DISubrange = DISubrange' BlockLabel  data DISubroutineType' lab = DISubroutineType   { distFlags     :: DIFlags   , distTypeArray :: Maybe (ValMd' lab)-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DISubroutineType = DISubroutineType' BlockLabel  -- | See <https://releases.llvm.org/13.0.0/docs/LangRef.html#diarglist>. newtype DIArgList' lab = DIArgList   { dialArgs :: [ValMd' lab]-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)  type DIArgList = DIArgList' BlockLabel @@ -1718,7 +2254,7 @@   = Invalid                             -- ^ An invalid use of GEP   | HasType Type                        -- ^ A resolved type   | Resolve Ident (Type -> IndexResult) -- ^ Continue, after resolving an alias-  deriving (Generic, Typeable)+  deriving (Generic)  isInvalid :: IndexResult -> Bool isInvalid ir = case ir of
+ src/Text/LLVM/Combine.hs view
@@ -0,0 +1,217 @@+{-# LANGUAGE LambdaCase #-}++{- |+Module      : Text.LLVM.Combine+Description : Combine LLVM Modules+License     : BSD3+Maintainer  : Kevin Quick <kquick@galois.com>+Stability   : provisional++This module provides the ability to smash together LLVM 'Module' specifications+to provide the ability to load separate LLVM 'Module's (e.g. bitcode files) and+analyze them as if they had been linked together as a single program.++-}++module Text.LLVM.Combine+  (+    llvmModuleCombine+  )+where++import Data.Bool ( bool )+import Data.Generics.Schemes ( everywhere )+import Data.Generics.Aliases ( mkT )+import Lens.Micro+import Lens.Micro.Extras+import Data.Function ( on )+import Data.List ( find )+import Data.Maybe ( fromMaybe )+import Data.String ( fromString )+import Text.LLVM.AST+import Text.LLVM.Lens+++-- | Combines LLVM 'Module's into a single, composite 'Module'.  This is akin to+-- linking, but just from the perspective of what is needed for program analysis.+--+-- This differs from `llvm-link` in the following known ways:+--+-- 1. The `llvm-link` tool uses structural typing resolution: if two modules+--    each have a type with the same structure, the resulting module will only+--    have one type; the name from one of the modules is chosen and all+--    references to the typename in the other module will be rewritten to the+--    first module.+--+--    The `llvmModuleCombine` function takes a slightly different approach: types+--    are not structurally coalesced, but this means that type names are+--    deconflicted by adding a numbered suffix.  This still requires modifying+--    the type name throughout that module, but (a) there are probably fewer type+--    name conflicts than structural equivalences, and (b) the original name is+--    still part of the new name which maintains origin information.+--+-- 2. The `llvm-link` tool will occasionally rewrite calls to llvm intrinsics to+--    explicitly add the default personality specification.  For example,+--    `llvm.stacksave` may be rewritten to `llvm.stacksave.p0`.  Because these+--    are intrinsics, this should not have any significant impact on the result,+--    but `llvmModuleCombine` does not perform this naming update.+--+-- 3. External declaration resolution is type independent and only name+--    sensitive.  If 'Module' A has an external declaration `declare @f(i32 x)`+--    and 'Module' B has a definition `define @f(float x)`, then this+--    `llvmModuleCombine` operation will use the latter to satisfy the former (by+--    removing the former) even though the types do not match.+--+llvmModuleCombine :: Module -> Module -> Module+llvmModuleCombine a addModule =+  let defs = a ^. modDefinesLens+      decls = a ^. modDeclaresLens+      newDefs = b ^. modDefinesLens+      newDecls = b ^. modDeclaresLens+      rmvDefined = flip (foldr removeDefined)+      newDeclsLessOldDefs = rmvDefined defs newDecls+      oldDeclsLessNewDefs = rmvDefined newDefs decls+      joinedName n = Just $ fromMaybe "..." n <> "+" <> fromMaybe "..." (modSourceName b)+      newUmdBase = let umIdxs = umIndex <$> modUnnamedMd a+                   in bool (succ $ maximum umIdxs) (UnnamedMdIdx 0) $ null umIdxs+      -- unnamed metadata is referenced almost everywhere, so update that globally+      -- first:+      b = updateUmd newUmdBase (deConflictTypes addModule (a ^. modTypesLens))+  in a+     & modSourceNameLens %~ joinedName+     & modDeclaresLens .~ (oldDeclsLessNewDefs <> newDeclsLessOldDefs)+     & modDefinesLens %~ deConflict (b ^. modDefinesLens)+     & modTypesLens <>~ b ^. modTypesLens+     & modUnnamedMdLens <>~ b ^. modUnnamedMdLens+     & modNamedMdLens <>~ b ^. modNamedMdLens+     & modComdatLens <>~ b ^. modComdatLens+     & modGlobalsLens <>~ b ^. modGlobalsLens+     & modInlineAsmLens <>~ b ^. modInlineAsmLens+     & modAliasesLens <>~ b ^. modAliasesLens+  -- TODO Globals any should override Linkage external for the same name+  -- TODO verify modTriple and modDataLayout are the same?+++-- | Rewrites type references in the input module to ensure uniqueness against+-- all types mentioned in the second module.+deConflictTypes :: Module -> [TypeDecl] -> Module+deConflictTypes inpMod existingTypes =+  let resolveTypeConflict m t =+        if any (((==) `on` typeName) t) existingTypes+        then renameType m t (let Ident n = typeName t in n <> "___") (0 :: Int)+        else m+      renameType m t b n =+        let newName = Ident (b <> show n)+        in if any ((newName ==) . typeName) existingTypes+           then if n > 100000+                then error $ "Unable to generate unique type name for " <> b+                else renameType m t b $ succ n+           else everywhere (mkT (chngType (typeName t) newName)) m+      chngType oldName newName n = bool n newName $ n == oldName+  in foldl resolveTypeConflict inpMod (inpMod ^. modTypesLens)+++-- | A 'Define' takes precedence over a 'Declare'.  When combining modules,+-- module A may 'Declare' a function that is handled by a 'Define' in module B,+-- so get rid of the 'Declare' when putting A and B together.++removeDefined :: Define -> [Declare] -> [Declare]+removeDefined def = filter ((def ^. defNameLens /=) . view decNameLens)+++-- | 'Module' A and 'Module' B may have a Define with the same name ('Symbol').+-- This is normal when linking multiple modules together, and is resolved by+-- linkers as guided by the 'Linkage' information for the two 'Definition's,+-- usually by either renaming or merging.++deConflict :: [Define] -> [Define] -> [Define]+deConflict new curr = uncurry (<>) $ foldl deConflictDef (curr, new) new+  where+    deConflictDef (ads, bds) bd =+      case find (((==) `on` defName) bd) ads of+        Nothing -> (ads, bds)+        Just ad -> handle ads bds ad bd+    handle ads bds ad bd =+      case bd ^. defLinkageLens of+        Just Private -> (ads, renameDef bd (view defNameLens <$> ads) bds)+        Just LinkerPrivate -> (ads, renameDef bd (view defNameLens <$> ads) bds)+        Just LinkerPrivateWeak ->+          (ads, renameDef bd (view defNameLens <$> ads) bds) -- ??+        Just LinkerPrivateWeakDefAuto ->+          (ads, renameDef bd (view defNameLens <$> ads) bds) -- ??+        Just Internal -> (ads, renameDef bd (view defNameLens <$> ads) bds)+        Just AvailableExternally ->+          -- Never happen: not allowed on defines.  Ignore+          (ads, bds)+        Just Linkonce -> (mergeDef ad bd ads, removeDef bd bds)+        Just Weak -> (mergeDef ad bd ads, removeDef bd bds)+        Just Common -> (mergeDef ad bd ads, removeDef bd bds)+        Just ExternWeak -> (mergeDef ad bd ads, removeDef bd bds)+        Just LinkonceODR -> (mergeDef ad bd ads, removeDef bd bds)+        Just WeakODR -> (mergeDef ad bd ads, removeDef bd bds)+        Just Appending -> (appendDef ad bd ads, removeDef bd bds)+        Just External ->+          -- This should never happen: it is truly a symbol conflict.  A+          -- linker would reject this, but here we will just preserve the+          -- original.+          (ads, removeDef bd bds)+        Just DLLImport -> (ads, removeDef bd bds) -- ??+        Just DLLExport -> (ads, removeDef bd bds) -- ??+        Nothing ->+          -- No linkage specified.  The default is 'External', with associated+          -- considerations as documented for that case above.+          (ads, removeDef bd bds)++-- Note: Used for Linkonce, Weak, Common, ExternWeak, LinkonceODR, WeakODR. LLVM+-- docs say "merged", but also indicates that maybe there is a replacement+-- instead?  For now, treat "merged" as appending.+mergeDef :: Define -> Define -> [Define] -> [Define]+mergeDef = appendDef++appendDef :: Define -> Define -> [Define] -> [Define]+appendDef d1 d2 =+  let appenD = d1 & defBodyLens <>~ d2 ^. defBodyLens+  in (appenD :) . filter (((/=) `on` defName) d1)++removeDef :: Define -> [Define] -> [Define]+removeDef d = filter (((/=) `on` defName) d)+++-- Renames the Defined symbol to a new name using a discriminator to avoid a+-- conflict.  Only valid for renaming Private/Internal Defines such that changing+-- any reference to the original Symbol to the new Symbol in the provided set of+-- Defines is sufficient to change all references.  Note therefore this excludes:+-- renaming of global variables, changing a GlobalAlias.++renameDef :: Define -> [Symbol] -> [Define] -> [Define]+renameDef toRename known inDefs =+  -- KWQ TODO: needs to change GlobalAlias aliasName?+  --+  let getNewName nm n =+        -- Note: adds a "discriminator" to the name in a way that is valid for+        -- both C functions and C++ mangled names (see+        -- https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-scope).+        let nn = if n < 10+                 then nm <> "_" <> show n+                 else nm <> "__" <> show n <> "_"+        in case find ((fromString nn ==) . defName) inDefs of+             Just _ -> getNewName nm $ succ n+             Nothing ->+               if fromString nn `elem` known+               then getNewName nm $ succ n+               else nn+      (Symbol oldname) = defName toRename+      newName = Symbol $ getNewName oldname (1 :: Integer)+  in changeSym (defName toRename) newName inDefs+++changeSym :: Symbol -> Symbol -> [Define] -> [Define]+changeSym old new = everywhere (mkT chngSym)+  where+    chngSym s = bool s new $ old == s++-- | Adjusts all unnamed metadata indices in the Module to begin at the specified+-- newBase, which allows this module to be combined without conflict with a+-- module whose metadata indices are all below the newBase.+updateUmd :: UnnamedMdIdx -> Module -> Module+updateUmd newBase = everywhere (mkT (\n -> n + newBase))
src/Text/LLVM/DebugUtils.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# Language TransformListComp, MonadComprehensions #-} {- | Module           : Text.LLVM.DebugUtils@@ -32,11 +34,16 @@   -- * Line numbers of definitions   , debugInfoGlobalLines   , debugInfoDefineLines+  , atFileLines+  , AtFileLines(atDefine, atBlockStart, atStmt, atGlobal)+  , DefineRel(..)+  , BlockRel(..)   ) where  import           Control.Applicative    ((<|>)) import           Control.Monad          ((<=<)) import           Data.Bits              (Bits(..))+import           Data.Bool              (bool) import           Data.IntMap            (IntMap) import qualified Data.IntMap as IntMap import           Data.List              (elemIndex, tails, stripPrefix)@@ -44,6 +51,9 @@ import qualified Data.Map    as Map import           Data.Maybe             (fromMaybe, listToMaybe, maybeToList, mapMaybe) import           Data.Word              (Word16, Word64)+import           Lens.Micro.Platform    ((^.), at, _Just, to)+import           System.FilePath        ( (</>), equalFilePath, normalise+                                        , hasTrailingPathSeparator ) import           Text.LLVM.AST  dbgKind :: String@@ -162,22 +172,50 @@  -- | Compute an 'IntMap' of the unnamed metadata in a module mkMdMap :: Module -> IntMap ValMd-mkMdMap m = IntMap.fromList [ (umIndex md, umValues md) | md <- modUnnamedMd m ]+mkMdMap m = IntMap.fromList [ (unnamedMdIdx $ umIndex md, umValues md)+                            | md <- modUnnamedMd m ]  ------------------------------------------------------------------------  getDebugInfo :: MdMap -> ValMd -> Maybe DebugInfo-getDebugInfo mdMap (ValMdRef i)    = getDebugInfo mdMap =<< IntMap.lookup i mdMap+getDebugInfo mdMap (ValMdRef (UnnamedMdIdx i)) =+  getDebugInfo mdMap =<< IntMap.lookup i mdMap getDebugInfo _ (ValMdDebugInfo di) = Just di getDebugInfo _ _                   = Nothing +getMDFile :: MdMap -> ValMd -> Maybe FilePath+getMDFile mdMap = \case+  ValMdDebugInfo (DebugInfoFile i) -> pure $ difDirectory i </> difFilename i+  --  ^^ found it! ^^ or else vvv keep looking (recursively) vvv+  ValMdLoc l -> getMDFile mdMap $ dlScope l+  ValMdRef i -> mdMap ^. at (unnamedMdIdx i) . _Just . to (getMDFile mdMap)+  ValMdDebugInfo (DebugInfoGlobalVariable gv) ->+    (getMDFile mdMap =<< digvFile gv) <|> (getMDFile mdMap =<< digvScope gv)+  ValMdDebugInfo (DebugInfoLocalVariable lv) ->+    (getMDFile mdMap =<< dilvFile lv) <|> (getMDFile mdMap =<< dilvScope lv)+  ValMdDebugInfo (DebugInfoSubprogram sp) ->+    (getMDFile mdMap =<< dispFile sp) <|> (getMDFile mdMap =<< dispScope sp)+  ValMdDebugInfo (DebugInfoLexicalBlock lb) ->+    (getMDFile mdMap =<< dilbFile lb) <|> (getMDFile mdMap =<< dilbScope lb)+  ValMdDebugInfo (DebugInfoLexicalBlockFile lf) ->+    (getMDFile mdMap =<< dilbfFile lf) <|> (getMDFile mdMap $ dilbfScope lf)+  ValMdDebugInfo (DebugInfoDerivedType dt) ->+    (getMDFile mdMap =<< didtFile dt) <|> (getMDFile mdMap =<< didtScope dt)+  ValMdDebugInfo (DebugInfoCompositeType ct) ->+    (getMDFile mdMap =<< dictFile ct) <|> (getMDFile mdMap =<< dictScope ct)+  ValMdDebugInfo (DebugInfoCompileUnit cu) -> getMDFile mdMap =<< dicuFile cu+  ValMdDebugInfo (DebugInfoNameSpace ns) -> getMDFile mdMap $ dinsFile ns+  ValMdDebugInfo (DebugInfoLabel bl) ->+    (getMDFile mdMap =<< dilFile bl) <|> (getMDFile mdMap =<< dilScope bl)+  _ -> Nothing+ getInteger :: MdMap -> ValMd -> Maybe Integer-getInteger mdMap (ValMdRef i)                          = getInteger mdMap =<< IntMap.lookup i mdMap+getInteger mdMap (ValMdRef (UnnamedMdIdx i))           = getInteger mdMap =<< IntMap.lookup i mdMap getInteger _     (ValMdValue (Typed _ (ValInteger i))) = Just i getInteger _     _                                     = Nothing  getList :: MdMap -> ValMd -> Maybe [Maybe ValMd]-getList mdMap (ValMdRef i) = getList mdMap =<< IntMap.lookup i mdMap+getList mdMap (ValMdRef (UnnamedMdIdx i)) = getList mdMap =<< IntMap.lookup i mdMap getList _ (ValMdNode di)   = Just di getList _ _                = Nothing @@ -230,16 +268,28 @@      let bitfield | testBit (didtFlags dt) 19                   , Just extraData      <- didtExtraData dt                   , Just bitfieldOffset <- getInteger mdMap extraData-                  = Just $ BitfieldInfo { biFieldSize      = didtSize dt-                                        , biBitfieldOffset = fromInteger bitfieldOffset-                                        }+                  = do size <- getSizeOrOffset (didtSize dt)+                       Just $ BitfieldInfo { biFieldSize      = size+                                           , biBitfieldOffset = fromInteger bitfieldOffset+                                           }                   | otherwise                   = Nothing+     offset <- getSizeOrOffset (didtOffset dt)      Just (StructFieldInfo { sfiName     = fieldName-                           , sfiOffset   = didtOffset dt+                           , sfiOffset   = offset                            , sfiBitfield = bitfield                            , sfiInfo     = valMdToInfo' mdMap (didtBaseType dt)                            })+  where+    -- TODO: Currently, this only recognizes bare integer (i.e., 'ValInteger')+    -- sizes and offsets. This is likely good enough for Clang-derived LLVM, but+    -- Ada-derived LLVM may contain more complex metadata values that this+    -- currently doesn't handle.+    getSizeOrOffset :: Maybe ValMd -> Maybe Word64+    getSizeOrOffset (Just (ValMdValue tv))+      | ValInteger i <- typedValue tv+      = Just (fromInteger i)+    getSizeOrOffset _ = Nothing   getUnionFields :: MdMap -> DICompositeType -> Maybe [UnionFieldInfo]@@ -365,10 +415,10 @@   where      aux :: [Stmt] -> Map Ident Ident -> Map Ident Ident-    aux ( Effect (Store src dst _ _) _-        : Effect (Call _ _ (ValSymbol (Symbol what)) [var,md,_]) _+    aux ( Effect (Store src dst _ _) _ _+        : Effect (Call _ _ (ValSymbol (Symbol what)) [var,md,_]) _ _         : _) sofar-      | what == "llvm.dbg.declare"+      | what == "llvm.dbg.declare"  -- pre-LLVM19: intrinsic declaration match       , Just dstIdent <- extractIdent dst       , Just srcIdent <- extractIdent src       , Just varIdent <- extractIdent var@@ -376,9 +426,9 @@       , Just name <- extractLvName md       = Map.insert name srcIdent sofar -    aux ( Effect (Call _ _ (ValSymbol (Symbol what)) [var,_,md,_]) _+    aux ( Effect (Call _ _ (ValSymbol (Symbol what)) [var,_,md,_]) _ _         : _) sofar-      | what == "llvm.dbg.value"+      | what == "llvm.dbg.value"  -- pre-LLVM19: intrinsic declaration match       , Just key  <- extractIdent var       , Just name <- extractLvName md       = Map.insert name key sofar@@ -449,7 +499,7 @@     Just (ValMdRef s) -> scopeArgs s     _ -> IntMap.empty   where-    scopeArgs :: Int -> IntMap String+    scopeArgs :: UnnamedMdIdx -> IntMap String     scopeArgs s = IntMap.fromList . mapMaybe go $ modUnnamedMd m       where         go :: UnnamedMd -> Maybe (Int, String)@@ -502,3 +552,96 @@            )          }) = Just (n, (fromIntegral l))     go _ = Nothing+++-- | Given a file and line number and a handler, call the appropriate handler+-- method on every Define, Block, and Stmt that is associated with the line+-- number.+atFileLines :: AtFileLines a b+            => b -> a -> FilePath -> Integer -> Module -> a+atFileLines handle seed file line mdule =+  let mdMap = mkMdMap mdule+      matchesFile x =+        let fn = normalise file+            fl = length fn+            xl = length x+            (p,r) = splitAt (xl - fl) x+        in and [ fl <= xl+               , fn `equalFilePath` r+               , null p || hasTrailingPathSeparator p+               ]+      locMatch di =+        let getMDLine = \case+              ValMdLoc x@(DebugLoc {}) -> dlLine x+              ValMdDebugInfo (DebugInfoSubprogram x) -> dispLine x+              ValMdDebugInfo (DebugInfoLocalVariable x) -> dilvLine x+              ValMdDebugInfo (DebugInfoLexicalBlock x) -> dilbLine x+              ValMdDebugInfo (DebugInfoGlobalVariable x) -> digvLine x+              ValMdDebugInfo (DebugInfoDerivedType x) -> didtLine x+              ValMdDebugInfo (DebugInfoCompositeType x) -> dictLine x+              ValMdDebugInfo (DebugInfoNameSpace x) -> dinsLine x+              ValMdDebugInfo (DebugInfoLabel x) -> dilLine x+              ValMdRef (UnnamedMdIdx i) -> maybe 0 getMDLine $ mdMap ^. at i+              _ -> 0+        in and [ maybe False matchesFile (getMDFile mdMap di)+               , line == (toInteger $ getMDLine di)+               ]+      onGlobal a g =+        bool a (atGlobal handle g a) $ any locMatch $ Map.elems $ globalMetadata g+      onDefs a d =+        let isMatch = any locMatch $ Map.elems $ defMetadata d+            onDecl = bool id (atDefine handle d) isMatch+        in snd $ foldl onBlock (FirstBlock isMatch, onDecl a) $ defBody d+      onBlock (dr, a) bb =+        let onBlockLabel = case bbStmts bb of+                             [] -> id+                             (s:_) -> bool id (atBlockStart handle dr bb)+                                      $ any (locMatch . snd) $ stmtMetadata s+            sseed = (dr, FirstBlockStmt, onBlockLabel a)+            (_, _, blkstmts) = foldl onStmt sseed $ bbStmts bb+        in (OtherBlock, blkstmts)+      onStmt (dr, br, a) s =+        bool+        (dr, FirstLineStmt, a)+        (dr, ContiguousStmt, atStmt handle dr br s a)+        $ or [ any (locMatch . snd) $ stmtMetadata s+             -- n.b. the DebugRecords describe associated data, but do not+             -- (at this time, circa LLVM 22) contain instruction location+             -- references, so they are not considered here.+             , dr == FirstBlock True && null (stmtMetadata s)+             ]+  in foldl onGlobal (foldl onDefs seed $ modDefines mdule) $ modGlobals mdule++-- | The handler passed to 'atFileLines' must be an instance of this class.+--+-- Here, @b@ is an object for which the following methods can be called with an+-- accumulator @a@ and the corresponding LLVM AST element that has the @lab@+-- label type.  The method will return an updated accumulator.+class AtFileLines a b where+  -- | The 'atDefine' method is called (before any enclosed 'BasicBlock' or+  --   'Stmt' elements) if the file and line number are associated with the+  --   'Define' signature line.+  atDefine :: b -> Define -> a -> a+  -- | The 'atBlockStart' method is called if the first 'Stmt' in the+  --   'BasicBlock' is associated with the file and line number, and before any+  --   'Stmt's in the block are passed to 'atStmt'.+  --   the first block in the 'Define'.+  atBlockStart :: b -> DefineRel -> BasicBlock -> a -> a+  -- | The 'atStmt' method is called for every 'Stmt' in the basic block that is+  --   associated with the file and line number.  The boolean value passed is+  --   true if the 'Stmt' immediately follows a previous 'Stmt' that was+  --   associated with the same line, or if this was the first 'Stmt' in the+  --   block.+  atStmt :: b -> DefineRel -> BlockRel -> Stmt -> a -> a+  -- | The 'atGlobal' method is called for evey 'Global' that is associated with+  -- the file and line number.+  atGlobal :: b -> Global -> a -> a++data DefineRel = FirstBlock Bool -- ^ first block of a 'Define', matched file & line?+               | OtherBlock      -- ^ other blocks of a 'Define'+  deriving Eq++data BlockRel = FirstBlockStmt -- ^ first statement in a 'BasicBlock'+              | ContiguousStmt -- ^ previous statement also matched the file & line+              | FirstLineStmt  -- ^ previous statement did not match the file & line+  deriving Eq
src/Text/LLVM/Labels.hs view
@@ -60,7 +60,8 @@                                 <*> traverse (relabel f) a                                 <*> pure s                                 <*> pure o-  relabel f (ICmp op l r)         = ICmp op+  relabel f (ICmp samesign op l r)+                                  = ICmp samesign op                                 <$> traverse (relabel f) l                                 <*> relabel f r   relabel f (FCmp op l r)         = FCmp op@@ -125,9 +126,17 @@ instance HasLabel Clause'                     where relabel = $(generateRelabel 'relabel ''Clause') instance HasLabel Value'                      where relabel = $(generateRelabel 'relabel ''Value') instance HasLabel ValMd'                      where relabel = $(generateRelabel 'relabel ''ValMd')+instance HasLabel DebugRecord'                where relabel = $(generateRelabel 'relabel ''DebugRecord')+instance HasLabel DbgRecAssign'               where relabel = $(generateRelabel 'relabel ''DbgRecAssign')+instance HasLabel DbgRecDeclare'              where relabel = $(generateRelabel 'relabel ''DbgRecDeclare')+instance HasLabel DbgRecLabel'                where relabel = $(generateRelabel 'relabel ''DbgRecLabel')+instance HasLabel DbgRecValueSimple'          where relabel = $(generateRelabel 'relabel ''DbgRecValueSimple')+instance HasLabel DbgRecValue'                where relabel = $(generateRelabel 'relabel ''DbgRecValue') instance HasLabel DILabel'                    where relabel = $(generateRelabel 'relabel ''DILabel') instance HasLabel DebugLoc'                   where relabel = $(generateRelabel 'relabel ''DebugLoc') instance HasLabel DebugInfo'                  where relabel = $(generateRelabel 'relabel ''DebugInfo')+instance HasLabel DIBasicType'                where relabel = $(generateRelabel 'relabel ''DIBasicType')+instance HasLabel DISubrangeType'             where relabel = $(generateRelabel 'relabel ''DISubrangeType') instance HasLabel DIDerivedType'              where relabel = $(generateRelabel 'relabel ''DIDerivedType') instance HasLabel DISubroutineType'           where relabel = $(generateRelabel 'relabel ''DISubroutineType') instance HasLabel DISubrange'                 where relabel = $(generateRelabel 'relabel ''DISubrange')@@ -136,6 +145,8 @@ instance HasLabel DILocalVariable'            where relabel = $(generateRelabel 'relabel ''DILocalVariable') instance HasLabel DISubprogram'               where relabel = $(generateRelabel 'relabel ''DISubprogram') instance HasLabel DICompositeType'            where relabel = $(generateRelabel 'relabel ''DICompositeType')+instance HasLabel DIFixedPointType'           where relabel = $(generateRelabel 'relabel ''DIFixedPointType')+instance HasLabel DIFixedPointKind'           where relabel = $(generateRelabel 'relabel ''DIFixedPointKind') instance HasLabel DILexicalBlock'             where relabel = $(generateRelabel 'relabel ''DILexicalBlock') instance HasLabel DICompileUnit'              where relabel = $(generateRelabel 'relabel ''DICompileUnit') instance HasLabel DILexicalBlockFile'         where relabel = $(generateRelabel 'relabel ''DILexicalBlockFile')
src/Text/LLVM/Lens.hs view
@@ -31,7 +31,9 @@     , ''DebugInfo'     , ''DIFile     , ''DISubrange'-    , ''DIBasicType+    , ''DIBasicType'+    , ''DISubrangeType'+    , ''DIFixedPointType'     , ''DIExpression     , ''DISubprogram'     , ''DISubroutineType'
src/Text/LLVM/PP.hs view
@@ -1,8 +1,10 @@ {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TypeApplications #-}  -- | -- Module      :  Text.LLVM.PP@@ -15,22 +17,176 @@ -- -- This is the pretty-printer for llvm assembly versions 3.6 and lower. ---module Text.LLVM.PP where+module Text.LLVM.PP+  (+    Config(Config, cfgVer), withConfig+  , Fmt+  , LLVMVer, llvmVer, llvmVerToString+  , llvmVlatest, llvmV3_5, llvmV3_6, llvmV3_7, llvmV3_8+  , ppLLVM, ppLLVM35, ppLLVM36, ppLLVM37, ppLLVM38+  , LLVMPretty(llvmPP)+  , ppModule+  , ppSourceName+  , ppNamedMd+  , ppUnnamedMd+  , ppGlobalAlias+  , ppTargetTriple+  , ppDataLayout+  , ppLayoutSpec+  , ppPointerSize+  , ppStorage+  , ppAlignment+  , ppFunctionPointerAlignType+  , ppMangling+  , ppInlineAsm+  , ppIdent+  , ppSymbol+  , ppPrimType+  , ppFloatType+  , ppType+  , ppTypeDecl+  , ppGlobal+  , ppGlobalMetadata+  , ppGlobalAttrs+  , ppDeclare+  , ppComdatName+  , ppComdat+  , ppSelectionKind+  , ppDefineSig+  , ppDefine+  , ppFunAttr+  , ppLabelDef+  , ppLabel+  , PrettyLabel(ppLabel')+  , ppBasicBlock+  , ppStmt+  , ppAttachedMetadata+  , ppLinkage+  , ppVisibility+  , ppGC+  , ppTyped+  , ppSignBits+  , ppExact+  , ppArithOp+  , ppUnaryArithOp+  , ppBitOp+  , ppConvOp+  , ppAtomicOrdering+  , ppAtomicOp+  , ppScope+  , ppInstr+  , ppLoad+  , ppStore+  , ppClauses+  , ppClause+  , ppTypedLabel+  , ppSwitchEntry+  , ppVectorIndex+  , ppAlign+  , ppAlloca+  , ppCall+  , ppCallBr+  , ppCallSym+  , ppGEP+  , ppInvoke+  , ppPhiArg+  , ppICmpOp+  , ppFCmpOp+  , ppValue'+  , ppValue+  , ppValMd'+  , ppValMd+  , ppDebugLoc'+  , ppDebugLoc+  , ppTypedValMd+  , ppMetadata+  , ppMetadataNode'+  , ppMetadataNode+  , ppStringLiteral+  , ppAsm+  , ppConstExpr'+  , ppConstExpr+  , ppGepFlags+  , ppDebugInfo'+  , ppDebugRecords+  , ppDebugRecord'+  , ppDbgRecValue'+  , ppDbgRecDeclare'+  , ppDbgRecAssign'+  , ppDbgRecValueSimple'+  , ppDebugInfo+  , ppDIImportedEntity'+  , ppDIImportedEntity+  , ppDILabel'+  , ppDILabel+  , ppDINameSpace'+  , ppDINameSpace+  , ppDITemplateTypeParameter'+  , ppDITemplateTypeParameter+  , ppDITemplateValueParameter'+  , ppDITemplateValueParameter+  , ppDIBasicType'+  , ppDISubrangeType'+  , ppDISubrangeType+  , ppDICompileUnit'+  , ppDICompileUnit+  , ppFlags+  , ppDICompositeType'+  , ppDICompositeType+  , ppDIDerivedType'+  , ppDIDerivedType+  , ppDIEnumerator+  , ppDIFixedPointType'+  , ppDIFixedPointType+  , ppDIExpression+  , ppDIFile+  , ppDIGlobalVariable'+  , ppDIGlobalVariable+  , ppDIGlobalVariableExpression'+  , ppDIGlobalVariableExpression+  , ppDILexicalBlock'+  , ppDILexicalBlock+  , ppDILexicalBlockFile'+  , ppDILexicalBlockFile+  , ppDILocalVariable'+  , ppDILocalVariable+  , ppDISubprogram'+  , ppDISubprogram+  , ppDISubrange'+  , ppDISubrange+  , ppDISubroutineType'+  , ppDISubroutineType+  , ppDIArgList'+  , ppDIArgList+  , ppModuleAtLine+  , ppArgList+  , ppBool+  , ppInt64ValMd'+  , ppSizeOrOffsetValMd'+  , ppMaybe+  , hex+  , onlyOnLLVM+  , droppedInLLVM+  )+where  import Text.LLVM.AST+import Text.LLVM.DebugUtils import Text.LLVM.Triple.AST (TargetTriple) import Text.LLVM.Triple.Print (printTriple)  import Control.Applicative ((<|>))-import Data.Bits ( shiftR, (.&.) )+import Data.Bits ( shiftL, shiftR, (.|.), (.&.) )+import Data.Bool ( bool ) import Data.Char (isAlphaNum,isAscii,isDigit,isPrint,ord,toUpper)-import Data.List (intersperse)+import Data.List ( intersperse, nub ) import qualified Data.Map as Map import Data.Maybe (catMaybes,fromMaybe,isJust)-import GHC.Float (castDoubleToWord64, castFloatToWord32)+import GHC.Float (castDoubleToWord64, castWord32ToFloat, float2Double) import Numeric (showHex) import Text.PrettyPrint.HughesPJ import Data.Int+import Data.Word (Word16, Word32) import Prelude hiding ((<>))  @@ -73,8 +229,8 @@ -- this is used for defaulting and otherwise reporting the maximum LLVM version -- known to be supported. llvmVlatest :: LLVMVer-llvmVlatest = 17-+llvmVlatest = 22 -- If you update this, make sure to also update the latest LLVM+                 -- version mentioned in the README.  -- | The differences between various versions of the llvm textual AST. newtype Config = Config { cfgVer :: LLVMVer }@@ -159,11 +315,11 @@ ppNamedMd :: Fmt NamedMd ppNamedMd nm =   sep [ ppMetadata (text (nmName nm)) <+> char '='-      , ppMetadata (braces (commas (map (ppMetadata . int) (nmValues nm)))) ]+      , ppMetadata (braces (commas (map (ppMetadata . int . unnamedMdIdx) (nmValues nm)))) ]  ppUnnamedMd :: Fmt UnnamedMd ppUnnamedMd um =-  sep [ ppMetadata (int (umIndex um)) <+> char '='+  sep [ ppMetadata (int (unnamedMdIdx $ umIndex um)) <+> char '='       , distinct <+> ppValMd (umValues um) ]   where   distinct | umDistinct um = "distinct"@@ -206,32 +362,59 @@   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+    PointerSize ps            -> char 'p' <> ppPointerSize ps+    IntegerSize sz            -> char 'i' <> ppStorage sz+    VectorSize  sz            -> char 'v' <> ppStorage sz+    FloatSize   sz            -> char 'f' <> ppStorage sz+    StackObjSize sz           -> char 's' <> ppStorage sz+    AggregateSize Nothing a   -> char 'a' <> char ':' <> ppAlignment a+    AggregateSize (Just s) a  -> char 'a' <> int s <> char ':' <> ppAlignment a     NativeIntSize szs         ->       char 'n' <> hcat (punctuate (char ':') (map int szs))     StackAlign a              -> char 'S' <> int a+    ProgramAddrSpace as       -> char 'P' <> int as+    GlobalAddrSpace as        -> char 'G' <> int as+    AllocaAddrSpace as        -> char 'A' <> int as+    FunctionPointerAlign ty abi ->+      char 'F' <> ppFunctionPointerAlignType ty <> int abi     Mangling m                -> char 'm' <> char ':' <> ppMangling m+    NonIntegralPointerSpaces asl ->+      "ni:" <> hcat (punctuate (char ':') (map int asl)) --- | 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+ppPointerSize :: Fmt PointerSize+ppPointerSize ps =+  if ptrAddrSpace ps == 0+  then char ':' <> ppStorage (ptrStorage ps)+       <> ppOptColonInt (ptrAddrIndexSize ps)+  else int (ptrAddrSpace ps) <> char ':' <> ppStorage (ptrStorage ps)+       <> ppOptColonInt (ptrAddrIndexSize ps) +ppStorage :: Fmt Storage+ppStorage s = int (storageSize s) <> char ':'+              <> ppAlignment (storageAlignment s)++ppAlignment :: Fmt Alignment+ppAlignment a = int (alignABI a) <> ppOptColonInt (alignPreferred a)++ppOptColonInt :: Fmt (Maybe Int)+ppOptColonInt = \case+  Nothing -> empty+  Just i  -> char ':' <> int i++ppFunctionPointerAlignType :: Fmt FunctionPointerAlignType+ppFunctionPointerAlignType ty =+  case ty of+    IndependentOfFunctionAlign -> char 'i'+    MultipleOfFunctionAlign -> char 'n'+ ppMangling :: Fmt Mangling ppMangling ElfMangling         = char 'e'+ppMangling GoffMangling        = char 'l' ppMangling MipsMangling        = char 'm' ppMangling MachOMangling       = char 'o' ppMangling WindowsCoffMangling = char 'w'+ppMangling WindowsX86CoffMangling = char 'x'+ppMangling XCoffMangling       = char 'a'   -- Inline Assembly -------------------------------------------------------------@@ -283,6 +466,7 @@  ppFloatType :: Fmt FloatType ppFloatType Half      = "half"+ppFloatType BFloat    = "bfloat" ppFloatType Float     = "float" ppFloatType Double    = "double" ppFloatType Fp128     = "fp128"@@ -313,8 +497,15 @@          <+> ppGlobalAttrs (isJust $ globalValue g) (globalAttrs g)          <+> ppType (globalType g) <+> ppMaybe ppValue (globalValue g)           <> ppAlign (globalAlign g)-          <> ppAttachedMetadata (Map.toList (globalMetadata g))+          <> ppGlobalMetadata (Map.toList (globalMetadata g)) +ppGlobalMetadata :: Fmt [(String, ValMd' BlockLabel)]+ppGlobalMetadata mds+  | null mds  = empty+  | otherwise = comma <+> commas (map step mds)+  where+  step (l,md) = ppMetadata (text l) <+> ppValMd md+ -- | 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@@ -370,26 +561,29 @@       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 '}'+ppDefineSig :: Fmt Define+ppDefineSig 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)   where   ppMds mdm =     case Map.toList mdm of       [] -> empty       mds -> hsep [ "!" <> text k <+> ppValMd md | (k, md) <- mds ] +ppDefine :: Fmt Define+ppDefine d = ppDefineSig d+             <+> char '{'+             $+$ vcat (map ppBasicBlock (defBody d))+             $+$ char '}'+ -- FunAttr ---------------------------------------------------------------------  ppFunAttr :: Fmt FunAttr@@ -439,13 +633,48 @@               $+$ nest 2 (vcat (map ppStmt (bbStmts bb)))  +-- | Many of the pretty printing functions (based on 'Pretty') in this module are+-- written for the monomorphized `BlockLabel` version of the AST objects, but+-- some (those ending with a "tick" or single-quote) support the parameterized form.+--+-- When the parameterized label type is known to be 'BlockLabel', then 'ppLabel'+-- can be passed as this first argument to those pretty printing functions.+--+-- When the parameterized label type is not known (e.g. when implementing other+-- libraries that utilize @llvm-pretty@), the library may need to invoke the+-- pretty printer without passing an explicit label printer, and can instead use+-- the following class as a constraint for an instance that will be resolved+-- later.+--+-- For example:+--+-- > data Something lab = Something { ..., val :: Value' lab, ... }+-- >+-- > instance PrettyLabel lab => Pretty Something where+-- >   pretty s = .... <> ppValue' ppLabel' <>+--+-- Note that the `PrettyLabel` class is very similar to the `Pretty` class, but+-- unlike the latter's `pretty` method, the `PrettyLabel` returns the `Fmt`+-- object that allows the pretty-printing to be affected by the implicit `Config`+-- parameter; this aligns the `PrettyLabel` usage to the other pretty-printing+-- functions in this module.++class PrettyLabel lab where+  ppLabel' :: Fmt lab++instance PrettyLabel BlockLabel where+  ppLabel' = ppLabel++ -- Statements ------------------------------------------------------------------  ppStmt :: Fmt Stmt ppStmt stmt = case stmt of-  Result var i mds -> ppIdent var <+> char '=' <+> ppInstr i-                   <> ppAttachedMetadata mds-  Effect i mds     -> ppInstr i <> ppAttachedMetadata mds+  Result var i drs mds -> ppDebugRecords drs (ppIdent var <+> char '='+                                              <+> ppInstr i+                                               <> ppAttachedMetadata mds)+  Effect i drs mds     -> ppDebugRecords drs (ppInstr i+                                              <> ppAttachedMetadata mds)  ppAttachedMetadata :: Fmt [(String,ValMd)] ppAttachedMetadata mds@@ -523,14 +752,14 @@ ppBitOp Xor           = "xor"  ppConvOp :: Fmt ConvOp-ppConvOp Trunc    = "trunc"-ppConvOp ZExt     = "zext"+ppConvOp (Trunc nuw nsw) = "trunc" <+> ppSignBits nuw nsw+ppConvOp (ZExt nneg)  = "zext" <+> opt nneg "nneg" ppConvOp SExt     = "sext" ppConvOp FpTrunc  = "fptrunc" ppConvOp FpExt    = "fpext" ppConvOp FpToUi   = "fptoui" ppConvOp FpToSi   = "fptosi"-ppConvOp UiToFp   = "uitofp"+ppConvOp (UiToFp nneg) = "uitofp" <+> opt nneg "nneg" ppConvOp SiToFp   = "sitofp" ppConvOp PtrToInt = "ptrtoint" ppConvOp IntToPtr = "inttoptr"@@ -599,7 +828,7 @@                          <> comma <+> ppTyped ppValue a                          <+> ppScope s                          <+> ppAtomicOrdering o-  ICmp op l r            -> "icmp" <+> ppICmpOp op+  ICmp samesign op l r   -> "icmp" <+> opt samesign "samesign" <+> ppICmpOp op                         <+> ppTyped ppValue l <> comma <+> ppValue r   FCmp op l r            -> "fcmp" <+> ppFCmpOp op                         <+> ppTyped ppValue l <> comma <+> ppValue r@@ -616,7 +845,7 @@   ShuffleVector a b m    -> "shufflevector" <+> ppTyped ppValue a                          <> comma <+> ppTyped ppValue (b <$ a)                          <> comma <+> ppTyped ppValue m-  GEP ib ty ptr ixs      -> ppGEP ib ty ptr ixs+  GEP gf ty ptr ixs      -> ppGEP gf ty ptr ixs   Comment str            -> char ';' <+> text str   Jump i                 -> "br"                         <+> ppTypedLabel i@@ -639,7 +868,10 @@                          <> comma <+> ppVectorIndex i   IndirectBr d ls        -> "indirectbr"                         <+> ppTyped ppValue d-                         <> comma <+> commas (map ppTypedLabel ls)+                         <> comma+                        <+> char '['+                        <+> commas (map ppTypedLabel ls)+                        <+> char ']'   Switch c d ls          -> "switch"                         <+> ppTyped ppValue c                          <> comma <+> ppTypedLabel d@@ -771,19 +1003,20 @@           -> ppType res         _ -> ppType ty -ppGEP :: Bool -> Type -> Typed Value -> Fmt [Typed Value]-ppGEP ib ty ptr ixs =-  "getelementptr" <+> inbounds+ppGEP :: [GEPAttr] -> Type -> Typed Value -> Fmt [Typed Value]+ppGEP gf ty ptr ixs =+  "getelementptr"+    <+> (if inlineIsBool+         then (if GEP_Inbounds `elem` gf then "inbounds" else empty)+         else ppGepFlags gf)     <+> (if isExplicit then explicit else empty)     <+> commas (map (ppTyped ppValue) (ptr:ixs))   where   isExplicit = llvmVer >= llvmV3_7+  inlineIsBool = llvmVer < 19    explicit = ppType ty <> comma -  inbounds | ib        = "inbounds"-           | otherwise = empty- ppInvoke :: Type -> Value -> [Typed Value] -> BlockLabel -> Fmt BlockLabel ppInvoke ty f args to uw = body   where@@ -825,17 +1058,89 @@ ppFCmpOp Funo   = "uno" ppFCmpOp Ftrue  = "true" +-- | Pad a string by prepending '0'.+zerofill :: Int -> String -> String+zerofill width s =+  let len = length s+      padding = if len < width then width - len else 0+      zeros = take padding $ repeat '0'+  in+  zeros ++ s++-- | Check if a half-float is an infinite or NaN value.+halfBitsIsInfOrNaN :: Word16 -> Bool+halfBitsIsInfOrNaN x =+  -- Half floats have a 1-bit sign, 5-bit exponent, and 10-bit+  -- significand. If the exponent field is all ones, the value+  -- is either +/- Inf (if the significand is 0) or a Nan.+  (x .&. 0x7c00) == 0x7c00++-- | Convert the bit representation of a half-float to a single.+halfToSingleBits :: Word16 -> Word32+halfToSingleBits x =+  -- Half floats have a 1-bit sign, 5-bit exponent, and 10-bit+  -- significand.+  --+  -- 32-bit single floats have a 1-bit sign, 8-bit exponent, and+  -- 23-bit significand. Convert as follows:+  --    - mask off the sign bit, widen, shift into place+  --    - mask off the significand, widen, shift into place+  --      (at the top of the new significand; rest stays 0)+  --    - mask off the exponent+  --    - convert it to int; if that's 0, it stays 0+  --    - otherwise re-offset it, convert to Word32, and shift+  --+  let sign = shiftL (fromIntegral (x .&. 0x8000)) (31 - 15)+      signif = shiftL (fromIntegral (x .&. 0xfff)) (23 - 10)+      expBits = x .&. 0x7c00+      expo = case fromIntegral @Word16 @Int (shiftR expBits 10) of+        0 -> 0+        n -> shiftL (fromIntegral ((n - 15) + 127)) 23+  in+  sign .|. expo .|. signif++-- | Check if a bfloat is an infinite or NaN value.+bfloatBitsIsInfOrNaN :: Word16 -> Bool+bfloatBitsIsInfOrNaN x =+  -- BFloat half floats have a 1-bit sign, 8-bit exponent, and 7-bit+  -- significand. If the exponent field is all ones, the value+  -- is either +/- Inf (if the significand is 0) or a Nan.+  (x .&. 0x7f80) == 0x7f80++-- | Convert the bit representation of a bfloat to a single.+bfloatToSingleBits :: Word16 -> Word32+bfloatToSingleBits x =+  -- Since 32-bit floats are the same layout, just with 16 bits+  -- more precision, all we need to do is widen and shift left.+  shiftL (fromIntegral x) 16+ ppValue' :: Fmt i -> Fmt (Value' i) ppValue' pp val = case val of   ValInteger i       -> integer i   ValBool b          -> ppBool b   -- Note: for +Inf/-Inf/NaNs, we want to output the bit-correct sequence+  ValHalf (FPHalf x) ->+    -- Shown in hex as 0H<<4-hex-digits>>, per+    -- https://llvm.org/docs/LangRef.html#simple-constants+    if halfBitsIsInfOrNaN x+      then text "0xH" <> text (zerofill 4 $ showHex x "")+      else float $ castWord32ToFloat $ halfToSingleBits x+  ValBFloat (FPBFloat x) ->+    -- Shown in hex as 0R<<4-hex-digits>>, per+    -- https://llvm.org/docs/LangRef.html#simple-constants+    if bfloatBitsIsInfOrNaN x+      then text "0xR" <> text (zerofill 4 $ showHex x "")+      else float $ castWord32ToFloat $ bfloatToSingleBits x   ValFloat f         ->     if isInfinite f || isNaN f-      then text "0x" <> text (showHex (castFloatToWord32 f) "")+      -- shown as 0x<<16-hex-digits>>, per+      -- https://llvm.org/docs/LangRef.html#simple-constants+      then text "0x" <> text (showHex (castDoubleToWord64 $ float2Double f) "")       else float f   ValDouble d        ->     if isInfinite d || isNaN d+      -- shown as 0x<<16-hex-digits>>, per+      -- https://llvm.org/docs/LangRef.html#simple-constants       then text "0x" <> text (showHex (castDoubleToWord64 d) "")       else double d   ValFP80 (FP80_LongDouble e s) ->@@ -845,6 +1150,19 @@               | otherwise = showHex n         fld v i = pad ((v `shiftR` (i * 8)) .&. 0xff)     in "0xK" <> text (foldr (fld e) (foldr (fld s) "" $ reverse [0..7::Int]) [1, 0])+  ValFP128 (FP128_LongDouble a b) ->+    -- shown as 0xL<<32-hex-digits>>, per+    -- https://llvm.org/docs/LangRef.html#simple-constants+    let print64 k = zerofill 16 $ showHex k "" in+    "0xL" <> text (print64 a ++ print64 b)+  ValFP128_PPC (FP128_PPC_DoubleDouble a b) ->+    -- shown as 0xM<<32-hex-digits>>, per+    -- https://llvm.org/docs/LangRef.html#simple-constants+    let print64 k = zerofill 16 $ showHex k ""+        a' = print64 (castDoubleToWord64 a)+        b' = print64 (castDoubleToWord64 b)+    in+    "0xM" <> text (a' ++ b')   ValIdent i         -> ppIdent i   ValSymbol s        -> ppSymbol s   ValNull            -> "null"@@ -871,7 +1189,7 @@ ppValMd' pp m = case m of   ValMdString str   -> ppMetadata (ppStringLiteral str)   ValMdValue tv     -> ppTyped (ppValue' pp) tv-  ValMdRef i        -> ppMetadata (int i)+  ValMdRef i        -> ppMetadata (int $ unnamedMdIdx i)   ValMdNode vs      -> ppMetadataNode' pp vs   ValMdLoc l        -> ppDebugLoc' pp l   ValMdDebugInfo di -> ppDebugInfo' pp di@@ -885,13 +1203,20 @@              <> parens (commas [ "line:"   <+> integral (dlLine dl)                                , "column:" <+> integral (dlCol dl)                                , "scope:"  <+> ppValMd' pp (dlScope dl)-                               ] <> mbIA <> mbImplicit)+                               ] <> mbIA <> mbImplicit <>+                        when' (llvmVer >= 21) (mbAtomGroup <> mbAtomRank))    where   mbIA = case dlIA dl of            Just md -> comma <+> "inlinedAt:" <+> ppValMd' pp md            Nothing -> empty   mbImplicit = if dlImplicit dl then comma <+> "implicit" else empty+  mbAtomGroup = if dlAtomGroup dl > 0+                  then comma <+> "atomGroup:" <+> integral (dlAtomGroup dl)+                  else empty+  mbAtomRank = if dlAtomRank dl > 0+                 then comma <+> "atomRank:" <+> integral (dlAtomRank dl)+                 else empty  ppDebugLoc :: Fmt DebugLoc ppDebugLoc = ppDebugLoc' ppLabel@@ -934,24 +1259,76 @@ ppConstExpr' :: Fmt i -> Fmt (ConstExpr' i) ppConstExpr' pp expr =   case expr of-    ConstGEP inb _mix ty ptr ixs  ->+    ConstGEP optflgs mrng ty ptr ixs  ->       "getelementptr"-        <+> opt inb "inbounds"-        <+> parens (commas (ppType ty : map ppTyp' (ptr:ixs)))-    ConstConv op tv t  -> ppConvOp op <+> parens (ppTyp' tv <+> "to" <+> ppType t)+        <+> ppGepFlags optflgs+        <+> ppRange mrng+        <+> parens (commas (+                       let argIndices = 0 : [0..] -- rval, ptr, then ixs indices+                       in reverse  -- ppTyp's pushes entries to the listg head+                          $ foldl (ppTyp's mrng) [ppType ty]+                          $ zip argIndices (ptr:ixs)))+    ConstConv op tv t  ->+      let droppedIn18 = case op of+                          -- https://github.com/llvm/llvm-project commit e4a4122 dropped ZExt and SExt+                          ZExt _ -> True+                          SExt -> True+                          -- https://github.com/llvm/llvm-project commit 17764d2 dropped FpTrunc through SiToFP+                          FpTrunc -> True+                          FpExt -> True+                          FpToUi -> True+                          FpToSi -> True+                          UiToFp _ -> True+                          SiToFp -> True+                          _ -> False+          ppConstConv = ppConvOp op <+> parens (ppTyp' tv <+> "to" <+> ppType t)+      in if droppedIn18+         then droppedInLLVM 18 "fptrunc/fpext/fptoui/fptosi/uitofp/sitofp constexprs" ppConstConv+         else ppConstConv     ConstSelect c l r  ->-      "select" <+> parens (commas [ ppTyp' c, ppTyp' l , ppTyp' r])+      droppedInLLVM 17 "select constexpr" -- https://github.com/llvm/llvm-project commit bbfb13a++      $ "select" <+> parens (commas [ ppTyp' c, ppTyp' l , ppTyp' r])     ConstBlockAddr t l -> "blockaddress" <+> parens (ppVal' (typedValue t) <> comma <+> pp l)-    ConstFCmp       op a b -> "fcmp" <+> ppFCmpOp op <+> ppTupleT a b-    ConstICmp       op a b -> "icmp" <+> ppICmpOp op <+> ppTupleT a b+    ConstFCmp       op a b -> droppedInLLVM 19 "fcmp constexprs"+                              $ "fcmp" <+> ppFCmpOp op <+> ppTupleT a b+    ConstICmp       op a b -> droppedInLLVM 19 "icmp constexprs"+                              $ "icmp" <+> ppICmpOp op <+> ppTupleT a b     ConstArith      op a b -> ppArithOp op <+> ppTuple a b     ConstUnaryArith op a   -> ppUnaryArithOp op <+> ppTyp' a-    ConstBit        op a b -> ppBitOp op   <+> ppTuple a b+    ConstBit        op@(Shl _ _) a b -> droppedInLLVM 19 "shl constexprs"+                                        $ ppBitOp op   <+> ppTuple a b+    ConstBit        Xor a b -> ppBitOp Xor <+> ppTuple a b+    ConstBit        op a b -> droppedInLLVM 18 "and/or/lshr/ashr constexprs"+                              $ ppBitOp op <+> ppTuple a b   where ppTuple  a b = parens $ ppTyped ppVal' a <> comma <+> ppVal' b         ppTupleT a b = parens $ ppTyped ppVal' a <> comma <+> ppTyp' b         ppVal'       = ppValue' pp         ppTyp'       = ppTyped ppVal'+        ppTyp's mrng a (i,t) =+          let inrangeMark = if Just (RangeIndex i) == mrng then "inrange" else empty+          in (inrangeMark <+> ppTyp' t) : a+        ppRange =+          let ppR = \case+                RangeIndex _i -> empty -- handled in ppTyp's+                Range _ l u ->+                  "inrange(" <> integral l <> ", " <> integral u <> ")"+          in maybe empty ppR +ppGepFlags :: Fmt [GEPAttr]+ppGepFlags s =+  let fltr = if GEP_Inbounds `elem` s+             then+               -- inbounds implies nusw, but LLVM stipulates that if+               -- inbounds is present, nusw is not also printed.+               filter (/= GEP_NUSW)+             else id+      ppF = \case+        GEP_Inbounds -> "inbounds"+        GEP_NUSW -> "nusw"+        GEP_NUW -> "nuw"+  in foldl (\o f -> o <+> ppF f) empty $ fltr $ nub s+ ppConstExpr :: Fmt ConstExpr ppConstExpr = ppConstExpr' ppLabel @@ -959,7 +1336,7 @@  ppDebugInfo' :: Fmt i -> Fmt (DebugInfo' i) ppDebugInfo' pp di = case di of-  DebugInfoBasicType bt         -> ppDIBasicType bt+  DebugInfoBasicType bt         -> ppDIBasicType' pp bt   DebugInfoCompileUnit cu       -> ppDICompileUnit' pp cu   DebugInfoCompositeType ct     -> ppDICompositeType' pp ct   DebugInfoDerivedType dt       -> ppDIDerivedType' pp dt@@ -973,6 +1350,8 @@   DebugInfoLocalVariable lv     -> ppDILocalVariable' pp lv   DebugInfoSubprogram sp        -> ppDISubprogram' pp sp   DebugInfoSubrange sr          -> ppDISubrange' pp sr+  DebugInfoSubrangeType srt     -> ppDISubrangeType' pp srt+  DebugInfoFixedPointType fpt   -> ppDIFixedPointType' pp fpt   DebugInfoSubroutineType st    -> ppDISubroutineType' pp st   DebugInfoNameSpace ns         -> ppDINameSpace' pp ns   DebugInfoTemplateTypeParameter dttp  -> ppDITemplateTypeParameter' pp dttp@@ -981,7 +1360,69 @@   DebugInfoLabel dil            -> ppDILabel' pp dil   DebugInfoArgList args         -> ppDIArgList' pp args   DebugInfoAssignID             -> "!DIAssignID()"+  -- DebugRecordDeclare drd        -> ppDbgRecDeclare' pp drd +-- Prints DebugRecords (introduced in LLVM 19) which replace debug intrinsics and+-- unlike the intrinsics that follow the instruction, the debug records *precede*+-- the Instruction they affect.+ppDebugRecords :: [DebugRecord' BlockLabel] -> Fmt Doc+ppDebugRecords [] = id+ppDebugRecords drs = ((nest 2 $ vcat (ppDebugRecord' ppLabel <$> drs)) $$)++ppDebugRecord' :: Fmt lab -> Fmt (DebugRecord' lab)+ppDebugRecord' pl = \case+  DebugRecordValue drv -> ppDbgRecValue' pl drv+  DebugRecordDeclare drd -> ppDbgRecDeclare' pl drd+  DebugRecordAssign dra -> ppDbgRecAssign' pl dra+  DebugRecordValueSimple dvs -> ppDbgRecValueSimple' pl dvs+  DebugRecordLabel drl -> ppDbgRecLabel' pl drl++ppDbgRecValue' :: Fmt lab -> Fmt (DbgRecValue' lab)+ppDbgRecValue' pl dr =+  "#dbg_value"+  <> parens (commas [ ppValMd' pl $ drvValAsMetadata dr+                    , ppValMd' pl $ drvLocalVariable dr+                    , ppValMd' pl $ drvExpression dr+                    , ppValMd' pl $ drvLocation dr+                    ])++ppDbgRecDeclare' :: Fmt lab -> Fmt (DbgRecDeclare' lab)+ppDbgRecDeclare' pl dr =+  "#dbg_declare"+  <> parens (commas [ ppValMd' pl $ drdValAsMetadata dr+                    , ppValMd' pl $ drdLocalVariable dr+                    , ppValMd' pl $ drdExpression dr+                    , ppValMd' pl $ drdLocation dr+                    ])++ppDbgRecAssign' :: Fmt lab -> Fmt (DbgRecAssign' lab)+ppDbgRecAssign' pl dr =+  "#dbg_assign"+  <> parens (commas [ ppValMd' pl $ draValAsMetadata dr+                    , ppValMd' pl $ draLocalVariable dr+                    , ppValMd' pl $ draExpression dr+                    , ppValMd' pl $ draAssignID dr+                    , ppValMd' pl $ draValAsMetadataAddr dr+                    , ppValMd' pl $ draExpressionAddr dr+                    , ppValMd' pl $ draLocation dr+                    ])++ppDbgRecValueSimple' :: Fmt lab -> Fmt (DbgRecValueSimple' lab)+ppDbgRecValueSimple' pl dr =+  "#dbg_value"+  <> parens (commas [ ppTyped (ppValue' pl) $ drvsValue dr+                    , ppValMd' pl $ drvsLocalVariable dr+                    , ppValMd' pl $ drvsExpression dr+                    , ppValMd' pl $ drvsLocation dr+                    ])++ppDbgRecLabel' :: Fmt lab -> Fmt (DbgRecLabel' lab)+ppDbgRecLabel' pl dr =+  "#dbg_label"+  <> parens (commas [ ppValMd' pl $ drlLabel dr+                    , ppValMd' pl $ drlLocation dr+                    ])+ ppDebugInfo :: Fmt DebugInfo ppDebugInfo = ppDebugInfo' ppLabel @@ -1000,11 +1441,17 @@  ppDILabel' :: Fmt i -> Fmt (DILabel' i) ppDILabel' pp ie = "!DILabel"-  <> parens (mcommas [ (("scope:"  <+>) . ppValMd' pp) <$> dilScope ie-                     , pure ("name:" <+> ppStringLiteral (dilName ie))-                     , (("file:"   <+>) . ppValMd' pp) <$> dilFile ie-                     , pure ("line:"   <+> integral (dilLine ie))-                     ])+  <> parens (mcommas $+       [ (("scope:"  <+>) . ppValMd' pp) <$> dilScope ie+       , pure ("name:" <+> ppStringLiteral (dilName ie))+       , (("file:"   <+>) . ppValMd' pp) <$> dilFile ie+       , pure ("line:"   <+> integral (dilLine ie))+       ] +++       when' (llvmVer >= 21)+       [ pure ("column:" <+> integral (dilColumn ie))+       , pure ("isArtificial:" <+> ppBool (dilIsArtificial ie))+       , (("coroSuspendIdx:" <+>) . integral) <$> dilCoroSuspendIdx ie+       ])  ppDILabel :: Fmt DILabel ppDILabel = ppDILabel' ppLabel@@ -1040,19 +1487,48 @@ ppDITemplateValueParameter :: Fmt DITemplateValueParameter ppDITemplateValueParameter = ppDITemplateValueParameter' ppLabel -ppDIBasicType :: Fmt DIBasicType-ppDIBasicType bt = "!DIBasicType"-  <> parens (commas [ "tag:"      <+> integral (dibtTag bt)-                    , "name:"     <+> doubleQuotes (text (dibtName bt))-                    , "size:"     <+> integral (dibtSize bt)-                    , "align:"    <+> integral (dibtAlign bt)-                    , "encoding:" <+> integral (dibtEncoding bt)-                    ] <> mbFlags)-  where-  mbFlags = case dibtFlags bt of-              Just flags -> comma <+> "flags:" <+> integral flags-              Nothing -> empty+ppDIBasicType' :: Fmt i -> Fmt (DIBasicType' i)+ppDIBasicType' pp bt = "!DIBasicType"+  <> parens (mcommas $+       [ pure ("tag:"      <+> integral (dibtTag bt))+       , pure ("name:"     <+> doubleQuotes (text (dibtName bt)))+       ,     (("size:"     <+>) . ppSizeOrOffsetValMd' pp) <$> dibtSize bt+       , pure ("align:"    <+> integral (dibtAlign bt))+       , pure ("encoding:" <+> integral (dibtEncoding bt))+       ,     (("flags:"    <+>) . integral)+             <$> dibtFlags bt+       , if dibtNumExtraInhabitants bt > 0+         then pure ("numExtraInhabitants:" <+> integral (dibtNumExtraInhabitants bt))+         else Nothing+       ]+       +++       when' (llvmVer >= 22)+       [ if dibtDataSize bt > 0+         then pure ("dataSize:" <+> integral (dibtDataSize bt))+         else Nothing+       ]+       ) +ppDISubrangeType' :: Fmt i -> Fmt (DISubrangeType' i)+ppDISubrangeType' pp srt = "!DISubrangeType"+  <> parens (mcommas+       [     (("name:"     <+>) . doubleQuotes . text) <$> (disrtName srt)+       ,     (("file:"     <+>) . ppValMd' pp) <$> (disrtFile srt)+       , pure ("line:"     <+> integral (disrtLine srt))+       ,     (("scope:"    <+>) . ppValMd' pp) <$> (disrtScope srt)+       ,     (("size:"     <+>) . ppValMd' pp) <$> (disrtSize srt)+       , pure ("align:"    <+> integral (disrtAlign srt))+       , pure ("flags:"    <+> integral (disrtFlags srt))+       ,     (("baseType:" <+>) . ppValMd' pp) <$> (disrtBaseType srt)+       , (("lowerBound:"   <+>) . ppInt64ValMd' True pp) <$> disrtLowerBound srt+       , (("upperBound:"   <+>) . ppInt64ValMd' True pp) <$> disrtUpperBound srt+       , (("stride:"       <+>) . ppInt64ValMd' True pp) <$> disrtStride srt+       , (("bias:"         <+>) . ppInt64ValMd' True pp) <$> disrtBias srt+       ])++ppDISubrangeType :: Fmt DISubrangeType+ppDISubrangeType = ppDISubrangeType' ppLabel+ ppDICompileUnit' :: Fmt i -> Fmt (DICompileUnit' i) ppDICompileUnit' pp cu = "!DICompileUnit"   <> parens (mcommas $@@ -1085,6 +1561,12 @@        ,     (("sdk:"                   <+>) . doubleQuotes . text)              <$> (dicuSDK cu)        ]+       +++       when' (llvmVer >= 22)+       [ if dicuSourceLanguageVersion cu > 0+         then pure ("sourceLanguageVersion:" <+> integral (dicuSourceLanguageVersion cu))+         else Nothing+       ]        )  @@ -1102,9 +1584,9 @@        ,     (("file:"           <+>) . ppValMd' pp) <$> (dictFile ct)        , pure ("line:"           <+> integral (dictLine ct))        ,     (("baseType:"       <+>) . ppValMd' pp) <$> (dictBaseType ct)-       , pure ("size:"           <+> integral (dictSize ct))+       ,     (("size:"           <+>) . ppSizeOrOffsetValMd' pp) <$> dictSize ct        , pure ("align:"          <+> integral (dictAlign ct))-       , pure ("offset:"         <+> integral (dictOffset ct))+       ,     (("offset:"         <+>) . ppSizeOrOffsetValMd' pp) <$> dictOffset ct        , pure ("flags:"          <+> integral (dictFlags ct))        ,     (("elements:"       <+>) . ppValMd' pp) <$> (dictElements ct)        , pure ("runtimeLang:"    <+> integral (dictRuntimeLang ct))@@ -1117,6 +1599,12 @@        ,     (("allocated:"      <+>) . ppValMd' pp) <$> (dictAllocated ct)        ,     (("rank:"           <+>) . ppValMd' pp) <$> (dictRank ct)        ,     (("annotations:"    <+>) . ppValMd' pp) <$> (dictAnnotations ct)+       , if dictNumExtraInhabitants ct > 0+         then pure ("numExtraInhabitants:" <+> integral (dictNumExtraInhabitants ct))+         else Nothing+       ,     (("specification:"  <+>) . ppValMd' pp) <$> (dictSpecification ct)+       ,     (("enumKind:"       <+>) . integral) <$> (dictEnumKind ct)+       ,     (("bitStride:"      <+>) . ppValMd' pp) <$> (dictBitStride ct)        ])  ppDICompositeType :: Fmt DICompositeType@@ -1131,9 +1619,9 @@        , pure ("line:"      <+> integral (didtLine dt))        ,     (("scope:"     <+>) . ppValMd' pp) <$> (didtScope dt)        ,      ("baseType:"  <+>) <$> (ppValMd' pp <$> didtBaseType dt <|> Just "null")-       , pure ("size:"      <+> integral (didtSize dt))+       ,     (("size:"      <+>) . ppSizeOrOffsetValMd' pp) <$> didtSize dt        , pure ("align:"     <+> integral (didtAlign dt))-       , pure ("offset:"    <+> integral (didtOffset dt))+       ,     (("offset:"    <+>) . ppSizeOrOffsetValMd' pp) <$> didtOffset dt        , pure ("flags:"     <+> integral (didtFlags dt))        ,     (("extraData:" <+>) . ppValMd' pp) <$> (didtExtraData dt)        ,     (("dwarfAddressSpace:" <+>) . integral) <$> didtDwarfAddressSpace dt@@ -1150,6 +1638,35 @@                     , "isUnsigned:" <+> ppBool u                     ]) +ppDIFixedPointType' :: Fmt i -> Fmt (DIFixedPointType' i)+ppDIFixedPointType' pp t = "!DIFixedPointType"+  <> parens (mcommas $+       [ pure ("tag:"       <+> integral (difptTag t))+       ,     (("name:"     <+>) . doubleQuotes . text) <$> (difptName t)+       ,     (("size:"     <+>) . ppValMd' pp) <$> (difptSize t)+       , pure ("align:"    <+> integral (difptAlign t))+       , pure ("encoding:" <+> integral (difptEncoding t))+       , pure ("flags:"    <+> integral (difptFlags t))+       ]+       ++ case difptKind t of+            FixedPointBinary v ->+              [ pure "kind: Binary"+              , pure ("factor:" <+> integral v)+              ]+            FixedPointDecimal v ->+              [ pure "kind: Decimal"+              , pure ("factor:" <+> integral v)+              ]+            FixedPointRational n d ->+              [ pure "kind: Rational"+              , pure ("numerator:" <+> integral n)+              , pure ("denominator:" <+> integral d)+              ]+       )++ppDIFixedPointType :: Fmt DIFixedPointType+ppDIFixedPointType = ppDIFixedPointType' ppLabel+ ppDIExpression :: Fmt DIExpression ppDIExpression e = "!DIExpression"   <> parens (commas (map integral (dieElements e)))@@ -1305,6 +1822,66 @@ ppDIArgList :: Fmt DIArgList ppDIArgList = ppDIArgList' ppLabel ++-- -------------------------------------------------------------------+-- Auxiliary pretty-printing functions+--+-- These are alternative pretty-printing functions (instead of the pretty-printers+-- for the basic AST elements above). These functions can be used in+-- situations where additional or alternative pretty-printing functionality is+-- needed.++-- | This is an auxiliary pretty printer for showing just part of a module: the+-- part corresponding to a specific source file and line in the source file+-- (whereas ppModule or even ppDefine will show the *entire* module or+-- definition/function).  A range of lines can be displayed by iterative calls+-- over multiple lines.+--+-- > putStrLn $ ppLLVM llvmVlatest $ ppModuleAtLine "foo.c" 23 llvmModule+--+-- The above example shows all the lines in llvmModule that correspond to line 23+-- of the "foo.c" source file.+ppModuleAtLine :: (?config :: Config) => String -> Integer -> Fmt Module+ppModuleAtLine file line =+  toDoc . atFileLines (AddDocAtLine ?config) (Start empty) file line++-- internal helper for the AtFileLines instance below+data AddDocAtLine = AddDocAtLine Config++-- internal helper for the AtFileLines instance below+data DocBld = Start Doc -- ^ at the start: doc-so-far+            | DF DocBld Define Doc+              -- ^ atDefine: doc-so-far, the define, and the doc for the body of+              -- the AtFileLines+            | BS DocBld (Maybe BlockLabel) Doc+              -- ^ atBlockStart: doc-so-far, block label (if any), and doc for the+              -- block body++instance AtFileLines DocBld AddDocAtLine where+  atDefine _ d docbld = DF docbld d empty+  atBlockStart _ _dr bb docbld = BS docbld (bbLabel bb) empty+  atStmt (AddDocAtLine c) _dr br s =+    let isContig = case br of+          FirstBlockStmt -> True+          ContiguousStmt -> True+          FirstLineStmt -> False+    in withConfig c $ emit $ bool (text "..." $$) (empty $$) isContig $ ppStmt s+  atGlobal (AddDocAtLine c) g = withConfig c $ emit $ ppGlobal g++-- internal helper for the AtFileLines instance below+emit :: Doc -> DocBld -> DocBld+emit n = \case+  DF b s d -> DF b s (d $$ n)+  BS b l d -> BS b l (d $$ n)+  Start d -> Start (d $$ n)++-- internal helper for the AtFileLines instance below+toDoc :: Fmt DocBld+toDoc = \case+  DF b s d -> toDoc b $$ ppDefineSig s $$ nest 2 d+  BS b l d -> toDoc b $$ text "" $$ ppMaybe ppLabelDef l $$ d+  Start d -> d+ -- Utilities -------------------------------------------------------------------  ppBool :: Fmt Bool@@ -1337,7 +1914,7 @@           ValMdValue tv             | PrimType (Integer _) <- typedType tv             , ValInteger i <- typedValue tv-              -> integer i  -- 64 bits is the largest Int, so no conversion needed+              -> integer i  -- 64 bits is the largest Int, so no conversion needed.           o@(ValMdDebugInfo (DebugInfoGlobalVariable gv)) ->             case digvVariable gv of               Nothing -> when' canFallBack $ ppValMd' pp o@@ -1351,7 +1928,18 @@           -- ValMdRef _idx -> mempty -- no table here to look this up...           o -> when' canFallBack $ ppValMd' pp o +-- | Print the size or offset of a type-related metadata node. This value can+-- either be an integer literal (in which case the bare literal is printed), or+-- in LLVM 21 or later, it can be a more complicated metadata expression (in+-- which case the metadata is pretty-printed).+ppSizeOrOffsetValMd' :: Fmt i -> Fmt (ValMd' i)+ppSizeOrOffsetValMd' pp = \case+  ValMdValue tv+    | ValInteger i <- typedValue tv+      -> integer i+  o -> when' (llvmVer >= 21) $ ppValMd' pp o + commas :: Fmt [Doc] commas  = fsep . punctuate comma @@ -1376,3 +1964,11 @@   | llvmVer >= fromVer = id   | otherwise          = error $ name ++ " is supported only on LLVM >= "                                  ++ llvmVerToString fromVer++-- | Throw an error if the @?config@ version is older than the given version. The+-- String indicates which constructor is unavailable in the error message.+droppedInLLVM :: (?config :: Config) => LLVMVer -> String -> a -> a+droppedInLLVM fromVer name+  | llvmVer >= fromVer = error $ name ++ " is supported only up to LLVM >= "+                                 ++ llvmVerToString fromVer+  | otherwise          = id
src/Text/LLVM/Triple/AST.hs view
@@ -21,7 +21,6 @@   ) where  import Data.Data (Data)-import Data.Typeable (Typeable) import GHC.Generics (Generic)  -- | The constructors of this type exactly mirror the LLVM @enum ArchType@,@@ -150,7 +149,7 @@   | RenderScript64     -- | NEC SX-Aurora Vector Engine   | VE-  deriving (Bounded, Data, Eq, Enum, Generic, Ord, Read, Show, Typeable)+  deriving (Bounded, Data, Eq, Enum, Generic, Ord, Read, Show)  -- | A 'First'-like semigroup instance that simply drops the RHS, unless the LHS -- is 'UnknownArch'.@@ -216,7 +215,7 @@   | SPIRVSubArch_v13   | SPIRVSubArch_v14   | SPIRVSubArch_v15-  deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show, Typeable)+  deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show)  -- | A 'First'-like semigroup instance that simply drops the RHS, unless the LHS -- is 'NoSubArch'.@@ -250,7 +249,7 @@   | Mesa   | SUSE   | OpenEmbedded-  deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show, Typeable)+  deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show)  -- | A 'First'-like semigroup instance that simply drops the RHS, unless the LHS -- is 'UnknownVendor'.@@ -323,7 +322,7 @@   | Emscripten     -- | DirectX ShaderModel   | ShaderModel-  deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show, Typeable)+  deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show)  -- | A 'First'-like semigroup instance that simply drops the RHS, unless the LHS -- is 'UnknownOS'.@@ -386,7 +385,7 @@   | Callable   | Mesh   | Amplification-  deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show, Typeable)+  deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show)  -- | A 'First'-like semigroup instance that simply drops the RHS, unless the LHS -- is 'UnknownEnvironment'.@@ -414,7 +413,7 @@   | SPIRV   | Wasm   | XCOFF-  deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show, Typeable)+  deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show)  -- | A 'First'-like semigroup instance that simply drops the RHS, unless the LHS -- is 'UnknownObjectFormat'.@@ -440,7 +439,7 @@     , ttEnv :: Environment     , ttObjFmt :: ObjectFormat     }-  deriving (Bounded, Data, Eq, Generic, Ord, Read, Show, Typeable)+  deriving (Bounded, Data, Eq, Generic, Ord, Read, Show)  -- | Combines fields pointwise. instance Semigroup TargetTriple where
+ test/CombineTests.hs view
@@ -0,0 +1,231 @@+module CombineTests+  (+    tests+  )+where++import           Data.Function ( on )+import           Data.String ( fromString )+import           Lens.Micro++import qualified Test.Tasty as Tasty+import           Test.Tasty.HUnit ( assertBool, testCase, (@?=) )++import           Text.LLVM -- ( emptyModule )+import           Text.LLVM.Combine+import           Text.LLVM.Lens+++tests :: Tasty.TestTree+tests = Tasty.testGroup "LLVM combine"+  [+    testCase "empty equivalences"+    $ let llvm1 = emptyModule+          llvm2 = emptyModule+          llvm3 = emptyModule+          llvmAll = llvmModuleCombine (llvmModuleCombine llvm1 llvm2) llvm3+      in assertBool "combining empty is empty"+         $ and [ llvm1 == llvm1+               , ((==) `on` (modSourceNameLens .~ Nothing)) llvmAll llvm1+               , ((==) `on` (modSourceNameLens .~ Nothing)) llvmAll llvm2+               , ((==) `on` (modSourceNameLens .~ Nothing)) llvmAll llvm3+               ]++  , testCase "metadata updates"+    $ let llvm1 = emptyModule+                  & modUnnamedMdLens .~ [ UnnamedMd { umIndex = 1+                                                    , umValues = ValMdString "a"+                                                    , umDistinct = False+                                                    }+                                        ]+                  & modNamedMdLens .~ [ NamedMd { nmName = "frog"+                                                , nmValues = [1]+                                                }+                                      ]+          llvm2 = emptyModule+                  & modUnnamedMdLens .~ [ UnnamedMd { umIndex = 1+                                                    , umValues = ValMdString "B"+                                                    , umDistinct = False+                                                    }+                                        , UnnamedMd { umIndex = 2+                                                    , umValues = ValMdRef 1+                                                    , umDistinct = False+                                                    }+                                        ]+                  & modNamedMdLens .~ [ NamedMd { nmName = "pig"+                                                , nmValues = [2, 1]+                                                }+                                      ]+          llvmAll = llvmModuleCombine llvm1 llvm2+    in do llvmAll ^. modUnnamedMdLens @?=+            [ UnnamedMd { umIndex = 1+                        , umValues = ValMdString "a"+                        , umDistinct = False+                        }+            , UnnamedMd { umIndex = 3+                        , umValues = ValMdString "B"+                        , umDistinct = False+                        }+            , UnnamedMd { umIndex = 4+                        , umValues = ValMdRef 3+                        , umDistinct = False+                        }+            ]+          llvmAll ^. modNamedMdLens @?=+            [ NamedMd { nmName = "frog"+                      , nmValues = [1]+                      }+            , NamedMd { nmName = "pig"+                      , nmValues = [4, 3]+                      }+            ]++  , testCase "type name deconflicting"+    $ let llvm1 = emptyModule+            & modTypesLens .~ [ TypeDecl { typeName = fromString "type1"+                                         , typeValue = Opaque }+                              , TypeDecl { typeName = fromString "type1___0"+                                         , typeValue = Alias $ fromString "cow"+                                         }+                              ]+          llvm2 = emptyModule+            & modTypesLens .~ [ TypeDecl { typeName = fromString "type1"+                                         , typeValue = PrimType Void+                                         }+                              , TypeDecl { typeName = fromString "type1___0"+                                         , typeValue = Alias $ fromString "moo"+                                         }+                              , TypeDecl { typeName = fromString "type2"+                                         , typeValue = PtrOpaque+                                         }+                              ]+            & modDefinesLens .~+            [+              Define { defName = fromString "foo"+                     , defLinkage = Nothing+                     , defVisibility = Nothing+                     , defComdat = Nothing+                     , defMetadata = mempty+                     , defGC = Nothing+                     , defSection = Nothing+                     , defVarArgs = False+                     , defArgs =+                         [ Typed { typedType = Alias $ fromString "moo"+                                 , typedValue = fromString "type1___0"+                                 }+                         ]+                     , defRetType = PrimType Void+                     , defAttrs = mempty+                     , defBody = []+                     }+            ]+          llvmAll = llvmModuleCombine llvm1 llvm2+    in do llvmAll ^. modTypesLens @?=+            [ TypeDecl { typeName = fromString "type1"+                       , typeValue = Opaque }+            , TypeDecl { typeName = fromString "type1___0"+                       , typeValue = Alias $ fromString "cow"+                       }+            , TypeDecl { typeName = fromString "type1___1"+                      , typeValue = PrimType Void+                      }+            , TypeDecl { typeName = fromString "type1___0___0"+                       , typeValue = Alias $ fromString "moo"+                       }+            , TypeDecl { typeName = fromString "type2"+                       , typeValue = PtrOpaque+                       }+            ]+          llvmAll ^. modDefinesLens @?=+            [+              Define { defName = fromString "foo"+                     , defLinkage = Nothing+                     , defVisibility = Nothing+                     , defComdat = Nothing+                     , defMetadata = mempty+                     , defGC = Nothing+                     , defSection = Nothing+                     , defVarArgs = False+                     , defArgs =+                         [ Typed { typedType = Alias $ fromString "moo"+                                 , typedValue = fromString "type1___0___0"+                                 }+                         ]+                     , defRetType = PrimType Void+                     , defAttrs = mempty+                     , defBody = []+                     }+            ]+  , testCase "internal define name deconflicting"+    $ let d1 = Define { defName = fromString "foo"+                           , defLinkage = Just Internal+                           , defVisibility = Nothing+                           , defComdat = Nothing+                           , defMetadata = mempty+                           , defGC = Nothing+                           , defSection = Nothing+                           , defVarArgs = False+                           , defArgs =+                             [ Typed { typedType = Alias $ fromString "moo"+                                     , typedValue = fromString "type1"+                                     }+                             ]+                           , defRetType = PrimType $ Integer 8+                           , defAttrs = mempty+                           , defBody = []+                           }+          d2 = Define { defName = fromString "foo"+                      , defLinkage = Just Internal+                      , defVisibility = Nothing+                      , defComdat = Nothing+                      , defMetadata = mempty+                      , defGC = Nothing+                      , defSection = Nothing+                      , defVarArgs = False+                      , defArgs = []+                      , defRetType = PrimType Void+                      , defAttrs = mempty+                      , defBody = []+                           }+          llvm1 = emptyModule & modDefinesLens .~ [ d1 ]+          llvm2 = emptyModule & modDefinesLens .~ [ d2 ]+          llvmAll = llvmModuleCombine llvm1 llvm2+    in do llvmAll ^. modDefinesLens @?=+            [ d1+            , d2 & defNameLens .~ fromString "foo_1"+            ]++  , testCase "declare to define resolution"+    $ let d1 = Declare { decName = fromString "foo"+                       , decLinkage = Nothing+                       , decVisibility = Nothing+                       , decComdat = Nothing+                       , decVarArgs = False+                       , decArgs = [ Alias $ fromString "moo" ]+                       , decRetType = PrimType $ Integer 8+                       , decAttrs = mempty+                       }+          d2 = Define { defName = fromString "foo"+                      , defLinkage = Nothing+                      , defVisibility = Nothing+                      , defComdat = Nothing+                      , defMetadata = mempty+                      , defGC = Nothing+                      , defSection = Nothing+                      , defVarArgs = False+                      , defArgs =+                           [ Typed { typedType = Alias $ fromString "cow"+                                   , typedValue = fromString "type1"+                                   }+                           ]+                      , defRetType = PrimType $ Integer 8+                      , defAttrs = mempty+                      , defBody = []+                           }+          llvm1 = emptyModule & modDeclaresLens .~ [ d1 ]+          llvm2 = emptyModule & modDefinesLens .~ [ d2 ]+          llvmAll = llvmModuleCombine llvm1 llvm2+    in do llvmAll ^. modDefinesLens @?= [ d2 ]+          llvmAll ^. modDeclaresLens @?= []++  ]
+ test/DataLayout.hs view
@@ -0,0 +1,31 @@+module DataLayout ( tests ) where++import Text.LLVM.AST+import Text.LLVM.PP++import Test.Tasty+import Test.Tasty.HUnit++tests :: TestTree+tests =+  let rspDL s = Just $ "target datalayout = \"" <> s <> "\""+      ppDL = show . ppLLVM llvmVlatest ppDataLayout+  in testGroup "Test DataLayout"+     [ testCase "datalayout 0" $ ppDL <$> parseDataLayout "" @?= Just ""+     , testCase "datalayout 1"+       $ let dl = "e" in ppDL <$> parseDataLayout dl @?= rspDL dl+     , testCase "datalayout 2"+       $ let dl = "e-m:e" in ppDL <$> parseDataLayout dl @?= rspDL dl+     , testCase "datalayout 3"+       $ let dl = "e-m:e-p270:32:32"+         in ppDL <$> parseDataLayout dl @?= rspDL dl+     , testCase "datalayout 4"+       $ let dl = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-S128"+         in ppDL <$> parseDataLayout dl @?= rspDL dl+     , testCase "datalayout 5"+       $ let dl = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"+         in ppDL <$> parseDataLayout dl @?= rspDL dl+     , testCase "datalayout 6"+       $ let dl = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-ni:3:4-S128"+         in ppDL <$> parseDataLayout dl @?= rspDL dl+     ]
test/Main.hs view
@@ -2,12 +2,18 @@  import qualified Test.Tasty as Tasty -import qualified Triple+import qualified CombineTests+import qualified DataLayout+import qualified Metadata import qualified Output+import qualified Triple  main :: IO () main = Tasty.defaultMain $ Tasty.testGroup "LLVM tests"        [-         Output.tests+         DataLayout.tests+       , Metadata.tests+       , Output.tests        , Triple.tests+       , CombineTests.tests        ]
+ test/Metadata.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE TypeApplications #-}++-- | This module provides some testing of the metadata that can be attached to+-- Statements.++module Metadata ( tests ) where++import qualified Test.Tasty as Tasty+import           Test.Tasty.HUnit++import           Text.LLVM.AST+++tests :: Tasty.TestTree+tests = Tasty.testGroup "LLVM metadata tests" $+  let s1 = Effect (Load PtrOpaque (Typed Opaque ValNull) Nothing Nothing)+           mempty md1+      s2 = Result (Ident "r1")+           (Load PtrOpaque (Typed Opaque ValNull) Nothing Nothing)+           mempty md1+      md1 = [ ("foo", ValMdLoc $ DebugLoc { dlLine = 12+                                           , dlCol = 34+                                           , dlScope = ValMdRef 5+                                           , dlIA = Nothing+                                           , dlImplicit = True+                                           , dlAtomGroup = 0+                                           , dlAtomRank = 0 })+             , ("moo", ValMdString @Value "cow")+             ]+  in+  [+    testCase "stmtMetadata Effect" $ stmtMetadata s1 @?= md1+  , testCase "stmtMetadata Result" $ stmtMetadata s2 @?= md1+  ]
test/Output.hs view
@@ -32,15 +32,23 @@         -- output are seen.         s1, s2 :: Stmt         s1 = Effect-             (GEP True (Alias (Ident "hi")) (Typed Opaque dcu) [])-             []+             (GEP [GEP_Inbounds] (Alias (Ident "hi")) (Typed Opaque dcu) [])+             mempty []         s2 = Effect (Load PtrOpaque (Typed Opaque ValNull) Nothing Nothing)+             mempty              [ ("location", ValMdLoc $ DebugLoc { dlLine = 12                                                 , dlCol = 34                                                 , dlScope = ValMdRef 5                                                 , dlIA = Nothing-                                                , dlImplicit = True })+                                                , dlImplicit = True+                                                , dlAtomGroup = 0+                                                , dlAtomRank = 0 })              ]+        s3 = Effect+               (IndirectBr+               (Typed PtrOpaque (ValIdent "addr"))+               [Named "hello", Named "world"])+               [] []         dcu :: Value         dcu = ValMd               $ ValMdDebugInfo@@ -66,6 +74,7 @@                               , dicuRangesBaseAddress = True                               , dicuSysRoot = Just "the root"                               , dicuSDK = Just "SDK"+                              , dicuSourceLanguageVersion = 0                               }         dtt = ValMdDebugInfo               $ DebugInfoTemplateTypeParameter@@ -75,10 +84,10 @@                                         }         blk1 = BasicBlock { bbLabel = Just $ Named $ Ident "blk1"                           , bbStmts =-                            [ Result (Ident "r1") (Comment "insanity follows...") []-                            , Effect (Jump $ Named $ Ident "blk1") []-                            , Result (Ident "oh no") RetVoid []-                            , Effect (Br (Typed (PrimType Metadata) ValZeroInit) (Anon 3) (Named "oh no")) []+                            [ Result (Ident "r1") (Comment "insanity follows...") mempty []+                            , Effect (Jump $ Named $ Ident "blk1") mempty []+                            , Result (Ident "oh no") RetVoid mempty []+                            , Effect (Br (Typed (PrimType Metadata) ValZeroInit) (Anon 3) (Named "oh no")) mempty []                             ]                           }         blk2 = BasicBlock { bbLabel = Just $ Anon 123@@ -167,6 +176,51 @@       ----       |] +  , testCase "Stmt 1, LLVM 18" $+    assertEqLines (ppToText $ ppLLVM 18 $ ppStmt s1) [sq|+      Significant changes occur in LLVM 19; ensure that the output is as+      expected just prior to those changes to properly capture the+      inflection point.+      ----+      getelementptr inbounds %hi, opaque !DICompileUnit(language: 12,+                                                        producer: "llvm-pretty-test",+                                                        isOptimized: true,+                                                        flags: "some flags",+                                                        runtimeVersion: 3,+                                                        emissionKind: 1,+                                                        enums: !DITemplateTypeParameter(name: ttp),+                                                        dwoId: 2,+                                                        splitDebugInlining: false,+                                                        debugInfoForProfiling: true,+                                                        nameTableKind: 4,+                                                        rangesBaseAddress: true,+                                                        sysroot: "the root",+                                                        sdk: "SDK")+      ----+      |]++  , testCase "Stmt 1, LLVM 19" $+    assertEqLines (ppToText $ ppLLVM 19 $ ppStmt s1) [sq|+      In LLVM 19, the GEP instruction "inbounds" is no longer a boolean+      but a flag with multiple possible values (only one is checked here).+      ----+      getelementptr inbounds %hi, opaque !DICompileUnit(language: 12,+                                                        producer: "llvm-pretty-test",+                                                        isOptimized: true,+                                                        flags: "some flags",+                                                        runtimeVersion: 3,+                                                        emissionKind: 1,+                                                        enums: !DITemplateTypeParameter(name: ttp),+                                                        dwoId: 2,+                                                        splitDebugInlining: false,+                                                        debugInfoForProfiling: true,+                                                        nameTableKind: 4,+                                                        rangesBaseAddress: true,+                                                        sysroot: "the root",+                                                        sdk: "SDK")+      ----+      |]+   ------------------------------------------------------------    , testCase "Stmt 2, LLVM 3.5" $@@ -203,6 +257,18 @@       (ppToText $ ppLLVM 10 $ ppStmt s2)    ------------------------------------------------------------++  , testCase "Stmt 3" $+    assertEqLines [sq|+      `indirectbr` pretty-printing works as expected (#174)+      ----+      indirectbr ptr %addr, [ label %hello,+                              label %world ]+      ----+      |]+      (ppToText $ ppLLVM llvmVlatest $ ppStmt s3)++  ------------------------------------------------------------   -- Verify named labels and label targets are emitted correctly    , testCase "Blk 1, LLVM 3.5" $@@ -244,25 +310,85 @@       --------       |] +  , testCase "Zero (half float)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValHalf (FPHalf 0x0000)))+      "0.0"++  , testCase "One (half float)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValHalf (FPHalf 0x3c00)))+      "1.0"++  , testCase "Two (half float)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValHalf (FPHalf 0x4000)))+      "2.0"++  , testCase "Positive Infinity (half float)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValHalf (FPHalf 0x7c00)))+      "0xH7c00"++  , testCase "Negative Infinity (half float)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValHalf (FPHalf 0xfc00)))+      "0xHfc00"++  , testCase "NaN (half float)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValHalf (FPHalf 0x7c55)))+      "0xH7c55"++  , testCase "Zero (bfloat)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValBFloat (FPBFloat 0x0000)))+      "0.0"++  , testCase "One (bfloat)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValBFloat (FPBFloat 0x3f80)))+      "1.0"++  , testCase "Two (bfloat)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValBFloat (FPBFloat 0x4000)))+      "2.0"++  , testCase "Positive Infinity (bfloat)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValBFloat (FPBFloat 0x7f80)))+      "0xR7f80"++  , testCase "Negative Infinity (bfloat)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValBFloat (FPBFloat 0xff80)))+      "0xRff80"++  , testCase "NaN (bfloat)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValBFloat (FPBFloat 0x7f85)))+      "0xR7f85"+   , testCase "Positive Infinity (float)" $     assertEqLines       (ppToText $ ppLLVM37 ppValue (ValFloat (castWord32ToFloat 0x7F800000)))-      "0x7f800000"+      "0x7ff0000000000000"    , testCase "Negative Infinity (float)" $     assertEqLines       (ppToText $ ppLLVM37 ppValue (ValFloat (castWord32ToFloat 0xFF800000)))-      "0xff800000"+      "0xfff0000000000000"    , testCase "NaN 1 (float)" $     assertEqLines       (ppToText $ ppLLVM37 ppValue (ValFloat (castWord32ToFloat 0x7FC00000)))-      "0x7fc00000"+      "0x7ff8000000000000"    , testCase "NaN 2 (float)" $     assertEqLines       (ppToText $ ppLLVM37 ppValue (ValFloat (castWord32ToFloat 0x7FD00000)))-      "0x7fd00000"+      "0x7ffa000000000000"    , testCase "Positive Infinity (double)" $     assertEqLines@@ -283,6 +409,21 @@     assertEqLines       (ppToText $ ppLLVM37 ppValue (ValDouble (castWord64ToDouble 0x7FFD000000000000)))       "0x7ffd000000000000"++  , testCase "Zero (FP80)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValFP80 (FP80_LongDouble 0x0000 0x0000000000000000)))+      "0xK00000000000000000000"++  , testCase "Zero (FP128)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValFP128 (FP128_LongDouble 0x0000000000000000 0x0000000000000000)))+      "0xL00000000000000000000000000000000"++  , testCase "Zero (FP128_PPC)" $+    assertEqLines+      (ppToText $ ppLLVM37 ppValue (ValFP128_PPC (FP128_PPC_DoubleDouble 0.0 0.0)))+      "0xM00000000000000000000000000000000"    ]