diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,8 @@
 # Changelog
 
+- 0.2.4 (2025-12-28)
+  * Adds an 'llvm' build flag and tests with GHC 9.10.3.
+
 - 0.2.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/SHA256.hs b/lib/Crypto/Hash/SHA256.hs
--- a/lib/Crypto/Hash/SHA256.hs
+++ b/lib/Crypto/Hash/SHA256.hs
@@ -99,6 +99,15 @@
   WSPair (unsafe_word32be (BI.BS x 4)) (BI.BS (plusForeignPtr x 4) (l - 4))
 {-# 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
 
@@ -110,9 +119,9 @@
 
 -- 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)
@@ -196,10 +205,8 @@
   padding l k bs
     | k == 0 =
           pure
-        . BL.toStrict
+        . to_strict
           -- more efficient for small builder
-        . BE.toLazyByteStringWith
-            (BE.safeStrategy 128 BE.smallChunkSize) mempty
         $ bs <> BSB.word64BE (l * 8)
     | otherwise =
         let nacc = bs <> BSB.word8 0x00
@@ -425,12 +432,9 @@
 
 -- register concatenation
 cat :: Registers -> BS.ByteString
-cat Registers {..} =
-      BL.toStrict
-      -- more efficient for small builder
-    . BE.toLazyByteStringWith (BE.safeStrategy 128 BE.smallChunkSize) mempty
-    $     BSB.word64BE w64_0 <> BSB.word64BE w64_1
-       <> BSB.word64BE w64_2 <> BSB.word64BE w64_3
+cat Registers {..} = to_strict_small $
+       BSB.word64BE w64_0 <> BSB.word64BE w64_1
+    <> BSB.word64BE w64_2 <> BSB.word64BE w64_3
   where
     !w64_0 = fi h0 `B.unsafeShiftL` 32 .|. fi h1
     !w64_1 = fi h2 `B.unsafeShiftL` 32 .|. fi h3
diff --git a/ppad-sha256.cabal b/ppad-sha256.cabal
--- a/ppad-sha256.cabal
+++ b/ppad-sha256.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               ppad-sha256
-version:            0.2.3
+version:            0.2.4
 synopsis:           The SHA-256 and HMAC-SHA256 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-256 and HMAC-SHA256 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/sha256.git
@@ -23,6 +28,8 @@
   hs-source-dirs:   lib
   ghc-options:
       -Wall
+  if flag(llvm)
+    ghc-options: -fllvm -O2
   exposed-modules:
       Crypto.Hash.SHA256
   build-depends:
@@ -43,8 +50,8 @@
   build-depends:
       aeson
     , base
-    , base16-bytestring
     , bytestring
+    , ppad-base16
     , ppad-sha256
     , 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 (SHA256.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 = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"
 
 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 = "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"
 
 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 = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"
 
 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 = "a3b6167473100ee06e0c796c2955552b"
 
 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"
