diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,8 @@
 # Changelog
 
+- 0.1.4 (2025-12-28)
+  * Adds an 'llvm' build flag and tests with GHC 9.10.3.
+
 - 0.1.3 (2025-01-21)
   * Improves padding handling for the strict bytestring case, yielding
     performance increases for both 'hash' and 'hmac'.
diff --git a/lib/Crypto/Hash/SHA512.hs b/lib/Crypto/Hash/SHA512.hs
--- a/lib/Crypto/Hash/SHA512.hs
+++ b/lib/Crypto/Hash/SHA512.hs
@@ -103,6 +103,15 @@
   WSPair (unsafe_word64be (BI.BS x 8)) (BI.BS (plusForeignPtr x 8) (l - 8))
 {-# INLINE unsafe_parseWsPair #-}
 
+-- builder realization strategies
+
+to_strict :: BSB.Builder -> BS.ByteString
+to_strict = BL.toStrict . BSB.toLazyByteString
+
+to_strict_small :: BSB.Builder -> BS.ByteString
+to_strict_small = BL.toStrict . BE.toLazyByteStringWith
+  (BE.safeStrategy 128 BE.smallChunkSize) mempty
+
 -- message padding and parsing ------------------------------------------------
 -- https://datatracker.ietf.org/doc/html/rfc6234#section-4.1
 
@@ -112,13 +121,11 @@
   let r = 112 - fi l `rem` 128 - 1 :: Integer -- fi prevents underflow
   in  fi (if r < 0 then r + 128 else r)
 
--- XX doesn't properly handle (> maxBound :: Word64) length
-
 -- RFC 6234 4.1 (strict)
 pad :: BS.ByteString -> BS.ByteString
-pad m@(BI.PS _ _ (fi -> l)) =
-    BL.toStrict . BE.toLazyByteStringWith
-      (BE.safeStrategy 128 BE.smallChunkSize) mempty $ padded
+pad m@(BI.PS _ _ (fi -> l))
+    | l < 128   = to_strict_small padded
+    | otherwise = to_strict padded
   where
     padded = BSB.byteString m
           <> fill (sol l) (BSB.word8 0x80)
@@ -203,10 +210,7 @@
   padding l k bs
     | k == 0 =
           pure
-        . BL.toStrict
-          -- more efficient for small builder
-        . BE.toLazyByteStringWith
-            (BE.safeStrategy 128 BE.smallChunkSize) mempty
+        . to_strict
         $ bs <> BSB.word64BE 0x00 <> BSB.word64BE (l * 8)
     | otherwise =
         let nacc = bs <> BSB.word8 0x00
@@ -500,14 +504,9 @@
 
 -- register concatenation
 cat :: Registers -> BS.ByteString
-cat Registers {..} =
-    BL.toStrict
-    -- more efficient for small builder
-  . BE.toLazyByteStringWith (BE.safeStrategy 128 BE.smallChunkSize) mempty
-  $ mconcat [
-        BSB.word64BE h0, BSB.word64BE h1, BSB.word64BE h2, BSB.word64BE h3
-      , BSB.word64BE h4, BSB.word64BE h5, BSB.word64BE h6, BSB.word64BE h7
-      ]
+cat Registers {..} = to_strict_small $
+     BSB.word64BE h0 <> BSB.word64BE h1 <> BSB.word64BE h2 <> BSB.word64BE h3
+  <> BSB.word64BE h4 <> BSB.word64BE h5 <> BSB.word64BE h6 <> BSB.word64BE h7
 
 -- | Compute a condensed representation of a strict bytestring via
 --   SHA-512.
diff --git a/ppad-sha512.cabal b/ppad-sha512.cabal
--- a/ppad-sha512.cabal
+++ b/ppad-sha512.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               ppad-sha512
-version:            0.1.3
+version:            0.1.4
 synopsis:           The SHA-512 and HMAC-SHA512 algorithms
 license:            MIT
 license-file:       LICENSE
@@ -8,12 +8,17 @@
 maintainer:         jared@ppad.tech
 category:           Cryptography
 build-type:         Simple
-tested-with:        GHC == { 9.8.1, 9.6.6, 9.6.4 }
+tested-with:        GHC == 9.10.3
 extra-doc-files:    CHANGELOG
 description:
   A pure implementation of SHA-512 and HMAC-SHA512 on strict and lazy
   ByteStrings, as specified by RFC's 6234 and 2104.
 
+flag llvm
+  description: Use GHC's LLVM backend.
+  default:     False
+  manual:      True
+
 source-repository head
   type:     git
   location: git.ppad.tech/sha512.git
@@ -23,6 +28,8 @@
   hs-source-dirs:   lib
   ghc-options:
       -Wall
+  if flag(llvm)
+    ghc-options: -fllvm -O2
   exposed-modules:
       Crypto.Hash.SHA512
   build-depends:
@@ -43,8 +50,8 @@
   build-depends:
       aeson
     , base
-    , base16-bytestring
     , bytestring
+    , ppad-base16
     , ppad-sha512
     , tasty
     , tasty-hunit
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -36,11 +36,16 @@
   where
     msg = "keysize " <> show mtg_keySize <> ", tagsize " <> show mtg_tagSize
 
+decodeLenient :: BS.ByteString -> BS.ByteString
+decodeLenient bs = case B16.decode bs of
+  Nothing -> error "bang"
+  Just b -> b
+
 execute :: Int -> W.MacTest -> TestTree
 execute tag_size W.MacTest {..} = testCase t_msg $ do
-    let key = B16.decodeLenient (TE.encodeUtf8 mt_key)
-        msg = B16.decodeLenient (TE.encodeUtf8 mt_msg)
-        pec = B16.decodeLenient (TE.encodeUtf8 mt_tag)
+    let key = decodeLenient (TE.encodeUtf8 mt_key)
+        msg = decodeLenient (TE.encodeUtf8 mt_msg)
+        pec = decodeLenient (TE.encodeUtf8 mt_tag)
         out = BS.take bytes (SHA512.hmac key msg)
     if   mt_result == "invalid"
     then assertBool "invalid" (pec /= out)
@@ -149,7 +154,7 @@
 -- https://datatracker.ietf.org/doc/html/rfc4231#section-4.1
 
 hmv1_key :: BS.ByteString
-hmv1_key = B16.decodeLenient "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"
+hmv1_key = decodeLenient "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"
 
 hmv1_put :: BS.ByteString
 hmv1_put = "Hi There"
@@ -167,25 +172,25 @@
 hmv2_pec = "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"
 
 hmv3_key :: BS.ByteString
-hmv3_key = B16.decodeLenient "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+hmv3_key = decodeLenient "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
 
 hmv3_put :: BS.ByteString
-hmv3_put = B16.decodeLenient "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
+hmv3_put = decodeLenient "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
 
 hmv3_pec :: BS.ByteString
 hmv3_pec = "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"
 
 hmv4_key :: BS.ByteString
-hmv4_key = B16.decodeLenient "0102030405060708090a0b0c0d0e0f10111213141516171819"
+hmv4_key = decodeLenient "0102030405060708090a0b0c0d0e0f10111213141516171819"
 
 hmv4_put :: BS.ByteString
-hmv4_put = B16.decodeLenient "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+hmv4_put = decodeLenient "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
 
 hmv4_pec :: BS.ByteString
 hmv4_pec = "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"
 
 hmv5_key :: BS.ByteString
-hmv5_key = B16.decodeLenient "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"
+hmv5_key = decodeLenient "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"
 
 hmv5_put :: BS.ByteString
 hmv5_put = "Test With Truncation"
@@ -194,7 +199,7 @@
 hmv5_pec = "415fad6271580a531d4179bc891d87a6"
 
 hmv6_key :: BS.ByteString
-hmv6_key = B16.decodeLenient "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+hmv6_key = decodeLenient "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
 
 hmv6_put :: BS.ByteString
 hmv6_put = "Test Using Larger Than Block-Size Key - Hash Key First"
