diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,11 @@
 # Revision history for Z-Data
 
+## 0.8.8.0  -- 2021-06-13
+
+* Add `withCPtrForever` and `addCPtrDep` to `Z.Foreign.CPtr` module.
+* Add `replicateMVec` to `Z.Data.Vector`.
+* Fix AVX512 text validate header building with C11.
+
 ## 0.8.7.0  -- 2021-06-12
 
 * Add `use-avx2` and `use-avx512` flags(default to False) to enable avx2 or avx512 base64 codec, UTF8 validator, etc.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -19,7 +19,14 @@
 
 ## Requirements
 
-* A working haskell compiler system, GHC(>=8.6), cabal-install(>=2.4), hsc2hs.
+* A working haskell compiler system, GHC(>=8.6), cabal-install(>=2.4), here're some options:
+    * Mac users can get them via [homebew](//brew.sh/): `brew install ghc cabal-install`.
+    * Windows users can get them via [chocolatey](//chocolatey.org): `choco install ghc cabal`.
+    * Ubuntu users are recommended to use this [ppa](//launchpad.net/~hvr/+archive/ubuntu/ghc).
+* A working C/C++ compiler support C++11, here're some options:
+    * Mac users can use the `clang` comes with the [XCode Command Line Tools](https://developer.apple.com/downloads).
+    * Windows users can use the mingw's one comes with GHC, you can use it by adding `your_path_to_ghc\mingw\bin` to your `PATH`.
+    * Ubuntu users can install `gcc/g++` by running `sudo apt install build-essential`.
 * Tests need [hspec-discover](https://hackage.haskell.org/package/hspec-discover).
 
 ## Example usage
diff --git a/Z-Data.cabal b/Z-Data.cabal
--- a/Z-Data.cabal
+++ b/Z-Data.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               Z-Data
-version:            0.8.7.0
+version:            0.8.8.0
 synopsis:           Array, vector and text
 description:        This package provides array, slice and text operations
 license:            BSD-3-Clause
@@ -23,6 +23,8 @@
   README.md
   third_party/fastvalidate-utf-8/include/simdasciicheck.h
   third_party/fastvalidate-utf-8/include/simdutf8check.h
+  third_party/fastvalidate-utf-8/include/simdasciicheck_avx512.h
+  third_party/fastvalidate-utf-8/include/simdutf8check_avx512.h
   third_party/fastbase64/include/chromiumbase64.h
   third_party/fastbase64/include/fastavxbase64.h
   third_party/fastbase64/src/fastavxbase64.c
@@ -119,7 +121,7 @@
 
 flag use-avx512
   description:
-    Use AVX512 instructions(utf8 validation, base64 codec, etc).
+    Use AVX512(AVX512F, AVX512VL, AVX512BW, AVX512VBMI) instructions(utf8 validation, base64 codec, etc).
 
   default:     False
   manual:      True
@@ -202,21 +204,24 @@
     third_party/re2
 
   includes:
-    simdasciicheck.h
-    simdutf8check.h
     utf8rewind.h
     chromiumbase64.h
 
-  if flag(use-avx2)
-      includes:
-        fastavxbase64.h
+  if arch(x86_64)
+    includes:
+      simdasciicheck.h
+      simdutf8check.h
 
-  if flag(use-avx512)
-      includes:
-        simdasciicheck_avx512.h
-        simdutf8check_avx512.h
-        fastavx512bwbase64.h
+    if flag(use-avx2)
+        includes:
+          fastavxbase64.h
 
+    if flag(use-avx512)
+        includes:
+          simdasciicheck_avx512.h
+          simdutf8check_avx512.h
+          fastavx512bwbase64.h
+
   c-sources:
     cbits/bytes.c
     cbits/dtoa.c
@@ -233,13 +238,14 @@
     third_party/utf8rewind/source/unicodedatabase.c
     third_party/utf8rewind/source/utf8rewind.c
 
-  if flag(use-avx2)
-      c-sources:
-        third_party/fastbase64/src/fastavxbase64.c
+  if arch(x86_64)
+    if flag(use-avx2)
+        c-sources:
+          third_party/fastbase64/src/fastavxbase64.c
 
-  if flag(use-avx512)
-      c-sources:
-        third_party/fastbase64/src/fastavx512bwbase64.c
+    if flag(use-avx512)
+        c-sources:
+          third_party/fastbase64/src/fastavx512bwbase64.c
 
   cxx-sources:
     cbits/regex.cc
@@ -269,22 +275,25 @@
   default-language:   Haskell2010
   build-tool-depends: hsc2hs:hsc2hs -any
 
-  cc-options:         -std=c11 -Wno-sign-compare
+  cc-options:         -std=c11 -Wall -Wno-pointer-to-int-cast
+  cxx-options:        -std=c++11
 
-  if flag(use-avx512)
-    cc-options:       -march=cannonlake -DZ_USE_AVX512
-  else
-    if flag(use-avx2)
-      cc-options:     -mavx2 -DZ_USE_AVX2
+  if arch(x86_64)
+    if flag(use-avx512)
+      cc-options:       -march=cannonlake
+      cxx-options:      -march=cannonlake
     else
-      cc-options:     -msse4.2
-
-  cxx-options:        -std=c++11
+      if flag(use-avx2)
+        cc-options:     -mavx2
+        cxx-options:    -mavx2
+      else
+        cc-options:     -msse4.2
+        cxx-options:    -msse4.2
 
   if os(windows)
     extra-libraries: stdc++
     cc-options:      -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600
-    cxx-options:      -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600
+    cxx-options:     -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600
   else
     if os(osx)
       extra-libraries:
@@ -306,7 +315,7 @@
   -- we work around this issue using Setup.hs
 
   ghc-options:
-    -Wall -Wno-unticked-promoted-constructors -Wno-incomplete-patterns
+    -Wall -Wno-unticked-promoted-constructors  -Wno-incomplete-patterns
 
   default-extensions:
     BangPatterns
diff --git a/Z/Data/Builder/Numeric.hs b/Z/Data/Builder/Numeric.hs
--- a/Z/Data/Builder/Numeric.hs
+++ b/Z/Data/Builder/Numeric.hs
@@ -516,7 +516,7 @@
             let !i = fromIntegral v .&. 0xFF; !j = i + i
             writePrimArray marr off $ indexOffPtr hexDigitTable j
             writePrimArray marr (off + 1) $ indexOffPtr hexDigitTable (j+1)
-        | d < 0  = do         -- for FiniteBits instances which has extra bits
+        | otherwise = do         -- for FiniteBits instances which has extra bits
             let !i = fromIntegral v .&. 0x0F :: Int
             writePrimArray marr off $ i2wHex i
 
@@ -538,7 +538,7 @@
             let !i = fromIntegral v .&. 0xFF; !j = i + i
             writePrimArray marr off $ indexOffPtr hexDigitTableUpper j
             writePrimArray marr (off + 1) $ indexOffPtr hexDigitTableUpper (j+1)
-        | d < 0  = do         -- for FiniteBits instances which has extra bits
+        | otherwise = do         -- for FiniteBits instances which has extra bits
             let !i = fromIntegral v .&. 0x0F :: Int
             writePrimArray marr off $ i2wHexUpper i
 
diff --git a/Z/Data/CBytes.hs b/Z/Data/CBytes.hs
--- a/Z/Data/CBytes.hs
+++ b/Z/Data/CBytes.hs
@@ -355,6 +355,7 @@
                 let i' = i + l
                 writePrimArray mba i' w8
                 copy bs (i'+1) mba
+    copy _ _ _ = error "Z.Data.CBytes.intercalateElem: impossible"
 
 instance IsString CBytes where
     {-# INLINE fromString #-}
diff --git a/Z/Data/Parser/Numeric.hs b/Z/Data/Parser/Numeric.hs
--- a/Z/Data/Parser/Numeric.hs
+++ b/Z/Data/Parser/Numeric.hs
@@ -108,8 +108,7 @@
 w2iHex w
     | w <= 57   = fromIntegral w - 48
     | w <= 70   = fromIntegral w - 55
-    | w <= 102  = fromIntegral w - 87
-
+    | otherwise = fromIntegral w - 87
 
 -- | Same with 'uint', but sliently cast in case of overflow.
 uint_ :: forall a. (Integral a, Bounded a) => Parser a
diff --git a/Z/Data/Vector.hs b/Z/Data/Vector.hs
--- a/Z/Data/Vector.hs
+++ b/Z/Data/Vector.hs
@@ -96,6 +96,7 @@
   , mapAccumR
   -- ** Generating and unfolding vector
   , replicate
+  , replicateMVec
   , cycleN
   , unfoldr
   , unfoldrN
diff --git a/Z/Data/Vector/Base.hs b/Z/Data/Vector/Base.hs
--- a/Z/Data/Vector/Base.hs
+++ b/Z/Data/Vector/Base.hs
@@ -56,6 +56,7 @@
   , mapAccumR
   -- ** Generating and unfolding vector
   , replicate
+  , replicateMVec
   , cycleN
   , unfoldr
   , unfoldrN
@@ -85,6 +86,7 @@
 import           Control.Exception
 import           Control.Monad
 import           Control.Monad.ST
+import           Control.Monad.Primitive
 import           Data.Bits
 import           Data.Char                      (ord)
 import qualified Data.Foldable                  as F
@@ -338,51 +340,33 @@
             | i >= end  = salt
             | otherwise = go (h salt (indexArr arr i)) (i+1)
 
--- | Traverse vector and gather result in another vector,
+-- | Traverse vector and gather result in another vector.
+--
+-- There're rules to optimize the intermedia list away when @f@ is an instance of 'PrimMoand',
+-- such as 'IO', 'ST' or 'Z.Data.Parser.Parser'.
 traverseVec :: (Vec v a, Vec u b, Applicative f) => (a -> f b) -> v a -> f (u b)
 {-# INLINE [1] traverseVec #-}
-{-# RULES "traverseVec/ST" forall f. traverseVec f = traverseWithIndexST (const f) #-}
-{-# RULES "traverseVec/IO" forall f. traverseVec f = traverseWithIndexIO (const f) #-}
+{-# RULES "traverseVec/PrimMonad" forall (f :: PrimMonad m => a -> m b). traverseVec f = traverseWithIndexPM (const f) #-}
 traverseVec f v = packN (length v) <$> T.traverse f (unpack v)
 
--- | Traverse vector and gather result in another vector,
+-- | Traverse vector and gather result in another vector.
 traverseWithIndex :: (Vec v a, Vec u b, Applicative f) => (Int -> a -> f b) -> v a -> f (u b)
 {-# INLINE [1] traverseWithIndex #-}
-{-# RULES "traverseWithIndex/ST" traverseWithIndex = traverseWithIndexST #-}
-{-# RULES "traverseWithIndex/IO" traverseWithIndex = traverseWithIndexIO #-}
+{-# RULES "traverseWithIndex/PrimMonad" forall (f :: PrimMonad m => Int -> a -> m b). traverseWithIndex f = traverseWithIndexPM f #-}
 traverseWithIndex f v = packN (length v) <$> zipWithM f [0..] (unpack v)
 
-traverseWithIndexST :: forall v u a b s. (Vec v a, Vec u b) => (Int -> a -> ST s b) -> v a -> ST s (u b)
-{-# INLINE traverseWithIndexST #-}
-traverseWithIndexST f (Vec arr s l)
-    | l == 0    = return empty
-    | otherwise = do
-        marr <- newArr l
-        go marr 0
-        ba <- unsafeFreezeArr marr
-        return $! fromArr ba 0 l
-  where
-    go :: MArr (IArray u) s b -> Int -> ST s ()
-    go !marr !i
-        | i >= l = return ()
-        | otherwise = do
-            x <- indexArrM arr (i+s)
-            writeArr marr i =<< f i x
-            go marr (i+1)
-
-traverseWithIndexIO :: forall v u a b. (Vec v a, Vec u b) => (Int -> a -> IO b) -> v a -> IO (u b)
-{-# INLINE traverseWithIndexIO #-}
-traverseWithIndexIO f (Vec arr s l)
+traverseWithIndexPM :: forall m v u a b. (PrimMonad m, Vec v a, Vec u b) => (Int -> a -> m b) -> v a -> m (u b)
+{-# INLINE traverseWithIndexPM #-}
+traverseWithIndexPM f (Vec arr s l)
     | l == 0    = return empty
     | otherwise = do
         marr <- newArr l
-        go marr 0
-        ba <- unsafeFreezeArr marr
+        ba <- go marr 0
         return $! fromArr ba 0 l
   where
-    go :: MArr (IArray u) RealWorld b -> Int -> IO ()
+    go :: MArr (IArray u) (PrimState m) b -> Int -> m (IArray u b)
     go !marr !i
-        | i >= l = return ()
+        | i >= l = unsafeFreezeArr marr
         | otherwise = do
             x <- indexArrM arr (i+s)
             writeArr marr i =<< f i x
@@ -563,9 +547,7 @@
 -- | /O(n)/, pack an ASCII 'String', multi-bytes char WILL BE CHOPPED!
 packASCII :: String -> Bytes
 {-# INLINE CONLIKE [0] packASCII #-}
-{-# RULES
-    "packASCII/packASCIIAddr" forall addr . packASCII (unpackCString# addr) = packASCIIAddr addr
-  #-}
+{-# RULES "packASCII/packASCIIAddr" forall addr . packASCII (unpackCString# addr) = packASCIIAddr addr #-}
 packASCII = pack . fmap (fromIntegral . ord)
 
 packASCIIAddr :: Addr# -> Bytes
@@ -735,7 +717,7 @@
 -- This function is a /good consumer/ in the sense of build/foldr fusion.
 --
 packN :: forall v a. Vec v a => Int -> [a] -> v a
-{-# INLINE packN #-}
+{-# INLINE [1] packN #-}
 packN n0 = \ ws0 -> runST (do let n = max 4 n0
                               marr <- newArr n
                               (IPair i marr') <- foldM go (IPair 0 marr) ws0
@@ -755,6 +737,31 @@
                 !marr' <- resizeMutableArr marr n'
                 writeArr marr' i x
                 return (IPair (i+1) marr')
+
+
+-- | A version of 'replicateM' which works on 'Vec', with specialized rules under 'PrimMonad'.
+--
+-- There're rules to optimize the intermedia list away when m is an instance of 'PrimMoand',
+-- such as 'IO', 'ST' or 'Z.Data.Parser.Parser'.
+replicateMVec :: (Applicative f, Vec v a) => Int -> f a -> f (v a)
+{-# INLINE [1] replicateMVec #-}
+{-# RULES "replicateMVec/PrimMonad" forall n (x :: IO a). replicateMVec n x = replicatePMVec n x #-}
+replicateMVec n f = packN n <$> replicateM n f
+
+-- | A version of 'replicateM' which works on 'PrimMonad' and 'Vec'.
+replicatePMVec :: (PrimMonad m, Vec v a) => Int -> m a -> m (v a)
+{-# INLINE replicatePMVec #-}
+replicatePMVec n f = do
+    marr <- newArr n
+    ba <- go marr 0
+    (return $! fromArr ba 0 n)
+  where
+    go marr i
+        | i >= n = unsafeFreezeArr marr
+        | otherwise = do
+            x <- f
+            writeArr marr i x
+            go marr (i+1)
 
 -- | /O(n)/ Convert a list into a vector with given size.
 --
diff --git a/Z/Data/Vector/Hex.hs b/Z/Data/Vector/Hex.hs
--- a/Z/Data/Vector/Hex.hs
+++ b/Z/Data/Vector/Hex.hs
@@ -34,7 +34,6 @@
 import           GHC.Stack
 import           System.IO.Unsafe
 import qualified Z.Data.Vector.Base         as V
-import qualified Z.Data.Vector.Extra        as V
 import qualified Z.Data.Builder.Base        as B
 import qualified Z.Data.Text.Base           as T
 import qualified Z.Data.Text.Print          as T
@@ -74,7 +73,6 @@
             if upper
             then hs_hex_encode_upper buf# 0 parr s l
             else hs_hex_encode buf# 0 parr s l
-
 
 -- | 'B.Builder' version of 'hexEncode'.
 hexEncodeBuilder :: Bool -- ^ uppercase?
diff --git a/Z/Foreign/CPtr.hs b/Z/Foreign/CPtr.hs
--- a/Z/Foreign/CPtr.hs
+++ b/Z/Foreign/CPtr.hs
@@ -12,7 +12,9 @@
 
 module Z.Foreign.CPtr (
   -- * CPtr type
-    CPtr, newCPtr', newCPtrUnsafe, newCPtr, withCPtr, withCPtrsUnsafe, withCPtrs
+    CPtr, newCPtr', newCPtrUnsafe, newCPtr
+  , withCPtr, withCPtrsUnsafe, withCPtrForever, withCPtrs
+  , addCPtrDep
   -- * Ptr type
   , Ptr
   , nullPtr
@@ -21,11 +23,11 @@
 
 import Control.Monad
 import Control.Monad.Primitive
-import Control.Exception                    (mask_)
 import Data.Primitive.PrimArray
 import qualified Z.Data.Text                as T
 import GHC.Ptr
 import GHC.Exts
+import GHC.IO
 import Z.Data.Array
 import Z.Foreign
 
@@ -52,6 +54,7 @@
 newCPtr' :: IO (Ptr a) -- ^ initializer
          -> FunPtr (Ptr a -> IO b) -- ^ finalizer
          -> IO (CPtr a)
+{-# INLINABLE newCPtr' #-}
 newCPtr' ini (FunPtr fin#) = mask_ $ do
     mpa <- newPrimArray 1
     p@(Ptr addr#) <- ini
@@ -70,6 +73,7 @@
 newCPtrUnsafe :: (MutableByteArray# RealWorld -> IO r) -- ^ initializer
               -> FunPtr (Ptr a -> IO b) -- ^ finalizer
               -> IO (CPtr a, r)
+{-# INLINABLE newCPtrUnsafe #-}
 newCPtrUnsafe ini (FunPtr fin#) = mask_ $ do
     mpa@(MutablePrimArray mba#) <- newPrimArray 1
     r <- ini mba#
@@ -88,6 +92,7 @@
 newCPtr :: (Ptr (Ptr a) -> IO r) -- ^ initializer
         -> FunPtr (Ptr a -> IO b) -- ^ finalizer
         -> IO (CPtr a, r)
+{-# INLINABLE newCPtr #-}
 newCPtr ini (FunPtr fin#) = mask_ $ do
     mpa <- newPinnedPrimArray 1
     r <- ini (mutablePrimArrayContents mpa)
@@ -99,15 +104,36 @@
         in s2#
     return (CPtr pa, r)
 
--- | The only way to use 'CPtr' as a 'Ptr' in FFI is to use 'withCPtr'.
+-- | Use 'CPtr' as a 'Ptr' in FFI.
 withCPtr :: CPtr a -> (Ptr a -> IO b) -> IO b
+{-# INLINABLE withCPtr #-}
 withCPtr (CPtr pa@(PrimArray ba#)) f = do
     r <- f (indexPrimArray pa 0)
     primitive_ (touch# ba#)
     return r
 
+-- | Use 'CPtr' as a 'Ptr' in FFI(potentially running forever).
+--
+-- When you pass a forever loop to 'withCPtr', GHC's simplifier may think the 'touch#'(which keep the 'CPtr' alive) after the loop are unreachable,
+-- so it may be optimized away, 'withCPtrForever' solves that.
+--
+withCPtrForever :: CPtr a -> (Ptr a -> IO b) -> IO b
+#if MIN_VERSION_base(4,15,0)
+{-# INLINABLE withCPtrForever #-}
+withCPtrForever (CPtr pa@(PrimArray ba#)) f = IO $ \ s ->
+    case f (indexPrimArray pa 0) of
+        IO action# -> keepAlive# ba# s action#
+#else
+{-# NOINLINE withCPtrForever #-}
+withCPtrForever (CPtr pa@(PrimArray ba#)) f = do
+    r <- f (indexPrimArray pa 0)
+    primitive_ (touch# ba#)
+    return r
+#endif
+
 -- | Pass a list of 'CPtr Foo' as @foo**@. USE THIS FUNCTION WITH UNSAFE FFI ONLY!
 withCPtrsUnsafe :: forall a b. [CPtr a] -> (BA# (Ptr a) -> Int -> IO b) -> IO b
+{-# INLINABLE withCPtrsUnsafe #-}
 withCPtrsUnsafe cptrs f = do
     mpa <- newPrimArray @IO @(Ptr a) len
     foldM_ (\ !i (CPtr pa) ->
@@ -120,6 +146,7 @@
 
 -- | Pass a list of 'CPtr Foo' as @foo**@.
 withCPtrs :: forall a b. [CPtr a] -> (Ptr (Ptr a) -> Int -> IO b) -> IO b
+{-# INLINABLE withCPtrs #-}
 withCPtrs cptrs f = do
     mpa <- newPinnedPrimArray @IO @(Ptr a) len
     foldM_ (\ !i (CPtr pa) ->
@@ -128,3 +155,19 @@
     primitive_ (touch# cptrs)
     return r
   where len = length cptrs
+
+-- | @addCPtrDep a b@ make @b@\'s life depends on @a@\'s, so that @b@ is guaranteed to outlive @a@.
+--
+-- This function is useful when you want to create life dependency among 'CPtr's,
+-- be careful about the cost of collecting weak pointers though, see
+-- <http://blog.ezyang.com/2014/05/the-cost-of-weak-pointers-and-finalizers-in-ghc/>.
+-- e.g. If three 'CPtr's form a dependency chain, the dead weak list may get traversed three times.
+-- So instead of adding dependencies in a chain or one by one, you should add them in a single call
+-- with a tuple like @addCPtrDep root (fieldA, fieldB, ...)@.
+--
+addCPtrDep :: CPtr a -> b -> IO ()
+{-# INLINABLE addCPtrDep #-}
+addCPtrDep (CPtr (PrimArray ba#)) b =
+    primitive_ $ \ s0# ->
+        let !(# s1#, _ #) = mkWeakNoFinalizer# ba# b s0#
+        in s1#
diff --git a/cbits/bytes.c b/cbits/bytes.c
--- a/cbits/bytes.c
+++ b/cbits/bytes.c
@@ -36,9 +36,9 @@
 #if defined(__x86_64__)
 #include <x86intrin.h>
 #endif
-#if defined(Z_USE_AVX512)
+#if defined(__AVX512BW__)
 #include <fastavx512bwbase64.h>
-#elif defined(Z_USE_AVX2)
+#elif defined(__AVX2__)
 #include <fastavxbase64.h>
 #endif
 
@@ -142,12 +142,12 @@
         mask = mask << 16 | mask;
         for (i = 32; i < LBLOCKSIZE * 8; i <<= 1)
             mask = (mask << i) | mask;
-            while (length >= LBLOCKSIZE) {
-                if (DETECTCHAR (*asrc, mask))
-                    break;
-                length -= LBLOCKSIZE;
-                asrc--;
-            }
+        while (length >= LBLOCKSIZE) {
+            if (DETECTCHAR (*asrc, mask))
+                break;
+            length -= LBLOCKSIZE;
+            asrc--;
+        }
         /* If there are fewer than LBLOCKSIZE characters left,
          then we resort to the bytewise loop.  */
         src = (unsigned char *) asrc + LBLOCKSIZE - 1;
@@ -293,9 +293,9 @@
 // Base64 codec
 
 void hs_base64_encode(char* output, HsInt output_off, const char* input, HsInt off, HsInt len){
-#if defined(Z_USE_AVX512)
+#if defined(__AVX512BW__)
     fast_avx512bw_base64_encode(output+output_off, input+off, len);
-#elif defined(Z_USE_AVX2)
+#elif defined(__AVX2__)
     fast_avx2_base64_encode(output+output_off, input+off, len);
 #else
     chromium_base64_encode(output+output_off, input+off, len);
@@ -303,9 +303,9 @@
 }
 
 HsInt hs_base64_decode(char* output, const char* input, HsInt off, HsInt len){
-#if defined(Z_USE_AVX512)
+#if defined(__AVX512BW__)
     size_t r = fast_avx512bw_base64_decode(output, input+off, len);
-#elif defined(Z_USE_AVX2)
+#elif defined(__AVX2__)
     size_t r = fast_avx2_base64_decode(output, input+off, len);
 #else
     size_t r = chromium_base64_decode(output, input+off, len);
@@ -352,7 +352,7 @@
 
 // this function should be called with large enough len, otherwise underflow will happen.
 size_t hs_count_simd(unsigned char *str, size_t len, unsigned char w) {
-#if defined(Z_USE_AVX2) 
+#if defined(__AVX2__) 
     __m256i pat = _mm256_set1_epi8(w);
     size_t prefix = 0, res = 0;
     size_t i = 0;
@@ -457,7 +457,7 @@
 }
 
 HsInt hs_count_ba(unsigned char *str, HsInt off, HsInt len, unsigned char w) {
-#if defined(__SSE4_2__) || defined(Z_USE_AVX2) || defined(Z_USE_AVX512)
+#if defined(__SSE4_2__) || defined(__AVX2__)
     if (len >= 1024) return (HsInt)hs_count_simd(str+off, len, w);
     else {
 #endif
@@ -466,7 +466,7 @@
         for (res = 0; len-- != 0; ++str)
             res += *str == w;
         return res;
-#if defined(__SSE4_2__) || defined(Z_USE_AVX2) || defined(Z_USE_AVX512)
+#if defined(__SSE4_2__) || defined(__AVX2__)
     }
 #endif
 }
diff --git a/cbits/dtoa.c b/cbits/dtoa.c
--- a/cbits/dtoa.c
+++ b/cbits/dtoa.c
@@ -32,6 +32,7 @@
 #include <stdint.h> // uint64_t etc.
 #include <assert.h> // assert
 #include <math.h> // ceil
+#include <string.h> // memcpy
 
 #ifdef _MSC_VER
 #pragma warning(disable : 4204) // nonstandard extension used : non-constant aggregate initializer
@@ -54,8 +55,6 @@
 #define MIN_TARGET_EXP   -60
 #define MASK32           0xFFFFFFFFULL
 
-#define CAST_U64(d) (*(uint64_t*)&d)
-#define CAST_U32(d) (*(uint32_t*)&d)
 #define MIN(x,y) ((x) <= (y) ? (x) : (y))
 #define MAX(x,y) ((x) >= (y) ? (x) : (y))
 
@@ -207,7 +206,8 @@
 static diy_fp double2diy_fp(double d)
 {
     diy_fp fp;
-    uint64_t u64 = CAST_U64(d);
+    uint64_t u64;
+    memcpy(&u64, &d, sizeof(double));
     if (!(u64 & D64_EXP_MASK)) {
         fp.f = u64 & D64_FRACT_MASK;
         fp.e = 1 - D64_EXP_BIAS;
@@ -222,7 +222,8 @@
 static diy_fp float2diy_fp(float d)
 {
     diy_fp fp;
-    uint32_t u32 = CAST_U32(d);
+    uint32_t u32;
+    memcpy(&u32, &d, sizeof(float));
     if (!(u32 & D32_EXP_MASK)) {
         fp.f = (uint64_t)u32 & D32_FRACT_MASK; 
         fp.e = 1 - D32_EXP_BIAS; 
@@ -315,7 +316,8 @@
     diy_fp b_plus = normalize_diy_fp(t);
     diy_fp b_minus;
     diy_fp c_mk; // Cached power of ten: 10^-k
-    uint64_t u64 = CAST_U64(v);
+    uint64_t u64;
+    memcpy(&u64, &v, sizeof(double));
     assert(v > 0 && v <= 1.7976931348623157e308); // Grisu only handles strictly positive finite numbers.
     if (!(u64 & D64_FRACT_MASK) && (u64 & D64_EXP_MASK) != 0) { 
         b_minus.f = (dfp.f << 2) - 1; b_minus.e =  dfp.e - 2;
@@ -346,7 +348,8 @@
     diy_fp b_plus = normalize_diy_fp(t);
     diy_fp b_minus;
     diy_fp c_mk; // Cached power of ten: 10^-k
-    uint64_t u32 = CAST_U32(v);
+    uint64_t u32;
+    memcpy(&u32, &v, sizeof(float));
     assert(v > 0 && v <= 3.4028235e38); // Grisu only handles strictly positive finite numbers.
     if (!(u32 & D32_FRACT_MASK) && (u32 & D32_EXP_MASK) != 0) {
         b_minus.f = (dfp.f << 2) - 1; b_minus.e =  dfp.e - 2;
diff --git a/cbits/text.c b/cbits/text.c
--- a/cbits/text.c
+++ b/cbits/text.c
@@ -30,21 +30,21 @@
 #include <utf8rewind.h>
 #include <codepoint.h>
 
-#if defined(Z_USE_AVX512)
+#if defined(__AVX512F__) && defined(__AVX512VL__) && defined(__AVX512VBMI__)
 #include <simdasciicheck_avx512.h>
 #include <simdutf8check_avx512.h>
-#else
+#elif defined(__SSSE3__) 
 #include <simdasciicheck.h>
 #include <simdutf8check.h>
 #endif
 
 HsInt ascii_validate(const char* p, HsInt off, HsInt len){
     const char* q = p + off;
-#if defined(Z_USE_AVX512)
+#if defined(AVX512_IMPLEMENTATION)
     return (HsInt)validate_ascii_fast_avx512(q, (size_t)len);
-#elif defined(Z_USE_AVX2)
+#elif defined(__AVX2__)
     return (HsInt)validate_ascii_fast_avx(q, (size_t)len);
-#elif defined(__SSE2__)
+#elif defined(__SSSE3__) 
     return (HsInt)validate_ascii_fast(q, (size_t)len);
 #else
     return (HsInt)ascii_u64(q, (size_t)len);
@@ -53,11 +53,11 @@
 // for some reason unknown, on windows we have to supply a seperated version of ascii_validate
 // otherwise we got segfault if we import the same FFI with different type (Addr# vs ByteArray#)
 HsInt ascii_validate_addr(const char* p, HsInt len){
-#if defined(Z_USE_AVX512)
+#if defined(AVX512_IMPLEMENTATION)
     return (HsInt)validate_ascii_fast_avx512(p, (size_t)len);
-#elif defined(Z_USE_AVX2)
+#elif defined(__AVX2__)
     return (HsInt)validate_ascii_fast_avx(p, (size_t)len);
-#elif defined(__SSE2__)
+#elif defined(__SSSE3__) 
     return (HsInt)validate_ascii_fast(p, (size_t)len);
 #else
     return (HsInt)ascii_u64(p, (size_t)len);
@@ -66,11 +66,11 @@
 
 HsInt utf8_validate(const char* p, HsInt off, HsInt len){
     const char* q = p + off;
-#if defined(Z_USE_AVX512)
+#if defined(AVX512_IMPLEMENTATION)
     return (HsInt)validate_utf8_fast_avx512(q, (size_t)len);
-#elif defined(Z_USE_AVX2)
+#elif defined(__AVX2__)
     return (HsInt)validate_utf8_fast_avx(q, (size_t)len);
-#elif defined(__SSE2__)
+#elif defined(__SSSE3__) 
     return (HsInt)validate_utf8_fast(q, (size_t)len);
 #else
     return utf8_validate_slow(q, (size_t)len);
@@ -79,11 +79,11 @@
 // for some reason unknown, on windows we have to supply a seperated version of utf8_validate
 // otherwise we got segfault if we import the same FFI with different type (Addr# vs ByteArray#)
 HsInt utf8_validate_addr(const char* p, HsInt len){
-#if defined(Z_USE_AVX512)
+#if defined(AVX512_IMPLEMENTATION)
     return (HsInt)validate_utf8_fast_avx512(p, (size_t)len);
-#elif defined(Z_USE_AVX2)
+#elif defined(__AVX2__)
     return (HsInt)validate_utf8_fast_avx(p, (size_t)len);
-#elif defined(__SSE2__)
+#elif defined(__SSSE3__) 
     return (HsInt)validate_utf8_fast(p, (size_t)len);
 #else
     return utf8_validate_slow(p, (size_t)len);
diff --git a/third_party/fastvalidate-utf-8/include/simdasciicheck_avx512.h b/third_party/fastvalidate-utf-8/include/simdasciicheck_avx512.h
new file mode 100644
--- /dev/null
+++ b/third_party/fastvalidate-utf-8/include/simdasciicheck_avx512.h
@@ -0,0 +1,29 @@
+#ifndef SIMDASCIICHECK_AXV512_H
+#define SIMDASCIICHECK_AXV512_H
+
+#ifdef __AVX512F__
+#include <stdbool.h>   // c99 bool
+#include <stddef.h>    // size_t
+#include <x86intrin.h>
+// The function returns true (1) if all chars passed in src are
+// 7-bit values (0x00..0x7F). Otherwise, it returns false (0).
+static bool validate_ascii_fast_avx512(const char *src, size_t len) {
+  size_t i = 0;
+  __m512i has_error = _mm512_setzero_si512();
+  if (len >= 64) {
+    for (; i <= len - 64; i += 64) {
+      __m512i current_bytes = _mm512_loadu_si512((const __m512i *)(src + i));
+      has_error = _mm512_or_si512(has_error, current_bytes);
+    }
+  }
+  int reduced = _mm512_reduce_or_epi32(has_error);
+
+  for (; i < len; i++) {
+    reduced |= src[i];
+  }
+
+  return !(reduced & 0x80808080);
+}
+#endif
+
+#endif
diff --git a/third_party/fastvalidate-utf-8/include/simdutf8check_avx512.h b/third_party/fastvalidate-utf-8/include/simdutf8check_avx512.h
new file mode 100644
--- /dev/null
+++ b/third_party/fastvalidate-utf-8/include/simdutf8check_avx512.h
@@ -0,0 +1,297 @@
+#ifndef SIMDUTF8CHECK_AXV512_H
+#define SIMDUTF8CHECK_AXV512_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <x86intrin.h>
+/*
+ * legal utf-8 byte sequence
+ * http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94
+ *
+ *  Code Points        1st       2s       3s       4s
+ * U+0000..U+007F     00..7F
+ * U+0080..U+07FF     C2..DF   80..BF
+ * U+0800..U+0FFF     E0       A0..BF   80..BF
+ * U+1000..U+CFFF     E1..EC   80..BF   80..BF
+ * U+D000..U+D7FF     ED       80..9F   80..BF
+ * U+E000..U+FFFF     EE..EF   80..BF   80..BF
+ * U+10000..U+3FFFF   F0       90..BF   80..BF   80..BF
+ * U+40000..U+FFFFF   F1..F3   80..BF   80..BF   80..BF
+ * U+100000..U+10FFFF F4       80..8F   80..BF   80..BF
+ *
+ */
+
+#if defined(__AVX512F__) && defined(__AVX512VL__) && defined(__AVX512VBMI__)
+#define AVX512_IMPLEMENTATION
+#endif
+
+#ifdef AVX512_IMPLEMENTATION
+
+/*****************************/
+static inline __m512i avx512_push_last_byte_of_a_to_b(__m512i a, __m512i b) {
+  __m512i indexes = _mm512_set_epi64(0x3E3D3C3B3A393837, 0x363534333231302F,
+                                     0x2E2D2C2B2A292827, 0x262524232221201F,
+                                     0x1E1D1C1B1A191817, 0x161514131211100F,
+                                     0x0E0D0C0B0A090807, 0x060504030201007F);
+  return _mm512_permutex2var_epi8(b, indexes, a);
+}
+
+static inline __m512i avx512_push_last_2bytes_of_a_to_b(__m512i a, __m512i b) {
+  __m512i indexes = _mm512_set_epi64(0x3D3C3B3A39383736, 0x3534333231302F2E,
+                                     0x2D2C2B2A29282726, 0x2524232221201F1E,
+                                     0x1D1C1B1A19181716, 0x1514131211100F0E,
+                                     0x0D0C0B0A09080706, 0x0504030201007F7E);
+  return _mm512_permutex2var_epi8(b, indexes, a);
+}
+
+// all byte values must be no larger than 0xF4
+static inline void avx512_checkSmallerThan0xF4(__m512i current_bytes,
+                                               __mmask64 *has_error) {
+  *has_error =
+      _kor_mask64(*has_error, _mm512_cmpgt_epu8_mask(current_bytes,
+                                                     _mm512_set1_epi8(0xF4)));
+}
+
+static inline __m512i avx512_continuationLengths(__m512i high_nibbles) {
+  return _mm512_shuffle_epi8(
+      _mm512_setr4_epi32(0x01010101, 0x01010101, 0x00000000,
+                         0x04030202), // see avx2 version for clarity
+      high_nibbles);
+}
+
+static inline __m512i avx512_carryContinuations(__m512i initial_lengths,
+                                                __m512i previous_carries) {
+
+  __m512i right1 = _mm512_subs_epu8(
+      avx512_push_last_byte_of_a_to_b(previous_carries, initial_lengths),
+      _mm512_set1_epi8(1));
+  __m512i sum = _mm512_add_epi8(initial_lengths, right1);
+
+  __m512i right2 =
+      _mm512_subs_epu8(avx512_push_last_2bytes_of_a_to_b(previous_carries, sum),
+                       _mm512_set1_epi8(2));
+  return _mm512_add_epi8(sum, right2);
+}
+
+static inline void avx512_checkContinuations(__m512i initial_lengths,
+                                             __m512i carries,
+                                             __mmask64 *has_error) {
+  // overlap || underlap
+  // carry > length && length > 0 || !(carry > length) && !(length > 0)
+  // (carries > length) == (lengths > 0)
+  *has_error = _kor_mask64(
+      *has_error,
+      _kxnor_mask64(
+          _mm512_cmpgt_epi8_mask(carries, initial_lengths),
+          _mm512_cmpgt_epi8_mask(initial_lengths, _mm512_setzero_si512())));
+}
+
+// when 0xED is found, next byte must be no larger than 0x9F
+// when 0xF4 is found, next byte must be no larger than 0x8F
+// next byte must be continuation, ie sign bit is set, so signed < is ok
+static inline void avx512_checkFirstContinuationMax(__m512i current_bytes,
+                                                    __m512i off1_current_bytes,
+                                                    __mmask64 *has_error) {
+  __mmask64 maskED =
+      _mm512_cmpeq_epi8_mask(off1_current_bytes, _mm512_set1_epi8(0xED));
+  __mmask64 maskF4 =
+      _mm512_cmpeq_epi8_mask(off1_current_bytes, _mm512_set1_epi8(0xF4));
+  __mmask64 badfollowED = _kand_mask64(
+      _mm512_cmpgt_epi8_mask(current_bytes, _mm512_set1_epi8(0x9F)), maskED);
+  __mmask64 badfollowF4 = _kand_mask64(
+      _mm512_cmpgt_epi8_mask(current_bytes, _mm512_set1_epi8(0x8F)), maskF4);
+
+  *has_error = _kor_mask64(*has_error, _kor_mask64(badfollowED, badfollowF4));
+}
+
+// map off1_hibits => error condition
+// hibits     off1    cur
+// C       => < C2 && true
+// E       => < E1 && < A0
+// F       => < F1 && < 90
+// else      false && false
+static inline void avx512_checkOverlong(__m512i current_bytes,
+                                        __m512i off1_current_bytes,
+                                        __m512i hibits, __m512i previous_hibits,
+                                        __mmask64 *has_error) {
+  __m512i off1_hibits =
+      avx512_push_last_byte_of_a_to_b(previous_hibits, hibits);
+  __m512i initial_mins = _mm512_shuffle_epi8(
+      _mm512_setr4_epi32(0x80808080, 0x80808080, 0x80808080,
+                         0xF1E180C2), // see avx2 version for clarity
+      off1_hibits);
+
+  __mmask64 initial_under =
+      _mm512_cmpgt_epi8_mask(initial_mins, off1_current_bytes);
+
+  __m512i second_mins = _mm512_shuffle_epi8(
+      _mm512_setr4_epi32(0x80808080, 0x80808080, 0x80808080,
+                         0x90A07F7F), // see avx2 version for clarity
+      off1_hibits);
+  __mmask64 second_under = _mm512_cmpgt_epi8_mask(second_mins, current_bytes);
+  *has_error =
+      _kor_mask64(*has_error, _kand_mask64(initial_under, second_under));
+}
+
+struct avx512_processed_utf_bytes {
+  __m512i rawbytes;
+  __m512i high_nibbles;
+  __m512i carried_continuations;
+};
+
+static inline void
+avx512_count_nibbles(__m512i bytes, struct avx512_processed_utf_bytes *answer) {
+  answer->rawbytes = bytes;
+  answer->high_nibbles =
+      _mm512_and_si512(_mm512_srli_epi16(bytes, 4), _mm512_set1_epi8(0x0F));
+}
+
+// check whether the current bytes are valid UTF-8
+// at the end of the function, previous gets updated
+static struct avx512_processed_utf_bytes
+avx512_checkUTF8Bytes(__m512i current_bytes,
+                      struct avx512_processed_utf_bytes *previous,
+                      __mmask64 *has_error) {
+  struct avx512_processed_utf_bytes pb;
+  avx512_count_nibbles(current_bytes, &pb);
+
+  avx512_checkSmallerThan0xF4(current_bytes, has_error);
+
+  __m512i initial_lengths = avx512_continuationLengths(pb.high_nibbles);
+
+  pb.carried_continuations = avx512_carryContinuations(
+      initial_lengths, previous->carried_continuations);
+
+  avx512_checkContinuations(initial_lengths, pb.carried_continuations,
+                            has_error);
+
+  __m512i off1_current_bytes =
+      avx512_push_last_byte_of_a_to_b(previous->rawbytes, pb.rawbytes);
+  avx512_checkFirstContinuationMax(current_bytes, off1_current_bytes,
+                                   has_error);
+
+  avx512_checkOverlong(current_bytes, off1_current_bytes, pb.high_nibbles,
+                       previous->high_nibbles, has_error);
+  return pb;
+}
+
+// check whether the current bytes are valid UTF-8
+// at the end of the function, previous gets updated
+static struct avx512_processed_utf_bytes
+avx512_checkUTF8Bytes_asciipath(__m512i current_bytes,
+                                struct avx512_processed_utf_bytes *previous,
+                                __mmask64 *has_error) {
+  if (!_mm512_cmpge_epu8_mask(current_bytes,
+                              _mm512_set1_epi8(0x80))) { // fast ascii path
+    *has_error = _kor_mask64(
+        *has_error,
+        _mm512_cmpgt_epi8_mask(
+            previous->carried_continuations,
+            _mm512_setr_epi32(0x09090909, 0x09090909, 0x09090909, 0x09090909,
+                              0x09090909, 0x09090909, 0x09090909, 0x09090909,
+                              0x09090909, 0x09090909, 0x09090909, 0x09090909,
+                              0x09090909, 0x09090909, 0x09090909, 0x01090909)));
+    return *previous;
+  }
+
+  struct avx512_processed_utf_bytes pb;
+  avx512_count_nibbles(current_bytes, &pb);
+
+  avx512_checkSmallerThan0xF4(current_bytes, has_error);
+
+  __m512i initial_lengths = avx512_continuationLengths(pb.high_nibbles);
+
+  pb.carried_continuations = avx512_carryContinuations(
+      initial_lengths, previous->carried_continuations);
+
+  avx512_checkContinuations(initial_lengths, pb.carried_continuations,
+                            has_error);
+
+  __m512i off1_current_bytes =
+      avx512_push_last_byte_of_a_to_b(previous->rawbytes, pb.rawbytes);
+  avx512_checkFirstContinuationMax(current_bytes, off1_current_bytes,
+                                   has_error);
+
+  avx512_checkOverlong(current_bytes, off1_current_bytes, pb.high_nibbles,
+                       previous->high_nibbles, has_error);
+  return pb;
+}
+
+static bool validate_utf8_fast_avx512_asciipath(const char *src, size_t len) {
+  size_t i = 0;
+  __mmask64 has_error = 0;
+  struct avx512_processed_utf_bytes previous = {
+    .rawbytes = _mm512_setzero_si512(),
+    .high_nibbles = _mm512_setzero_si512(),
+    .carried_continuations = _mm512_setzero_si512()
+  };
+  if (len >= 64) {
+    for (; i <= len - 64; i += 64) {
+      __m512i current_bytes = _mm512_loadu_si512((const __m512i *)(src + i));
+      previous =
+          avx512_checkUTF8Bytes_asciipath(current_bytes, &previous, &has_error);
+    }
+  }
+
+  // last part
+  if (i < len) {
+    char buffer[64];
+    memset(buffer, 0, 64);
+    memcpy(buffer, src + i, len - i);
+    __m512i current_bytes = _mm512_loadu_si512((const __m512i *)(buffer));
+    previous = avx512_checkUTF8Bytes(current_bytes, &previous, &has_error);
+  } else {
+    has_error = _kor_mask64(
+        has_error,
+        _mm512_cmpgt_epi8_mask(
+            previous.carried_continuations,
+            _mm512_setr_epi32(0x09090909, 0x09090909, 0x09090909, 0x09090909,
+                              0x09090909, 0x09090909, 0x09090909, 0x09090909,
+                              0x09090909, 0x09090909, 0x09090909, 0x09090909,
+                              0x09090909, 0x09090909, 0x09090909, 0x01090909)));
+  }
+
+  return !has_error;
+}
+
+static bool validate_utf8_fast_avx512(const char *src, size_t len) {
+  size_t i = 0;
+  __mmask64 has_error = 0;
+  struct avx512_processed_utf_bytes previous = {
+    .rawbytes = _mm512_setzero_si512(),
+    .high_nibbles = _mm512_setzero_si512(),
+    .carried_continuations = _mm512_setzero_si512()
+  };
+  if (len >= 64) {
+    for (; i <= len - 64; i += 64) {
+      __m512i current_bytes = _mm512_loadu_si512((const __m512i *)(src + i));
+      previous = avx512_checkUTF8Bytes(current_bytes, &previous, &has_error);
+    }
+  }
+
+  // last part
+  if (i < len) {
+    char buffer[64];
+    memset(buffer, 0, 64);
+    memcpy(buffer, src + i, len - i);
+    __m512i current_bytes = _mm512_loadu_si512((const __m512i *)(buffer));
+    previous = avx512_checkUTF8Bytes(current_bytes, &previous, &has_error);
+  } else {
+    has_error = _kor_mask64(
+        has_error,
+        _mm512_cmpgt_epi8_mask(
+            previous.carried_continuations,
+            _mm512_setr_epi32(0x09090909, 0x09090909, 0x09090909, 0x09090909,
+                              0x09090909, 0x09090909, 0x09090909, 0x09090909,
+                              0x09090909, 0x09090909, 0x09090909, 0x09090909,
+                              0x09090909, 0x09090909, 0x09090909, 0x01090909)));
+  }
+
+  return !has_error;
+}
+
+#endif // AVX512_IMPLEMENTATION
+
+#endif // SIMDUTF8CHECK_AXV512_H
diff --git a/third_party/re2/re2/compile.cc b/third_party/re2/re2/compile.cc
--- a/third_party/re2/re2/compile.cc
+++ b/third_party/re2/re2/compile.cc
@@ -79,9 +79,11 @@
 struct Frag {
   uint32_t begin;
   PatchList end;
+  bool nullable;
 
-  Frag() : begin(0) { end.head = 0; }  // needed so Frag can go in vector
-  Frag(uint32_t begin, PatchList end) : begin(begin), end(end) {}
+  Frag() : begin(0), end(kNullPatchList), nullable(false) {}
+  Frag(uint32_t begin, PatchList end, bool nullable)
+      : begin(begin), end(end), nullable(nullable) {}
 };
 
 // Input encodings.
@@ -264,7 +266,7 @@
 
 // Returns an unmatchable fragment.
 Frag Compiler::NoMatch() {
-  return Frag(0, kNullPatchList);
+  return Frag();
 }
 
 // Is a an unmatchable fragment?
@@ -290,11 +292,11 @@
   // To run backward over string, reverse all concatenations.
   if (reversed_) {
     PatchList::Patch(inst_.data(), b.end, a.begin);
-    return Frag(b.begin, a.end);
+    return Frag(b.begin, a.end, b.nullable && a.nullable);
   }
 
   PatchList::Patch(inst_.data(), a.end, b.begin);
-  return Frag(a.begin, b.end);
+  return Frag(a.begin, b.end, a.nullable && b.nullable);
 }
 
 // Given fragments for a and b, returns fragment for a|b.
@@ -310,7 +312,8 @@
     return NoMatch();
 
   inst_[id].InitAlt(a.begin, b.begin);
-  return Frag(id, PatchList::Append(inst_.data(), a.end, b.end));
+  return Frag(id, PatchList::Append(inst_.data(), a.end, b.end),
+              a.nullable || b.nullable);
 }
 
 // When capturing submatches in like-Perl mode, a kOpAlt Inst
@@ -320,27 +323,44 @@
 // then the operator is greedy.  If out1_ is the repetition
 // (and out_ moves forward), then the operator is non-greedy.
 
-// Given a fragment a, returns a fragment for a* or a*? (if nongreedy)
-Frag Compiler::Star(Frag a, bool nongreedy) {
+// Given a fragment for a, returns a fragment for a+ or a+? (if nongreedy)
+Frag Compiler::Plus(Frag a, bool nongreedy) {
   int id = AllocInst(1);
   if (id < 0)
     return NoMatch();
-  inst_[id].InitAlt(0, 0);
-  PatchList::Patch(inst_.data(), a.end, id);
+  PatchList pl;
   if (nongreedy) {
-    inst_[id].out1_ = a.begin;
-    return Frag(id, PatchList::Mk(id << 1));
+    inst_[id].InitAlt(0, a.begin);
+    pl = PatchList::Mk(id << 1);
   } else {
-    inst_[id].set_out(a.begin);
-    return Frag(id, PatchList::Mk((id << 1) | 1));
+    inst_[id].InitAlt(a.begin, 0);
+    pl = PatchList::Mk((id << 1) | 1);
   }
+  PatchList::Patch(inst_.data(), a.end, id);
+  return Frag(a.begin, pl, a.nullable);
 }
 
-// Given a fragment for a, returns a fragment for a+ or a+? (if nongreedy)
-Frag Compiler::Plus(Frag a, bool nongreedy) {
-  // a+ is just a* with a different entry point.
-  Frag f = Star(a, nongreedy);
-  return Frag(a.begin, f.end);
+// Given a fragment for a, returns a fragment for a* or a*? (if nongreedy)
+Frag Compiler::Star(Frag a, bool nongreedy) {
+  // When the subexpression is nullable, one Alt isn't enough to guarantee
+  // correct priority ordering within the transitive closure. The simplest
+  // solution is to handle it as (a+)? instead, which adds the second Alt.
+  if (a.nullable)
+    return Quest(Plus(a, nongreedy), nongreedy);
+
+  int id = AllocInst(1);
+  if (id < 0)
+    return NoMatch();
+  PatchList pl;
+  if (nongreedy) {
+    inst_[id].InitAlt(0, a.begin);
+    pl = PatchList::Mk(id << 1);
+  } else {
+    inst_[id].InitAlt(a.begin, 0);
+    pl = PatchList::Mk((id << 1) | 1);
+  }
+  PatchList::Patch(inst_.data(), a.end, id);
+  return Frag(id, pl, true);
 }
 
 // Given a fragment for a, returns a fragment for a? or a?? (if nongreedy)
@@ -358,7 +378,7 @@
     inst_[id].InitAlt(a.begin, 0);
     pl = PatchList::Mk((id << 1) | 1);
   }
-  return Frag(id, PatchList::Append(inst_.data(), pl, a.end));
+  return Frag(id, PatchList::Append(inst_.data(), pl, a.end), true);
 }
 
 // Returns a fragment for the byte range lo-hi.
@@ -367,7 +387,7 @@
   if (id < 0)
     return NoMatch();
   inst_[id].InitByteRange(lo, hi, foldcase, 0);
-  return Frag(id, PatchList::Mk(id << 1));
+  return Frag(id, PatchList::Mk(id << 1), false);
 }
 
 // Returns a no-op fragment.  Sometimes unavoidable.
@@ -376,7 +396,7 @@
   if (id < 0)
     return NoMatch();
   inst_[id].InitNop(0);
-  return Frag(id, PatchList::Mk(id << 1));
+  return Frag(id, PatchList::Mk(id << 1), true);
 }
 
 // Returns a fragment that signals a match.
@@ -385,7 +405,7 @@
   if (id < 0)
     return NoMatch();
   inst_[id].InitMatch(match_id);
-  return Frag(id, kNullPatchList);
+  return Frag(id, kNullPatchList, false);
 }
 
 // Returns a fragment matching a particular empty-width op (like ^ or $)
@@ -394,7 +414,7 @@
   if (id < 0)
     return NoMatch();
   inst_[id].InitEmptyWidth(empty, 0);
-  return Frag(id, PatchList::Mk(id << 1));
+  return Frag(id, PatchList::Mk(id << 1), true);
 }
 
 // Given a fragment a, returns a fragment with capturing parens around a.
@@ -408,7 +428,7 @@
   inst_[id+1].InitCapture(2*n+1, 0);
   PatchList::Patch(inst_.data(), a.end, id+1);
 
-  return Frag(id, PatchList::Mk((id+1) << 1));
+  return Frag(id, PatchList::Mk((id+1) << 1), a.nullable);
 }
 
 // A Rune is a name for a Unicode code point.
@@ -567,7 +587,7 @@
 Frag Compiler::FindByteRange(int root, int id) {
   if (inst_[root].opcode() == kInstByteRange) {
     if (ByteRangeEqual(root, id))
-      return Frag(root, kNullPatchList);
+      return Frag(root, kNullPatchList, false);
     else
       return NoMatch();
   }
@@ -575,7 +595,7 @@
   while (inst_[root].opcode() == kInstAlt) {
     int out1 = inst_[root].out1();
     if (ByteRangeEqual(out1, id))
-      return Frag(root, PatchList::Mk((root << 1) | 1));
+      return Frag(root, PatchList::Mk((root << 1) | 1), false);
 
     // CharClass is a sorted list of ranges, so if out1 of the root Alt wasn't
     // what we're looking for, then we can stop immediately. Unfortunately, we
@@ -587,7 +607,7 @@
     if (inst_[out].opcode() == kInstAlt)
       root = out;
     else if (ByteRangeEqual(out, id))
-      return Frag(root, PatchList::Mk(root << 1));
+      return Frag(root, PatchList::Mk(root << 1), false);
     else
       return NoMatch();
   }
diff --git a/third_party/re2/re2/dfa.cc b/third_party/re2/re2/dfa.cc
--- a/third_party/re2/re2/dfa.cc
+++ b/third_party/re2/re2/dfa.cc
@@ -167,6 +167,9 @@
   typedef std::unordered_set<State*, StateHash, StateEqual> StateSet;
 
  private:
+  // Make it easier to swap in a scalable reader-writer mutex.
+  using CacheMutex = Mutex;
+
   enum {
     // Indices into start_ for unanchored searches.
     // Add kStartAnchored for anchored searches.
@@ -331,7 +334,7 @@
   // while holding cache_mutex_ for writing, to avoid interrupting other
   // readers.  Any State* pointers are only valid while cache_mutex_
   // is held.
-  Mutex cache_mutex_;
+  CacheMutex cache_mutex_;
   int64_t mem_budget_;     // Total memory budget for all States.
   int64_t state_budget_;   // Amount of memory remaining for new States.
   StateSet state_cache_;   // All States computed so far.
@@ -1106,7 +1109,7 @@
 
 class DFA::RWLocker {
  public:
-  explicit RWLocker(Mutex* mu);
+  explicit RWLocker(CacheMutex* mu);
   ~RWLocker();
 
   // If the lock is only held for reading right now,
@@ -1116,14 +1119,14 @@
   void LockForWriting();
 
  private:
-  Mutex* mu_;
+  CacheMutex* mu_;
   bool writing_;
 
   RWLocker(const RWLocker&) = delete;
   RWLocker& operator=(const RWLocker&) = delete;
 };
 
-DFA::RWLocker::RWLocker(Mutex* mu) : mu_(mu), writing_(false) {
+DFA::RWLocker::RWLocker(CacheMutex* mu) : mu_(mu), writing_(false) {
   mu_->ReaderLock();
 }
 
diff --git a/third_party/re2/re2/nfa.cc b/third_party/re2/re2/nfa.cc
--- a/third_party/re2/re2/nfa.cc
+++ b/third_party/re2/re2/nfa.cc
@@ -600,7 +600,7 @@
     // by simply not continuing the loop.
     // This complements the special case in NFA::Step().
     if (p == NULL) {
-      (void)Step(runq, nextq, p < etext_ ? p[0] & 0xFF : -1, context, p);
+      (void) Step(runq, nextq, -1, context, p);
       DCHECK_EQ(runq->size(), 0);
       using std::swap;
       swap(nextq, runq);
diff --git a/third_party/re2/re2/perl_groups.cc b/third_party/re2/re2/perl_groups.cc
--- a/third_party/re2/re2/perl_groups.cc
+++ b/third_party/re2/re2/perl_groups.cc
@@ -20,12 +20,12 @@
 	{ 0x61, 0x7a },
 };
 const UGroup perl_groups[] = {
-	{ "\\d", +1, code1, 1 },
-	{ "\\D", -1, code1, 1 },
-	{ "\\s", +1, code2, 3 },
-	{ "\\S", -1, code2, 3 },
-	{ "\\w", +1, code3, 4 },
-	{ "\\W", -1, code3, 4 },
+	{ "\\d", +1, code1, 1, 0, 0 },
+	{ "\\D", -1, code1, 1, 0, 0 },
+	{ "\\s", +1, code2, 3, 0, 0 },
+	{ "\\S", -1, code2, 3, 0, 0 },
+	{ "\\w", +1, code3, 4, 0, 0 },
+	{ "\\W", -1, code3, 4, 0, 0 },
 };
 const int num_perl_groups = 6;
 static const URange16 code4[] = {  /* [:alnum:] */
@@ -85,34 +85,34 @@
 	{ 0x61, 0x66 },
 };
 const UGroup posix_groups[] = {
-	{ "[:alnum:]", +1, code4, 3 },
-	{ "[:^alnum:]", -1, code4, 3 },
-	{ "[:alpha:]", +1, code5, 2 },
-	{ "[:^alpha:]", -1, code5, 2 },
-	{ "[:ascii:]", +1, code6, 1 },
-	{ "[:^ascii:]", -1, code6, 1 },
-	{ "[:blank:]", +1, code7, 2 },
-	{ "[:^blank:]", -1, code7, 2 },
-	{ "[:cntrl:]", +1, code8, 2 },
-	{ "[:^cntrl:]", -1, code8, 2 },
-	{ "[:digit:]", +1, code9, 1 },
-	{ "[:^digit:]", -1, code9, 1 },
-	{ "[:graph:]", +1, code10, 1 },
-	{ "[:^graph:]", -1, code10, 1 },
-	{ "[:lower:]", +1, code11, 1 },
-	{ "[:^lower:]", -1, code11, 1 },
-	{ "[:print:]", +1, code12, 1 },
-	{ "[:^print:]", -1, code12, 1 },
-	{ "[:punct:]", +1, code13, 4 },
-	{ "[:^punct:]", -1, code13, 4 },
-	{ "[:space:]", +1, code14, 2 },
-	{ "[:^space:]", -1, code14, 2 },
-	{ "[:upper:]", +1, code15, 1 },
-	{ "[:^upper:]", -1, code15, 1 },
-	{ "[:word:]", +1, code16, 4 },
-	{ "[:^word:]", -1, code16, 4 },
-	{ "[:xdigit:]", +1, code17, 3 },
-	{ "[:^xdigit:]", -1, code17, 3 },
+	{ "[:alnum:]", +1, code4, 3, 0, 0 },
+	{ "[:^alnum:]", -1, code4, 3, 0, 0 },
+	{ "[:alpha:]", +1, code5, 2, 0, 0 },
+	{ "[:^alpha:]", -1, code5, 2, 0, 0 },
+	{ "[:ascii:]", +1, code6, 1, 0, 0 },
+	{ "[:^ascii:]", -1, code6, 1, 0, 0 },
+	{ "[:blank:]", +1, code7, 2, 0, 0 },
+	{ "[:^blank:]", -1, code7, 2, 0, 0 },
+	{ "[:cntrl:]", +1, code8, 2, 0, 0 },
+	{ "[:^cntrl:]", -1, code8, 2, 0, 0 },
+	{ "[:digit:]", +1, code9, 1, 0, 0 },
+	{ "[:^digit:]", -1, code9, 1, 0, 0 },
+	{ "[:graph:]", +1, code10, 1, 0, 0 },
+	{ "[:^graph:]", -1, code10, 1, 0, 0 },
+	{ "[:lower:]", +1, code11, 1, 0, 0 },
+	{ "[:^lower:]", -1, code11, 1, 0, 0 },
+	{ "[:print:]", +1, code12, 1, 0, 0 },
+	{ "[:^print:]", -1, code12, 1, 0, 0 },
+	{ "[:punct:]", +1, code13, 4, 0, 0 },
+	{ "[:^punct:]", -1, code13, 4, 0, 0 },
+	{ "[:space:]", +1, code14, 2, 0, 0 },
+	{ "[:^space:]", -1, code14, 2, 0, 0 },
+	{ "[:upper:]", +1, code15, 1, 0, 0 },
+	{ "[:^upper:]", -1, code15, 1, 0, 0 },
+	{ "[:word:]", +1, code16, 4, 0, 0 },
+	{ "[:^word:]", -1, code16, 4, 0, 0 },
+	{ "[:xdigit:]", +1, code17, 3, 0, 0 },
+	{ "[:^xdigit:]", -1, code17, 3, 0, 0 },
 };
 const int num_posix_groups = 28;
 
diff --git a/third_party/re2/re2/re2.h b/third_party/re2/re2/re2.h
--- a/third_party/re2/re2/re2.h
+++ b/third_party/re2/re2/re2.h
@@ -830,8 +830,9 @@
 #if !defined(_MSC_VER)
   template <typename T>
   using CanParseFrom = typename std::enable_if<
-      std::is_member_function_pointer<decltype(
-          (bool (T::*)(const char*, size_t))&T::ParseFrom)>::value,
+      std::is_member_function_pointer<
+          decltype(static_cast<bool (T::*)(const char*, size_t)>(
+              &T::ParseFrom))>::value,
       int>::type;
 #endif
 
diff --git a/third_party/re2/re2/regexp.cc b/third_party/re2/re2/regexp.cc
--- a/third_party/re2/re2/regexp.cc
+++ b/third_party/re2/re2/regexp.cc
@@ -722,8 +722,14 @@
   *foldcase = false;
 
   // No need for a walker: the regexp must either begin with or be
-  // a literal char or string.
+  // a literal char or string. We "see through" capturing groups,
+  // but make no effort to glue multiple prefix fragments together.
   Regexp* re = op_ == kRegexpConcat && nsub_ > 0 ? sub()[0] : this;
+  while (re->op_ == kRegexpCapture) {
+    re = re->sub()[0];
+    if (re->op_ == kRegexpConcat && re->nsub_ > 0)
+      re = re->sub()[0];
+  }
   if (re->op_ != kRegexpLiteral &&
       re->op_ != kRegexpLiteralString)
     return false;
@@ -913,7 +919,7 @@
 // The ranges are allocated in the same block as the header,
 // necessitating a special allocator and Delete method.
 
-CharClass* CharClass::New(int maxranges) {
+CharClass* CharClass::New(size_t maxranges) {
   CharClass* cc;
   uint8_t* data = new uint8_t[sizeof *cc + maxranges*sizeof cc->ranges_[0]];
   cc = reinterpret_cast<CharClass*>(data);
@@ -930,7 +936,7 @@
 }
 
 CharClass* CharClass::Negate() {
-  CharClass* cc = CharClass::New(nranges_+1);
+  CharClass* cc = CharClass::New(static_cast<size_t>(nranges_+1));
   cc->folds_ascii_ = folds_ascii_;
   cc->nrunes_ = Runemax + 1 - nrunes_;
   int n = 0;
@@ -967,7 +973,7 @@
 }
 
 CharClass* CharClassBuilder::GetCharClass() {
-  CharClass* cc = CharClass::New(static_cast<int>(ranges_.size()));
+  CharClass* cc = CharClass::New(ranges_.size());
   int n = 0;
   for (iterator it = begin(); it != end(); ++it)
     cc->ranges_[n++] = *it;
diff --git a/third_party/re2/re2/regexp.h b/third_party/re2/re2/regexp.h
--- a/third_party/re2/re2/regexp.h
+++ b/third_party/re2/re2/regexp.h
@@ -86,6 +86,7 @@
 // form accessible to clients, so that client code can analyze the
 // parsed regular expressions.
 
+#include <stddef.h>
 #include <stdint.h>
 #include <map>
 #include <set>
@@ -259,7 +260,7 @@
  private:
   CharClass();  // not implemented
   ~CharClass();  // not implemented
-  static CharClass* New(int maxranges);
+  static CharClass* New(size_t maxranges);
 
   friend class CharClassBuilder;
 
diff --git a/third_party/re2/re2/set.cc b/third_party/re2/re2/set.cc
--- a/third_party/re2/re2/set.cc
+++ b/third_party/re2/re2/set.cc
@@ -133,6 +133,9 @@
       error_info->kind = kNotCompiled;
     return false;
   }
+#ifdef RE2_HAVE_THREAD_LOCAL
+  hooks::context = NULL;
+#endif
   bool dfa_failed = false;
   std::unique_ptr<SparseSet> matches;
   if (v != NULL) {
diff --git a/third_party/re2/re2/tostring.cc b/third_party/re2/re2/tostring.cc
--- a/third_party/re2/re2/tostring.cc
+++ b/third_party/re2/re2/tostring.cc
@@ -291,7 +291,7 @@
       // There's no syntax accepted by the parser to generate
       // this node (it is generated by RE2::Set) so make something
       // up that is readable but won't compile.
-      t_->append("(?HaveMatch:%d)", re->match_id());
+      t_->append(StringPrintf("(?HaveMatch:%d)", re->match_id()));
       break;
   }
 
diff --git a/third_party/re2/util/mutex.h b/third_party/re2/util/mutex.h
--- a/third_party/re2/util/mutex.h
+++ b/third_party/re2/util/mutex.h
@@ -12,6 +12,7 @@
 
 #ifdef _WIN32
 // Requires Windows Vista or Windows Server 2008 at minimum.
+#include <windows.h>
 #if defined(WINVER) && WINVER >= 0x0600
 #define MUTEX_IS_WIN32_SRWLOCK
 #endif
@@ -26,7 +27,6 @@
 #endif
 
 #if defined(MUTEX_IS_WIN32_SRWLOCK)
-#include <windows.h>
 typedef SRWLOCK MutexType;
 #elif defined(MUTEX_IS_PTHREAD_RWLOCK)
 #include <pthread.h>
diff --git a/third_party/utf8rewind/include/utf8rewind/utf8rewind.h b/third_party/utf8rewind/include/utf8rewind/utf8rewind.h
--- a/third_party/utf8rewind/include/utf8rewind/utf8rewind.h
+++ b/third_party/utf8rewind/include/utf8rewind/utf8rewind.h
@@ -62,6 +62,7 @@
 	Custom type definitions used throughout the library.
 */
 
+#include <ctype.h>
 #include <locale.h>
 #include <stddef.h>
 #include <stdio.h>
diff --git a/third_party/utf8rewind/source/utf8rewind.c b/third_party/utf8rewind/source/utf8rewind.c
--- a/third_party/utf8rewind/source/utf8rewind.c
+++ b/third_party/utf8rewind/source/utf8rewind.c
@@ -549,6 +549,22 @@
 	}
 }
 
+// internal helper to avoid the use of non-standard strncasecmp
+int __strncasecmp(const char* s1, const char* s2, size_t n) {
+  if (n == 0)
+    return 0;
+
+  do {
+    if (tolower((unsigned char) *s1) != tolower((unsigned char) *s2++))
+      return (int)tolower((unsigned char)*s1) -
+	(int) tolower((unsigned char) *--s2);
+    if (*s1++ == 0)
+      break;
+  } while (--n != 0);
+
+  return 0;
+}
+
 UTF8_API size_t utf8envlocale()
 {
 	/*
@@ -562,7 +578,7 @@
 	*/
 
 #define UTF8_LOCALE_CHECK(_name) \
-	!strncasecmp(locale, _name, 5)
+	!__strncasecmp(locale, _name, 5)
 
 #if WIN32 || _WINDOWS
     WCHAR localeW[LOCALE_NAME_MAX_LENGTH];
@@ -1376,4 +1392,4 @@
 	}
 
 	return src - input;
-}
+}
