diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,77 @@
 # Revision history for llvm-pretty
 
+## 0.14.0.0 -- 2026-01-22
+
+* 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.
diff --git a/llvm-pretty.cabal b/llvm-pretty.cabal
--- a/llvm-pretty.cabal
+++ b/llvm-pretty.cabal
@@ -1,6 +1,6 @@
 Cabal-version:       2.2
 Name:                llvm-pretty
-Version:             0.12.1.0
+Version:             0.14.0.0
 License:             BSD-3-Clause
 License-file:        LICENSE
 Author:              Trevor Elliott
@@ -13,7 +13,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.3, GHC==9.10.1, GHC==9.8.4
 extra-doc-files:     CHANGELOG.md, README.md
 
 
@@ -25,6 +25,7 @@
   Default-language:    Haskell2010
   Ghc-options:
     -Wall
+    -fhide-source-paths
 
 Library
   Import:              common
@@ -55,13 +56,15 @@
                        microlens-th     >= 0.4,
                        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:
+    DataLayout
+    Metadata
     Output
     Triple
     TQQDefs
diff --git a/src/Text/LLVM.hs b/src/Text/LLVM.hs
--- a/src/Text/LLVM.hs
+++ b/src/Text/LLVM.hs
@@ -40,6 +40,8 @@
 
     -- * Basic Blocks
   , BB()
+  , runBB
+  , bbStmtModifier
   , freshLabel
   , label
   , comment
@@ -313,9 +315,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 +361,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 +395,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 +537,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 +669,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 +690,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 +705,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 +725,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 ()
diff --git a/src/Text/LLVM/AST.hs b/src/Text/LLVM/AST.hs
--- a/src/Text/LLVM/AST.hs
+++ b/src/Text/LLVM/AST.hs
@@ -9,12 +9,15 @@
 incomplete: there are some values that new LLVM versions would accept but are
 not yet represented here.
 -}
+
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE ViewPatterns #-}
 {-# LANGUAGE DeriveDataTypeable, DeriveFunctor, DeriveGeneric #-}
 {-# LANGUAGE PatternGuards #-}
 {-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DeriveLift #-}
+
 module Text.LLVM.AST
   ( -- * Modules
     Module(..)
@@ -28,6 +31,12 @@
     -- * Data Layout
   , DataLayout
   , LayoutSpec(..)
+  , Alignment(..)
+  , FunctionPointerAlignType(..)
+  , Storage(..)
+  , PointerSize(..)
+  , AddressSpace
+  , NumBits
   , Mangling(..)
   , parseDataLayout
     -- * Inline Assembly
@@ -131,8 +140,12 @@
   , stmtInstr
   , stmtMetadata
   , extendMetadata
+  , addDebugRecord
     -- * Constant Expressions
   , ConstExpr'(..), ConstExpr
+  , GEPAttr(..)
+  , orderedGEPAttrs
+  , RangeSpec(RangeIndex, Range)
     -- * DWARF Debug Info
   , DebugInfo'(..), DebugInfo
   , DILabel, DILabel'(..)
@@ -146,7 +159,7 @@
   , DwarfVirtuality
   , DIFlags
   , DIEmissionKind
-  , DIBasicType(..)
+  , DIBasicType'(..), DIBasicType
   , DICompileUnit'(..), DICompileUnit
   , DICompositeType'(..), DICompositeType
   , DIDerivedType'(..), DIDerivedType
@@ -161,6 +174,13 @@
   , DISubrange'(..), DISubrange
   , 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 +192,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,7 +230,7 @@
   , 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
@@ -254,7 +275,7 @@
 data NamedMd = NamedMd
   { nmName   :: String
   , nmValues :: [Int]
-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)
+  } deriving (Data, Eq, Generic, Ord, Show)
 
 
 -- Unnamed Metadata ------------------------------------------------------------
@@ -263,7 +284,7 @@
   { umIndex    :: !Int
   , umValues   :: ValMd
   , umDistinct :: Bool
-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)
+  } deriving (Data, Eq, Generic, Ord, Show)
 
 
 -- Aliases ---------------------------------------------------------------------
@@ -274,38 +295,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 +379,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 +404,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 +469,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 +482,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,7 +503,7 @@
   | FloatType FloatType
   | X86mmx
   | Metadata
-    deriving (Data, Eq, Generic, Ord, Show, Typeable, Lift)
+    deriving (Data, Eq, Generic, Ord, Show, Lift)
 
 data FloatType
   = Half
@@ -418,7 +512,7 @@
   | 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 +548,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 +574,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 +590,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 +704,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
@@ -678,7 +772,7 @@
 data TypeDecl = TypeDecl
   { typeName  :: Ident
   , typeValue :: Type
-  } deriving (Data, Eq, Generic, Ord, Show, Typeable)
+  } deriving (Data, Eq, Generic, Ord, Show)
 
 
 -- Globals ---------------------------------------------------------------------
@@ -690,7 +784,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 +793,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 +814,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 +836,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 +877,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 +894,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
 
@@ -834,23 +928,23 @@
   | External
   | 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 +1014,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 +1032,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 +1069,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 +1137,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 +1146,7 @@
   | Release
   | AcqRel
   | SeqCst
-    deriving (Data, Eq, Enum, Generic, Ord, Show, Typeable)
+    deriving (Data, Eq, Enum, Generic, Ord, Show)
 
 type Align = Int
 
@@ -1129,10 +1255,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 +1275,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 +1329,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 +1350,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 +1369,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,15 +1450,89 @@
 
 -- | 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
@@ -1303,12 +1556,12 @@
   | 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
 
 data FP80Value = FP80_LongDouble Word16 Word64
-               deriving (Data, Eq, Ord, Generic, Show, Typeable)
+               deriving (Data, Eq, Ord, Generic, Show)
 
 data ValMd' lab
   = ValMdString String
@@ -1317,7 +1570,7 @@
   | 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,7 +1584,9 @@
   , 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
 
@@ -1358,36 +1613,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 +1681,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 +1763,8 @@
   | 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.
+    deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)
 
 type DebugInfo = DebugInfo' BlockLabel
 
@@ -1437,7 +1774,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 +1787,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 +1803,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 +1811,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 +1826,27 @@
 -- 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.
+  } deriving (Data, Eq, Functor, Generic, Ord, Show)
 
+type DIBasicType = DIBasicType' BlockLabel
+
 data DICompileUnit' lab = DICompileUnit
   { dicuLanguage           :: DwarfLang
   , dicuFile               :: Maybe (ValMd' lab)
@@ -1518,7 +1870,7 @@
   , dicuRangesBaseAddress  :: Bool
   , dicuSysRoot            :: Maybe String
   , dicuSDK                :: Maybe String
-  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show, Typeable)
+  } deriving (Data, Eq, Functor, Generic, Generic1, Ord, Show)
 
 type DICompileUnit = DICompileUnit' BlockLabel
 
@@ -1529,9 +1881,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 +1905,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 +1921,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 +1943,18 @@
   -- 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
 
 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 +1970,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 +1986,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 +1994,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 +2010,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 +2037,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 +2069,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 +2093,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
diff --git a/src/Text/LLVM/DebugUtils.hs b/src/Text/LLVM/DebugUtils.hs
--- a/src/Text/LLVM/DebugUtils.hs
+++ b/src/Text/LLVM/DebugUtils.hs
@@ -230,16 +230,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 +377,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 +388,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
diff --git a/src/Text/LLVM/Labels.hs b/src/Text/LLVM/Labels.hs
--- a/src/Text/LLVM/Labels.hs
+++ b/src/Text/LLVM/Labels.hs
@@ -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,16 @@
 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 DIDerivedType'              where relabel = $(generateRelabel 'relabel ''DIDerivedType')
 instance HasLabel DISubroutineType'           where relabel = $(generateRelabel 'relabel ''DISubroutineType')
 instance HasLabel DISubrange'                 where relabel = $(generateRelabel 'relabel ''DISubrange')
diff --git a/src/Text/LLVM/Lens.hs b/src/Text/LLVM/Lens.hs
--- a/src/Text/LLVM/Lens.hs
+++ b/src/Text/LLVM/Lens.hs
@@ -31,7 +31,7 @@
     , ''DebugInfo'
     , ''DIFile
     , ''DISubrange'
-    , ''DIBasicType
+    , ''DIBasicType'
     , ''DIExpression
     , ''DISubprogram'
     , ''DISubroutineType'
diff --git a/src/Text/LLVM/PP.hs b/src/Text/LLVM/PP.hs
--- a/src/Text/LLVM/PP.hs
+++ b/src/Text/LLVM/PP.hs
@@ -24,7 +24,7 @@
 import Control.Applicative ((<|>))
 import Data.Bits ( shiftR, (.&.) )
 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)
@@ -73,7 +73,7 @@
 -- this is used for defaulting and otherwise reporting the maximum LLVM version
 -- known to be supported.
 llvmVlatest :: LLVMVer
-llvmVlatest = 17
+llvmVlatest = 19
 
 
 -- | The differences between various versions of the llvm textual AST.
@@ -206,32 +206,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 -------------------------------------------------------------
@@ -313,8 +340,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
@@ -443,9 +477,11 @@
 
 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 +559,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 +635,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 +652,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 +675,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 +810,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
@@ -885,13 +925,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 +981,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 +1058,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
@@ -981,7 +1080,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 +1161,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,18 +1207,20 @@
 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
+       ])
 
 ppDICompileUnit' :: Fmt i -> Fmt (DICompileUnit' i)
 ppDICompileUnit' pp cu = "!DICompileUnit"
@@ -1102,9 +1271,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 +1286,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 +1306,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
@@ -1351,7 +1526,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 +1562,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
diff --git a/src/Text/LLVM/Triple/AST.hs b/src/Text/LLVM/Triple/AST.hs
--- a/src/Text/LLVM/Triple/AST.hs
+++ b/src/Text/LLVM/Triple/AST.hs
@@ -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
diff --git a/test/DataLayout.hs b/test/DataLayout.hs
new file mode 100644
--- /dev/null
+++ b/test/DataLayout.hs
@@ -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
+     ]
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -2,12 +2,16 @@
 
 import qualified Test.Tasty as Tasty
 
-import qualified Triple
+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
        ]
diff --git a/test/Metadata.hs b/test/Metadata.hs
new file mode 100644
--- /dev/null
+++ b/test/Metadata.hs
@@ -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
+  ]
diff --git a/test/Output.hs b/test/Output.hs
--- a/test/Output.hs
+++ b/test/Output.hs
@@ -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
@@ -75,10 +83,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 +175,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" $
@@ -201,6 +254,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
