diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,10 @@
 # Changelog for `ghc-bignum` package
 
+## 1.3
+
+- Expose backendName
+- Add `naturalSetBit[#]` (#21173), `naturalClearBit[#]` (#21175), `naturalComplementBit[#]` (#21181)
+
 ## 1.2
 
 - Moved naturalToDouble# and naturalToFloat# to `base` package
diff --git a/configure b/configure
--- a/configure
+++ b/configure
@@ -664,6 +664,7 @@
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -739,6 +740,7 @@
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -991,6 +993,15 @@
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1128,7 +1139,7 @@
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1281,6 +1292,7 @@
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
diff --git a/ghc-bignum.cabal b/ghc-bignum.cabal
--- a/ghc-bignum.cabal
+++ b/ghc-bignum.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.0
 name:                ghc-bignum
-version:             1.2
+version:             1.3
 synopsis:            GHC BigNum library
 license:             BSD3
 license-file:        LICENSE
@@ -77,7 +77,7 @@
     ForeignFunctionInterface
 
   build-depends:
-    ghc-prim >= 0.5.1.0 && < 0.9
+    ghc-prim >= 0.5.1.0 && < 0.10
 
   hs-source-dirs: src/
   include-dirs: include/
diff --git a/src/GHC/Num/Backend/Check.hs b/src/GHC/Num/Backend/Check.hs
--- a/src/GHC/Num/Backend/Check.hs
+++ b/src/GHC/Num/Backend/Check.hs
@@ -12,6 +12,7 @@
 -- | Check Native implementation against another backend
 module GHC.Num.Backend.Check where
 
+import GHC.CString
 import GHC.Prim
 import GHC.Types
 import GHC.Num.WordArray
@@ -26,6 +27,12 @@
 #endif
 
 default ()
+
+-- | ghc-bignum backend name
+backendName :: [Char]
+backendName = unpackAppendCString# "check-"# Other.backendName
+  -- we don't have (++) at our disposal, so we directly use
+  -- `unpackAppendCString#`
 
 bignat_compare
    :: WordArray#
diff --git a/src/GHC/Num/Backend/FFI.hs b/src/GHC/Num/Backend/FFI.hs
--- a/src/GHC/Num/Backend/FFI.hs
+++ b/src/GHC/Num/Backend/FFI.hs
@@ -25,6 +25,10 @@
 
 default ()
 
+-- | ghc-bignum backend name
+backendName :: [Char]
+backendName = "ffi"
+
 -- | Compare two non-zero BigNat of the same length
 --
 -- Return:
diff --git a/src/GHC/Num/Backend/GMP.hs b/src/GHC/Num/Backend/GMP.hs
--- a/src/GHC/Num/Backend/GMP.hs
+++ b/src/GHC/Num/Backend/GMP.hs
@@ -32,6 +32,10 @@
 
 default ()
 
+-- | ghc-bignum backend name
+backendName :: [Char]
+backendName = "gmp"
+
 ----------------------------------------------------------------------------
 -- type definitions
 
diff --git a/src/GHC/Num/Backend/Native.hs b/src/GHC/Num/Backend/Native.hs
--- a/src/GHC/Num/Backend/Native.hs
+++ b/src/GHC/Num/Backend/Native.hs
@@ -30,6 +30,11 @@
 
 default ()
 
+-- | ghc-bignum backend name
+backendName :: [Char]
+backendName = "native"
+
+
 count_words_bits :: Word# -> (# Word#, Word# #)
 count_words_bits n = (# nw, nb #)
    where
@@ -182,8 +187,8 @@
       !ctzB = word2Int# (bigNatCtzWord# wb)
 
       -- multiply a single bj Word# to the whole wa WordArray
-      mul bj j i carry s
-         | isTrue# (i ==# szA)
+      mul mwa wa bj j i carry s
+         | isTrue# (i ==# wordArraySize# wa)
          -- write the carry
          = mwaAddInplaceWord# mwa (i +# j) carry s
 
@@ -193,7 +198,7 @@
                      !(# c'',r #) = plusWord2# r' carry
                      carry'       = plusWord# c' c''
                   in case mwaAddInplaceWord# mwa (i +# j) r s of
-                        s' -> mul bj j (i +# 1#) carry' s'
+                        s' -> mul mwa wa bj j (i +# 1#) carry' s'
 
       -- for each bj in wb, call `mul bj wa`
       mulEachB i s
@@ -201,7 +206,7 @@
          | True = case indexWordArray# wb i of
             -- detect bj == 0## and skip the loop
             0## -> mulEachB (i +# 1#) s
-            bi  -> case mul bi i ctzA 0## s of
+            bi  -> case mul mwa wa bi i ctzA 0## s of
                      s' -> mulEachB (i +# 1#) s'
 
 bignat_sub
diff --git a/src/GHC/Num/BigNat.hs b/src/GHC/Num/BigNat.hs
--- a/src/GHC/Num/BigNat.hs
+++ b/src/GHC/Num/BigNat.hs
@@ -263,6 +263,16 @@
          in uncheckedShiftL64# wh 32# `or64#` wl
       else wl
 
+#else
+
+-- | Convert a Word64# into a BigNat on 64-bit architectures
+bigNatFromWord64# :: Word64# -> BigNat#
+bigNatFromWord64# w64 = bigNatFromWord# (word64ToWord# w64)
+
+-- | Convert a BigNat into a Word64# on 64-bit architectures
+bigNatToWord64# :: BigNat# -> Word64#
+bigNatToWord64# b = wordToWord64# (bigNatToWord# b)
+
 #endif
 
 -- | Encode (# BigNat mantissa, Int# exponent #) into a Double#
@@ -312,6 +322,7 @@
 
 -- | Equality test for BigNat
 bigNatEq# :: BigNat# -> BigNat# -> Bool#
+{-# NOINLINE bigNatEq# #-}
 bigNatEq# wa wb
    | isTrue# (wordArraySize# wa /=# wordArraySize# wb) = 0#
    | isTrue# (wordArraySize# wa ==# 0#)                = 1#
@@ -331,6 +342,7 @@
 
 -- | Compare a BigNat and a Word#
 bigNatCompareWord# :: BigNat# -> Word# -> Ordering
+{-# NOINLINE bigNatCompareWord# #-}
 bigNatCompareWord# a b
    | bigNatIsZero a                   = cmpW# 0## b
    | isTrue# (wordArraySize# a ># 1#) = GT
@@ -343,6 +355,7 @@
 
 -- | Compare two BigNat
 bigNatCompare :: BigNat# -> BigNat# -> Ordering
+{-# NOINLINE bigNatCompare #-}
 bigNatCompare a b =
    let
       szA = wordArraySize# a
@@ -408,7 +421,7 @@
    = bigNatFromWord# b
 
    | True
-   = withNewWordArrayTrimed# (wordArraySize# a +# 1#) \mwa s ->
+   = withNewWordArrayTrimmed# (wordArraySize# a +# 1#) \mwa s ->
          inline bignat_add_word mwa a b s
 
 -- | Add a bigNat and a Word
@@ -426,7 +439,7 @@
       !szB     = wordArraySize# b
       !szMax   = maxI# szA szB
       !sz      = szMax +# 1# -- for the potential carry
-   in withNewWordArrayTrimed# sz \mwa s ->
+   in withNewWordArrayTrimmed# sz \mwa s ->
          inline bignat_add mwa a b s
 
 -------------------------------------------------
@@ -443,7 +456,7 @@
    | isTrue# (bigNatSize# a ==# 1#)
    = case timesWord2# (bigNatIndex# a 0#) w of
       (# h, l #) -> bigNatFromWord2# h l
-   | True = withNewWordArrayTrimed# (bigNatSize# a +# 1#) \mwa s ->
+   | True = withNewWordArrayTrimmed# (bigNatSize# a +# 1#) \mwa s ->
                inline bignat_mul_word mwa a w s
 
 -- | Multiply a BigNAt by a Word
@@ -469,7 +482,7 @@
          !szA = wordArraySize# a
          !szB = wordArraySize# b
          !sz  = szA +# szB
-      in withNewWordArrayTrimed# sz \mwa s->
+      in withNewWordArrayTrimmed# sz \mwa s->
             inline bignat_mul mwa a b s
 
 
@@ -483,7 +496,7 @@
 bigNatSubWordUnsafe# :: BigNat# -> Word# -> BigNat#
 bigNatSubWordUnsafe# x y
    | 0## <- y = x
-   | True     = withNewWordArrayTrimed# sz \mwa -> go mwa y 0#
+   | True     = withNewWordArrayTrimmed# sz \mwa -> go mwa y 0#
    where
       !sz = wordArraySize# x
 
@@ -511,7 +524,7 @@
    | 0## <- b          = (# | a #)
    | bigNatIsZero a    = (# (# #) | #)
    | True
-   = withNewWordArrayTrimedMaybe# (bigNatSize# a) \mwa s ->
+   = withNewWordArrayTrimmedMaybe# (bigNatSize# a) \mwa s ->
             inline bignat_sub_word mwa a b s
 
 
@@ -521,7 +534,7 @@
    | bigNatIsZero b = a
    | True =
       let szA = wordArraySize# a
-      in withNewWordArrayTrimed# szA \mwa s->
+      in withNewWordArrayTrimmed# szA \mwa s->
             case inline bignat_sub mwa a b s of
                (# s', 1# #) -> s'
                (# s', _  #) -> case raiseUnderflow of
@@ -537,7 +550,7 @@
    = (# (# #) | #)
 
    | True
-   = withNewWordArrayTrimedMaybe# (bigNatSize# a) \mwa s ->
+   = withNewWordArrayTrimmedMaybe# (bigNatSize# a) \mwa s ->
             inline bignat_sub mwa a b s
 
 
@@ -556,7 +569,7 @@
    | True =
    let
       sz = wordArraySize# a
-   in withNewWordArrayTrimed# sz \mwq s ->
+   in withNewWordArrayTrimmed# sz \mwq s ->
          inline bignat_quot_word mwq a b s
 
 -- | Divide a BigNat by a Word, return the quotient
@@ -628,7 +641,7 @@
    | isTrue# (szB ==# 1#)    = case bigNatQuotRemWord# a (bigNatIndex# b 0#) of
                                  (# q, r #) -> (# q, bigNatFromWord# r #)
 
-   | True = withNewWordArray2Trimed# szQ szR \mwq mwr s ->
+   | True = withNewWordArray2Trimmed# szQ szR \mwq mwr s ->
                      inline bignat_quotrem mwq mwr a b s
    where
    cmp = bigNatCompare a b
@@ -647,7 +660,7 @@
    | LT <- cmp               = bigNatZero# (# #)
    | EQ <- cmp               = bigNatOne# (# #)
    | isTrue# (szB ==# 1#)    = bigNatQuotWord# a (bigNatIndex# b 0#)
-   | True                    = withNewWordArrayTrimed# szQ \mwq s ->
+   | True                    = withNewWordArrayTrimmed# szQ \mwq s ->
                                  inline bignat_quot mwq a b s
    where
    cmp = bigNatCompare a b
@@ -665,7 +678,7 @@
    | EQ <- cmp               = bigNatZero# (# #)
    | isTrue# (szB ==# 1#)    = case bigNatRemWord# a (bigNatIndex# b 0#) of
                                  r -> bigNatFromWord# r
-   | True                    = withNewWordArrayTrimed# szR \mwr s ->
+   | True                    = withNewWordArrayTrimmed# szR \mwr s ->
                                  inline bignat_rem mwr a b s
    where
    cmp = bigNatCompare a b
@@ -719,7 +732,7 @@
       _            ->
          let
             go wx wy = -- wx > wy
-               withNewWordArrayTrimed# (wordArraySize# wy) \mwr s ->
+               withNewWordArrayTrimmed# (wordArraySize# wy) \mwr s ->
                   bignat_gcd mwr wx wy s
          in case bigNatCompare a b of
                EQ -> a
@@ -808,7 +821,7 @@
 bigNatAnd a b
    | bigNatIsZero a = a
    | bigNatIsZero b = b
-   | True           = withNewWordArrayTrimed# sz \mwa s ->
+   | True           = withNewWordArrayTrimmed# sz \mwa s ->
                         inline bignat_and mwa a b s
    where
       !szA = wordArraySize# a
@@ -820,7 +833,7 @@
 bigNatAndNot a b
    | bigNatIsZero a = a
    | bigNatIsZero b = a
-   | True           = withNewWordArrayTrimed# szA \mwa s ->
+   | True           = withNewWordArrayTrimmed# szA \mwa s ->
                         inline bignat_and_not mwa a b s
    where
       !szA = wordArraySize# a
@@ -860,7 +873,7 @@
 bigNatXor a b
    | bigNatIsZero a = b
    | bigNatIsZero b = a
-   | True           = withNewWordArrayTrimed# sz \mwa s ->
+   | True           = withNewWordArrayTrimmed# sz \mwa s ->
                         inline bignat_xor mwa a b s
    where
       !szA = wordArraySize# a
@@ -907,7 +920,7 @@
       !szA = wordArraySize# a
       !nw  = word2Int# (n `uncheckedShiftRL#` WORD_SIZE_BITS_SHIFT#)
       !sz  = szA -# nw
-     in withNewWordArrayTrimed# sz \mwa s ->
+     in withNewWordArrayTrimmed# sz \mwa s ->
          inline bignat_shiftr mwa a n s
 
 -- | Bit shift right (two's complement)
@@ -928,7 +941,7 @@
       !szA = wordArraySize# a
       !nw  = (word2Int# n -# 1#) `uncheckedIShiftRL#` WORD_SIZE_BITS_SHIFT#
       !sz  = szA -# nw
-     in withNewWordArrayTrimed# sz \mwa s ->
+     in withNewWordArrayTrimmed# sz \mwa s ->
          inline bignat_shiftr_neg mwa a n s
 
 
@@ -956,7 +969,7 @@
       !nb  = word2Int# (n `and#` WORD_SIZE_BITS_MASK##)
       !sz   = szA +# nw +# (nb /=# 0#)
 
-     in withNewWordArrayTrimed# sz \mwa s ->
+     in withNewWordArrayTrimmed# sz \mwa s ->
          inline bignat_shiftl mwa a n s
 
 
@@ -1032,6 +1045,7 @@
 
 -- | BigNat set bit
 bigNatSetBit# :: BigNat# -> Word# -> BigNat#
+{-# NOINLINE bigNatSetBit# #-}
 bigNatSetBit# a n
    -- check the current bit value
    | isTrue# (bigNatTestBit# a n) = a
@@ -1056,10 +1070,25 @@
 
 -- | Reverse the given bit
 bigNatComplementBit# :: BigNat# -> Word# -> BigNat#
-bigNatComplementBit# bn i
-  | isTrue# (bigNatTestBit# bn i) = bigNatClearBit# bn i
-  | True                          = bigNatSetBit#   bn i
+bigNatComplementBit# a n =
+   let
+      !sz = wordArraySize# a
+      !nw = word2Int# (n `uncheckedShiftRL#` WORD_SIZE_BITS_SHIFT#)
+      !nb = word2Int# (n `and#` WORD_SIZE_BITS_MASK##)
+      d   = nw +# 1# -# sz
+   in if
+      -- result BigNat will have more limbs
+      | isTrue# (d ># 0#)
+      -> withNewWordArray# (nw +# 1#) \mwa s ->
+            case mwaArrayCopy# mwa 0# a 0# sz s of
+               s' -> case mwaFill# mwa 0## (int2Word# sz) (int2Word# (d -# 1#)) s' of
+                  s'' -> writeWordArray# mwa nw (bitW# nb) s''
 
+      | nv <- bigNatIndex# a nw `xor#` bitW# nb
+      -> withNewWordArrayTrimmed# sz \mwa s ->
+            case mwaArrayCopy# mwa 0# a 0# sz s of
+               s' -> writeWordArray# mwa nw nv s'
+
 -------------------------------------------------
 -- Log operations
 -------------------------------------------------
@@ -1175,7 +1204,7 @@
    | bigNatIsZero e = bigNatFromWord# 1##
    | bigNatIsZero b = bigNatFromWord# 0##
    | bigNatIsOne  b = bigNatFromWord# 1##
-   | True           = withNewWordArrayTrimed# (bigNatSize# m) \mwa s ->
+   | True           = withNewWordArrayTrimmed# (bigNatSize# m) \mwa s ->
                          inline bignat_powmod mwa b e m s
 
 -- | Return count of trailing zero bits
@@ -1226,6 +1255,7 @@
 -- written in advance. In case of @/i/ == 0@, the function will write and report
 -- zero bytes written.
 bigNatToAddrLE# :: BigNat# -> Addr# -> State# s -> (# State# s, Word# #)
+{-# NOINLINE bigNatToAddrLE# #-}
 bigNatToAddrLE# a addr s0
    | isTrue# (sz ==# 0#) = (# s0, 0## #)
    | True = case writeMSB s0 of
@@ -1255,6 +1285,7 @@
 -- written in advance. In case of @/i/ == 0@, the function will write and report
 -- zero bytes written.
 bigNatToAddrBE# :: BigNat# -> Addr# -> State# s -> (# State# s, Word# #)
+{-# NOINLINE bigNatToAddrBE# #-}
 bigNatToAddrBE# a addr s0
    | isTrue# (sz ==# 0#) = (# s0, 0## #)
    | msw <- indexWordArray# a (sz -# 1#)
@@ -1312,8 +1343,9 @@
 --
 -- The size is given in bytes.
 --
--- Higher limbs equal to 0 are automatically trimed.
+-- Higher limbs equal to 0 are automatically trimmed.
 bigNatFromAddrLE# :: Word# -> Addr# -> State# s -> (# State# s, BigNat# #)
+{-# NOINLINE bigNatFromAddrLE# #-}
 bigNatFromAddrLE# 0## _    s = (# s, bigNatZero# (# #) #)
 bigNatFromAddrLE# sz  addr s =
    let
@@ -1347,8 +1379,9 @@
 --
 -- The size is given in bytes.
 --
--- Null higher limbs are automatically trimed.
+-- Null higher limbs are automatically trimmed.
 bigNatFromAddrBE# :: Word# -> Addr# -> State# s -> (# State# s, BigNat# #)
+{-# NOINLINE bigNatFromAddrBE# #-}
 bigNatFromAddrBE# 0## _    s = (# s, bigNatZero# (# #) #)
 bigNatFromAddrBE# sz  addr s =
    let
@@ -1387,7 +1420,7 @@
 -- byte first (big-endian) if @1#@ or least significant byte first
 -- (little-endian) if @0#@.
 --
--- Null higher limbs are automatically trimed.
+-- Null higher limbs are automatically trimmed.
 bigNatFromAddr# :: Word# -> Addr# -> Bool# -> State# s -> (# State# s, BigNat# #)
 bigNatFromAddr# sz addr 0# s = bigNatFromAddrLE# sz addr s
 bigNatFromAddr# sz addr _  s = bigNatFromAddrBE# sz addr s
@@ -1403,6 +1436,7 @@
 -- written in advance. In case of @/i/ == 0@, the function will write and report
 -- zero bytes written.
 bigNatToMutableByteArrayLE# :: BigNat# -> MutableByteArray# s -> Word# -> State# s -> (# State# s, Word# #)
+{-# NOINLINE bigNatToMutableByteArrayLE# #-}
 bigNatToMutableByteArrayLE# a mba moff s0
    | isTrue# (sz ==# 0#) = (# s0, 0## #)
    | True = case writeMSB s0 of
@@ -1432,6 +1466,7 @@
 -- written in advance. In case of @/i/ == 0@, the function will write and report
 -- zero bytes written.
 bigNatToMutableByteArrayBE# :: BigNat# -> MutableByteArray# s -> Word# -> State# s -> (# State# s, Word# #)
+{-# NOINLINE bigNatToMutableByteArrayBE# #-}
 bigNatToMutableByteArrayBE# a mba moff s0
    | isTrue# (sz ==# 0#) = (# s0, 0## #)
    | msw <- indexWordArray# a (sz -# 1#)
@@ -1473,8 +1508,9 @@
 --
 -- The size is given in bytes.
 --
--- Null higher limbs are automatically trimed.
+-- Null higher limbs are automatically trimmed.
 bigNatFromByteArrayLE# :: Word# -> ByteArray# -> Word# -> State# s -> (# State# s, BigNat# #)
+{-# NOINLINE bigNatFromByteArrayLE# #-}
 bigNatFromByteArrayLE# 0## _  _    s = (# s, bigNatZero# (# #) #)
 bigNatFromByteArrayLE# sz  ba moff s =
    let
@@ -1508,8 +1544,9 @@
 --
 -- The size is given in bytes.
 --
--- Null higher limbs are automatically trimed.
+-- Null higher limbs are automatically trimmed.
 bigNatFromByteArrayBE# :: Word# -> ByteArray# -> Word# -> State# s -> (# State# s, BigNat# #)
+{-# NOINLINE bigNatFromByteArrayBE# #-}
 bigNatFromByteArrayBE# 0## _  _    s = (# s, bigNatZero# (# #) #)
 bigNatFromByteArrayBE# sz  ba moff s =
    let
@@ -1548,7 +1585,7 @@
 -- byte first (big-endian) if @1#@ or least significant byte first
 -- (little-endian) if @0#@.
 --
--- Null higher limbs are automatically trimed.
+-- Null higher limbs are automatically trimmed.
 bigNatFromByteArray# :: Word# -> ByteArray# -> Word# -> Bool# -> State# s -> (# State# s, BigNat# #)
 bigNatFromByteArray# sz ba off 0# s = bigNatFromByteArrayLE# sz ba off s
 bigNatFromByteArray# sz ba off _  s = bigNatFromByteArrayBE# sz ba off s
@@ -1562,6 +1599,7 @@
 -- If possible 'WordArray#', will be used directly (i.e. shared
 -- /without/ cloning the 'WordArray#' into a newly allocated one)
 bigNatFromWordArray# :: WordArray# -> Word# -> BigNat#
+{-# NOINLINE bigNatFromWordArray# #-}
 bigNatFromWordArray# wa n0
    | isTrue# (n `eqWord#` 0##)
    = bigNatZero# (# #)
diff --git a/src/GHC/Num/Integer.hs b/src/GHC/Num/Integer.hs
--- a/src/GHC/Num/Integer.hs
+++ b/src/GHC/Num/Integer.hs
@@ -194,7 +194,7 @@
 integerFromNatural :: Natural -> Integer
 {-# NOINLINE integerFromNatural #-}
 integerFromNatural (NS x) = integerFromWord# x
-integerFromNatural (NB x) = integerFromBigNat# x
+integerFromNatural (NB x) = IP x
 
 -- | Convert a list of Word into an Integer
 integerFromWordList :: Bool -> [Word] -> Integer
@@ -282,7 +282,6 @@
 
 -- | Equal predicate.
 integerEq# :: Integer -> Integer -> Bool#
-{-# NOINLINE integerEq# #-}
 integerEq# (IS x) (IS y) = x ==# y
 integerEq# (IN x) (IN y) = bigNatEq# x y
 integerEq# (IP x) (IP y) = bigNatEq# x y
@@ -290,7 +289,6 @@
 
 -- | Not-equal predicate.
 integerNe# :: Integer -> Integer -> Bool#
-{-# NOINLINE integerNe# #-}
 integerNe# (IS x) (IS y) = x /=# y
 integerNe# (IN x) (IN y) = bigNatNe# x y
 integerNe# (IP x) (IP y) = bigNatNe# x y
@@ -298,31 +296,27 @@
 
 -- | Greater predicate.
 integerGt# :: Integer -> Integer -> Bool#
-{-# NOINLINE integerGt# #-}
-integerGt# (IS x) (IS y)                   = x ># y
-integerGt# x y | GT <- integerCompare' x y = 1#
-integerGt# _ _                             = 0#
+integerGt# (IS x) (IS y)                  = x ># y
+integerGt# x y | GT <- integerCompare x y = 1#
+integerGt# _ _                            = 0#
 
 -- | Lower-or-equal predicate.
 integerLe# :: Integer -> Integer -> Bool#
-{-# NOINLINE integerLe# #-}
-integerLe# (IS x) (IS y)                   = x <=# y
-integerLe# x y | GT <- integerCompare' x y = 0#
-integerLe# _ _                             = 1#
+integerLe# (IS x) (IS y)                  = x <=# y
+integerLe# x y | GT <- integerCompare x y = 0#
+integerLe# _ _                            = 1#
 
 -- | Lower predicate.
 integerLt# :: Integer -> Integer -> Bool#
-{-# NOINLINE integerLt# #-}
-integerLt# (IS x) (IS y)                   = x <# y
-integerLt# x y | LT <- integerCompare' x y = 1#
-integerLt# _ _                             = 0#
+integerLt# (IS x) (IS y)                  = x <# y
+integerLt# x y | LT <- integerCompare x y = 1#
+integerLt# _ _                            = 0#
 
 -- | Greater-or-equal predicate.
 integerGe# :: Integer -> Integer -> Bool#
-{-# NOINLINE integerGe# #-}
-integerGe# (IS x) (IS y)                   = x >=# y
-integerGe# x y | LT <- integerCompare' x y = 0#
-integerGe# _ _                             = 1#
+integerGe# (IS x) (IS y)                  = x >=# y
+integerGe# x y | LT <- integerCompare x y = 0#
+integerGe# _ _                            = 1#
 
 instance Eq Integer where
    (==) = integerEq
@@ -330,20 +324,16 @@
 
 -- | Compare two Integer
 integerCompare :: Integer -> Integer -> Ordering
-{-# NOINLINE integerCompare #-}
-integerCompare = integerCompare'
-
-integerCompare' :: Integer -> Integer -> Ordering
-{-# INLINE integerCompare' #-}
-integerCompare' (IS x) (IS y) = compareInt# x y
-integerCompare' (IP x) (IP y) = bigNatCompare x y
-integerCompare' (IN x) (IN y) = bigNatCompare y x
-integerCompare' (IS _) (IP _) = LT
-integerCompare' (IS _) (IN _) = GT
-integerCompare' (IP _) (IS _) = GT
-integerCompare' (IN _) (IS _) = LT
-integerCompare' (IP _) (IN _) = GT
-integerCompare' (IN _) (IP _) = LT
+{-# INLINEABLE integerCompare #-}
+integerCompare (IS x) (IS y) = compareInt# x y
+integerCompare (IP x) (IP y) = bigNatCompare x y
+integerCompare (IN x) (IN y) = bigNatCompare y x
+integerCompare (IS _) (IP _) = LT
+integerCompare (IS _) (IN _) = GT
+integerCompare (IP _) (IS _) = GT
+integerCompare (IN _) (IS _) = LT
+integerCompare (IP _) (IN _) = GT
+integerCompare (IN _) (IP _) = LT
 
 instance Ord Integer where
    compare = integerCompare
@@ -524,13 +514,11 @@
 -- | Return @-1@, @0@, and @1@ depending on whether argument is
 -- negative, zero, or positive, respectively
 integerSignum :: Integer -> Integer
-{-# NOINLINE integerSignum #-}
 integerSignum !j = IS (integerSignum# j)
 
 -- | Return @-1#@, @0#@, and @1#@ depending on whether argument is
 -- negative, zero, or positive, respectively
 integerSignum# :: Integer -> Int#
-{-# NOINLINE integerSignum# #-}
 integerSignum# (IN _)  = -1#
 integerSignum# (IS i#) = sgnI# i#
 integerSignum# (IP _ ) =  1#
@@ -649,7 +637,7 @@
                IS -1# -> IS -1#
                IS  y  -> IS (orI# x y)
                IP  y
-                  | isTrue# (x >=# 0#) -> integerFromBigNat# (bigNatOrWord# y (int2Word# x))
+                  | isTrue# (x >=# 0#) -> IP (bigNatOrWord# y (int2Word# x))
                   | True               -> integerFromBigNatNeg#
                                              (bigNatAddWord#
                                                 (bigNatAndNot -- use De Morgan's laws
@@ -672,7 +660,7 @@
                                                 1##)
    IP  x  -> case b of
                IS _ -> integerOr b a
-               IP y -> integerFromBigNat# (bigNatOr x y)
+               IP y -> IP (bigNatOr x y)
                IN y -> integerFromBigNatNeg#
                         (bigNatAddWord#
                            (bigNatAndNot -- use De Morgan's laws
@@ -1010,14 +998,12 @@
 integerIsPowerOf2# (IN _) = (# (# #) | #)
 integerIsPowerOf2# (IP w) = bigNatIsPowerOf2# w
 
-#if WORD_SIZE_IN_BITS == 32
-
--- | Convert an Int64# into an Integer on 32-bit architectures
+-- | Convert an Int64# into an Integer
 integerFromInt64# :: Int64# -> Integer
 {-# NOINLINE integerFromInt64# #-}
-integerFromInt64# !i
-  | isTrue# ((i `leInt64#` intToInt64#  0x7FFFFFFF#)
-      &&# (i `geInt64#` intToInt64# -0x80000000#))
+integerFromInt64# i
+  | isTrue# ((i `leInt64#` intToInt64#  INT_MAXBOUND#)
+      &&# (i `geInt64#` intToInt64# INT_MINBOUND#))
   = IS (int64ToInt# i)
 
   | isTrue# (i `geInt64#` intToInt64# 0#)
@@ -1026,37 +1012,29 @@
   | True
   = IN (bigNatFromWord64# (int64ToWord64# (negateInt64# i)))
 
--- | Convert a Word64# into an Integer on 32-bit architectures
+-- | Convert a Word64# into an Integer
 integerFromWord64# :: Word64# -> Integer
 {-# NOINLINE integerFromWord64# #-}
 integerFromWord64# !w
-  | isTrue# (w `leWord64#` wordToWord64# 0x7FFFFFFF##)
+  | isTrue# (w `leWord64#` wordToWord64# INT_MAXBOUND##)
   = IS (int64ToInt# (word64ToInt64# w))
   | True
   = IP (bigNatFromWord64# w)
 
--- | Convert an Integer into an Int64# on 32-bit architectures
+-- | Convert an Integer into an Int64#
 integerToInt64# :: Integer -> Int64#
 {-# NOINLINE integerToInt64# #-}
 integerToInt64# (IS i) = intToInt64# i
 integerToInt64# (IP b) = word64ToInt64# (bigNatToWord64# b)
 integerToInt64# (IN b) = negateInt64# (word64ToInt64# (bigNatToWord64# b))
 
--- | Convert an Integer into a Word64# on 32-bit architectures
+-- | Convert an Integer into a Word64#
 integerToWord64# :: Integer -> Word64#
 {-# NOINLINE integerToWord64# #-}
 integerToWord64# (IS i) = int64ToWord64# (intToInt64# i)
 integerToWord64# (IP b) = bigNatToWord64# b
 integerToWord64# (IN b) = int64ToWord64# (negateInt64# (word64ToInt64# (bigNatToWord64# b)))
 
-#else
-
--- | Convert an Int64# into an Integer on 64-bit architectures
-integerFromInt64# :: Int# -> Integer
-integerFromInt64# !x = IS x
-
-#endif
-
 ----------------------------------------------------------------------------
 -- Conversions to/from floating point
 ----------------------------------------------------------------------------
@@ -1269,3 +1247,271 @@
 
      -- e > 0 by cases above
    | True = (# Backend.integer_powmod b (integerToNatural e) m | #)
+
+
+{-
+Note [Optimising conversions between numeric types]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Converting between numeric types is very common in Haskell codes.  Suppose that
+we have N inter-convertible numeric types (Word, Word8, Word32, Int, etc.).
+
+- We don't want to have to use one conversion function per pair of types as that
+would require N^2 functions: wordToWord8, wordToInt, word8ToWord32...
+
+- The following kind of class would allow us to have a single conversion
+function but at the price of N^2 instances and of the use of
+MultiParamTypeClasses extension.
+
+    class Convert a b where
+      convert :: a -> b
+
+So what we do instead is that we use the Integer type (signed, unbounded) as a
+passthrough type to perform every conversion. Hence we only need to define two
+functions per numeric type:
+
+  class Integral a where
+    toInteger :: a -> Integer
+
+  class Num a where
+    fromInteger :: Integer -> a
+
+These classes have a single parameter and can be derived automatically (e.g. for
+newtypes). So we don't even have to define 2*N instances. For example, all the
+instances for the types in Foreign.C.Types (CChar, CShort, CInt, CUInt, etc.)
+are automatically derived from the instances for Word, Int, Word8, Word16, etc.
+
+Finally we can define a generic conversion function:
+
+  -- in the Prelude
+  fromIntegral :: (Integral a, Num b) => a -> b
+  fromIntegral = fromInteger . toInteger
+
+Efficient conversions
+~~~~~~~~~~~~~~~~~~~~~
+
+An issue with this approach is that performance might be terrible. E.g.
+converting an Int into a Word, which is a no-op at the machine level, becomes
+costly when performed via `fromIntegral` or any similar function because an
+intermediate Integer has to be allocated in the heap to perform the conversion.
+
+A solution is to bless one particular `fromIntegral`-like function and to use
+rewrite rules to replace it with a more efficient function when both types are
+known. This is what was done in the past, see next section. We use another
+approach nowadays:
+
+Notice that the set of primitive operations to convert from and to Integer and
+Natural is pretty small:
+
+  - Natural <-> Word#/BigNat#
+  - Integer <-> Int#/Word#/Natural/BigNat# (+ Int64#/Word64# on 32-bit arch)
+
+For example, we have the following primitives:
+  - integerToWord#   :: Integer -> Word#
+  - integerFromWord# :: Word# -> Integer
+  - integerToInt#    :: Integer -> Int#
+  - ...
+
+Compared to optimising `fromIntegral :: (Integral a, Num b) => a -> b` where `a`
+and `b` are arbitrary, we only have to write rewrite rules for the concrete
+types that can be converted from and to Natural/Integer. All the other ones
+necessarily pass through these concrete types!
+
+For example we have the following rules:
+    integerToWord# (integerFromWord# x) ===> x
+    integerToInt# (integerFromWord# x)  ===> word2Int# x
+
+But we don't need rules to handle conversion from/to e.g. Word32# because there
+is no Word32#-to-Integer primitive: Word32# must be converted into something
+else first (e.g. Word#) for which we have rules.
+
+We rely on inlining of fromInteger/toInteger and on other transformations (e.g.
+float-in) to make these rules likely to fire. It seems to work well in practice.
+
+Example 1: converting an Int into a Word
+
+  fromIntegral @Int @Word x
+
+  ===> {inline fromIntegral}
+  fromInteger @Word (toInteger @Int x)
+
+  ===> {inline fromInteger and toInteger}
+  W# (integerToWord# (case x of { I# x# -> IS x# }))
+
+  ===> {float-in}
+  case x of { I# x# -> W# (integerToWord# (IS x#)) }
+
+  ===> {rewrite rule for "integerToWord# . IS"}
+  case x of { I# x# -> W# (int2Word# x#) }
+
+
+Example 2: converting an Int8 into a Word32
+
+  fromIntegral @Int8 @Word32 x
+
+  ===> {inline fromIntegral}
+  fromInteger @Word32 (toInteger @Int8 x)
+
+  ===> {inline fromInteger and toInteger}
+  W32# (wordToWord32# (integerToWord# (case x of { I8# x# -> IS (int8ToInt# x#) })))
+
+  ===> {float-in}
+  case x of { I8# x# -> W32# (wordToWord32# (integerToWord# (IS (int8ToInt# x#)))) }
+
+  ===> {rewrite rule for "integerToWord# . IS"}
+  case x of { I8# x# -> W32# (wordToWord32# (int2Word# (int8ToInt# x#))) }
+
+  Notice that in the resulting expression the value passes through types Int#
+  and Word# with native machine word size: it is first sign-extended from Int8#
+  to Int#, then cast into Word#, and finally truncated into Word32#. These are
+  all very cheap operations that are performed in registers without allocating
+  anything in the heap.
+
+
+
+Historical fromIntegral optimisations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the past, `fromIntegral` function in the Prelude was special because many
+rewrite rules were mentioning it explicitly. For example to replace a call to
+`fromIntegral :: Int -> Word`, which allocates an intermediate Integer, with a
+call to `intToWord`, which is a no-op when compiled into machine code. Nowadays
+`fromIntegral` isn't a special function anymore and we just INLINE it (see above).
+
+- first `fromIntegral` was specialized (SPECIALIZE pragma). However it would
+need N^2 pragmas to cover every case and it wouldn't cover user defined numeric
+types which don't belong to base.
+
+- `-fwarn-identities` enables a warning to detect useless conversions via
+fromIntegral (since 0656c72a8f):
+
+  > fromIntegral (1 :: Int) :: Int
+
+  <interactive>:3:21: warning: [-Widentities]
+      Call of fromIntegral :: Int -> Int
+        can probably be omitted
+
+
+- many rules were added (e.g. in e0c787c10f) to perform float-in transformations
+explicitly (to allow more fromIntegral rules to fire) and to replace some
+fromIntegral calls with faster operations:
+
+    "fromIntegral/Int8->Int8" fromIntegral = id :: Int8 -> Int8
+    "fromIntegral/a->Int8"    fromIntegral = \x -> case fromIntegral x of I# x# -> I8# (intToInt8# x#)
+    "fromIntegral/Int8->a"    fromIntegral = \(I8# x#) -> fromIntegral (I# x#)
+
+It worked but there were still some issues with this approach:
+
+1. These rules only work for `fromIntegral`. If we wanted to define our own
+   similar function (e.g. using other type-classes), we would also have to redefine
+   all the rules to get similar performance.
+
+2. `fromIntegral` had to be marked `NOINLINE [1]`:
+    - NOINLINE to allow rules to match
+    - [1] to allow inlining in later phases to avoid incurring a function call
+      overhead for such a trivial operation
+
+   Users of the function had to be careful because a simple helper without an
+   INLINE pragma like:
+
+      toInt :: Integral a => a -> Int
+      toInt = fromIntegral
+
+   had the following unfolding:
+
+      toInt = integerToInt . toInteger
+
+   which doesn't mention `fromIntegral` anymore. Hence `fromIntegral` rules
+   wouldn't fire for codes using `toInt` while they would if they had used
+   `fromIntegral` directly!
+   For this reason, a bunch of rules for bignum primitives as we have now were
+   already present to handle these cases.
+
+3. These rewrite rules were tedious to write and error-prone (cf #19345).
+
+For these reasons, it is simpler to not consider fromIntegral special at all and
+to only rely on rewrite rules for bignum functions.
+
+-}
+
+-- See Note [Optimising conversions between numeric types]
+{-# RULES
+"Word# -> Natural -> Integer"
+  forall x. integerFromNatural (NS x) = integerFromWord# x
+
+"BigNat# -> Natural -> Integer"
+  forall x. integerFromNatural (NB x) = IP x
+
+"Int# -> Integer -> Int#"
+  forall x. integerToInt# (IS x) = x
+
+"Word# -> Integer -> Word#"
+  forall x. integerToWord# (integerFromWord# x) = x
+
+"Natural -> Integer -> Natural (wrap)"
+  forall x. integerToNatural (integerFromNatural x) = x
+
+"Natural -> Integer -> Natural (throw)"
+  forall x. integerToNaturalThrow (integerFromNatural x) = x
+
+"Natural -> Integer -> Natural (clamp)"
+  forall x. integerToNaturalClamp (integerFromNatural x) = x
+
+"Natural -> Integer -> Word#"
+  forall x. integerToWord# (integerFromNatural x) = naturalToWord# x
+
+"Int# -> Integer -> Word#"
+  forall x. integerToWord# (IS x) = int2Word# x
+
+"Word# -> Integer -> Int#"
+  forall x. integerToInt# (integerFromWord# x) = word2Int# x
+
+"Word# -> Integer -> Natural (wrap)"
+  forall x. integerToNatural (integerFromWord# x) = NS x
+
+"Word# -> Integer -> Natural (throw)"
+  forall x. integerToNaturalThrow (integerFromWord# x) = NS x
+
+"Word# -> Integer -> Natural (clamp)"
+  forall x. integerToNaturalClamp (integerFromWord# x) = NS x
+
+#-}
+
+{-# RULES
+
+"Int64# -> Integer -> Int64#"
+  forall x. integerToInt64# (integerFromInt64# x) = x
+
+"Word64# -> Integer -> Word64#"
+  forall x. integerToWord64# (integerFromWord64# x) = x
+
+"Int64# -> Integer -> Word64#"
+  forall x. integerToWord64# (integerFromInt64# x) = int64ToWord64# x
+
+"Word64# -> Integer -> Int64#"
+  forall x. integerToInt64# (integerFromWord64# x) = word64ToInt64# x
+
+"Word# -> Integer -> Word64#"
+  forall x. integerToWord64# (integerFromWord# x) = wordToWord64# x
+
+"Word64# -> Integer -> Word#"
+  forall x. integerToWord# (integerFromWord64# x) = word64ToWord# x
+
+"Int# -> Integer -> Int64#"
+  forall x. integerToInt64# (IS x) = intToInt64# x
+
+"Int64# -> Integer -> Int#"
+  forall x. integerToInt# (integerFromInt64# x) = int64ToInt# x
+
+"Int# -> Integer -> Word64#"
+  forall x. integerToWord64# (IS x) = int64ToWord64# (intToInt64# x)
+
+"Int64# -> Integer -> Word#"
+  forall x. integerToWord# (integerFromInt64# x) = int2Word# (int64ToInt# x)
+
+"Word# -> Integer -> Int64#"
+  forall x. integerToInt64# (integerFromWord# x) = word64ToInt64# (wordToWord64# x)
+
+"Word64# -> Integer -> Int#"
+  forall x. integerToInt# (integerFromWord64# x) = word2Int# (word64ToWord# x)
+
+#-}
diff --git a/src/GHC/Num/Natural.hs b/src/GHC/Num/Natural.hs
--- a/src/GHC/Num/Natural.hs
+++ b/src/GHC/Num/Natural.hs
@@ -72,6 +72,7 @@
 
 -- | Create a Natural from a BigNat# (respect the invariants)
 naturalFromBigNat# :: BigNat# -> Natural
+{-# NOINLINE naturalFromBigNat# #-}
 naturalFromBigNat# x = case bigNatSize# x of
    0# -> naturalZero
    1# -> NS (bigNatIndex# x 0#)
@@ -79,6 +80,7 @@
 
 -- | Convert a Natural into a BigNat#
 naturalToBigNat# :: Natural -> BigNat#
+{-# NOINLINE naturalToBigNat# #-}
 naturalToBigNat# (NS w)  = bigNatFromWord# w
 naturalToBigNat# (NB bn) = bn
 
@@ -112,7 +114,6 @@
 
 -- | Convert a Natural into a Word# clamping to (maxBound :: Word#).
 naturalToWordClamp# :: Natural -> Word#
-{-# NOINLINE naturalToWordClamp #-}
 naturalToWordClamp# (NS x) = x
 naturalToWordClamp# (NB _) = WORD_MAXBOUND##
 
@@ -140,7 +141,6 @@
 
 -- | Equality test for Natural
 naturalEq# :: Natural -> Natural -> Bool#
-{-# NOINLINE naturalEq# #-}
 naturalEq# (NS x) (NS y) = x `eqWord#` y
 naturalEq# (NB x) (NB y) = bigNatEq# x y
 naturalEq# _      _      = 0#
@@ -151,7 +151,6 @@
 
 -- | Inequality test for Natural
 naturalNe# :: Natural -> Natural -> Bool#
-{-# NOINLINE naturalNe# #-}
 naturalNe# (NS x) (NS y) = x `neWord#` y
 naturalNe# (NB x) (NB y) = bigNatNe# x y
 naturalNe# _      _      = 1#
@@ -162,7 +161,6 @@
 
 -- | Greater or equal test for Natural
 naturalGe# :: Natural -> Natural -> Bool#
-{-# NOINLINE naturalGe# #-}
 naturalGe# (NS x) (NS y) = x `geWord#` y
 naturalGe# (NS _) (NB _) = 0#
 naturalGe# (NB _) (NS _) = 1#
@@ -174,7 +172,6 @@
 
 -- | Lower or equal test for Natural
 naturalLe# :: Natural -> Natural -> Bool#
-{-# NOINLINE naturalLe# #-}
 naturalLe# (NS x) (NS y) = x `leWord#` y
 naturalLe# (NS _) (NB _) = 1#
 naturalLe# (NB _) (NS _) = 0#
@@ -187,7 +184,6 @@
 
 -- | Greater test for Natural
 naturalGt# :: Natural -> Natural -> Bool#
-{-# NOINLINE naturalGt# #-}
 naturalGt# (NS x) (NS y) = x `gtWord#` y
 naturalGt# (NS _) (NB _) = 0#
 naturalGt# (NB _) (NS _) = 1#
@@ -199,7 +195,6 @@
 
 -- | Lower test for Natural
 naturalLt# :: Natural -> Natural -> Bool#
-{-# NOINLINE naturalLt# #-}
 naturalLt# (NS x) (NS y) = x `ltWord#` y
 naturalLt# (NS _) (NB _) = 1#
 naturalLt# (NB _) (NS _) = 0#
@@ -211,7 +206,6 @@
 
 -- | Compare two Natural
 naturalCompare :: Natural -> Natural -> Ordering
-{-# NOINLINE naturalCompare #-}
 naturalCompare (NS x) (NS y) = cmpW# x y
 naturalCompare (NB x) (NB y) = bigNatCompare x y
 naturalCompare (NS _) (NB _) = LT
@@ -330,13 +324,11 @@
 
 -- | Signum for Natural
 naturalSignum :: Natural -> Natural
-{-# NOINLINE naturalSignum #-}
 naturalSignum (NS 0##) = NS 0##
 naturalSignum _        = NS 1##
 
 -- | Negate for Natural
 naturalNegate :: Natural -> Natural
-{-# NOINLINE naturalNegate #-}
 naturalNegate (NS 0##) = NS 0##
 naturalNegate _        = raiseUnderflow
 
@@ -429,6 +421,39 @@
 naturalBit :: Word -> Natural
 naturalBit (W# i) = naturalBit# i
 
+-- | @since 1.3
+naturalSetBit# :: Natural -> Word# -> Natural
+naturalSetBit# (NS n) i
+  | isTrue# (i `ltWord#` WORD_SIZE_IN_BITS##) = NS (n `or#` (1## `uncheckedShiftL#` word2Int# i))
+  | True                                      = NB (bigNatSetBit# (bigNatFromWord# n) i)
+naturalSetBit# (NB n) i                       = NB (bigNatSetBit# n i)
+
+-- | @since 1.3
+naturalSetBit :: Natural -> Word -> Natural
+naturalSetBit !n (W# i) = naturalSetBit# n i
+
+-- | @since 1.3
+naturalClearBit# :: Natural -> Word# -> Natural
+naturalClearBit# x@(NS n) i
+  | isTrue# (i `ltWord#` WORD_SIZE_IN_BITS##) = NS (n `and#` not# (1## `uncheckedShiftL#` word2Int# i))
+  | True                                      = x
+naturalClearBit# (NB n) i                     = naturalFromBigNat# (bigNatClearBit# n i)
+
+-- | @since 1.3
+naturalClearBit :: Natural -> Word -> Natural
+naturalClearBit !n (W# i) = naturalClearBit# n i
+
+-- | @since 1.3
+naturalComplementBit# :: Natural -> Word# -> Natural
+naturalComplementBit# (NS n) i
+  | isTrue# (i `ltWord#` WORD_SIZE_IN_BITS##) = NS (n `xor#` (1## `uncheckedShiftL#` word2Int# i))
+  | True                                      = NB (bigNatSetBit# (bigNatFromWord# n) i)
+naturalComplementBit# (NB n) i                = naturalFromBigNat# (bigNatComplementBit# n i)
+
+-- | @since 1.3
+naturalComplementBit :: Natural -> Word -> Natural
+naturalComplementBit !n (W# i) = naturalComplementBit# n i
+
 -- | Compute greatest common divisor.
 naturalGcd :: Natural -> Natural -> Natural
 {-# NOINLINE naturalGcd #-}
@@ -518,7 +543,6 @@
 -- byte first (big-endian) if @1#@ or least significant byte first
 -- (little-endian) if @0#@.
 naturalToAddr# :: Natural -> Addr# -> Bool# -> State# s -> (# State# s, Word# #)
-{-# NOINLINE naturalToAddr# #-}
 naturalToAddr# (NS i) = wordToAddr# i
 naturalToAddr# (NB n) = bigNatToAddr# n
 
@@ -543,7 +567,6 @@
 --
 -- Null higher limbs are automatically trimed.
 naturalFromAddr# :: Word# -> Addr# -> Bool# -> State# s -> (# State# s, Natural #)
-{-# NOINLINE naturalFromAddr# #-}
 naturalFromAddr# sz addr e s =
    case bigNatFromAddr# sz addr e s of
       (# s', n #) -> (# s', naturalFromBigNat# n #)
@@ -568,7 +591,6 @@
 -- byte first (big-endian) if @1#@ or least significant byte first
 -- (little-endian) if @0#@.
 naturalToMutableByteArray# :: Natural -> MutableByteArray# s -> Word# -> Bool# -> State# s -> (# State# s, Word# #)
-{-# NOINLINE naturalToMutableByteArray# #-}
 naturalToMutableByteArray# (NS w) = wordToMutableByteArray# w
 naturalToMutableByteArray# (NB a) = bigNatToMutableByteArray# a
 
@@ -582,6 +604,17 @@
 --
 -- Null higher limbs are automatically trimed.
 naturalFromByteArray# :: Word# -> ByteArray# -> Word# -> Bool# -> State# s -> (# State# s, Natural #)
-{-# NOINLINE naturalFromByteArray# #-}
 naturalFromByteArray# sz ba off e s = case bigNatFromByteArray# sz ba off e s of
    (# s', a #) -> (# s', naturalFromBigNat# a #)
+
+
+
+-- See Note [Optimising conversions between numeric types]
+-- in GHC.Num.Integer
+{-# RULES
+"Word# -> Natural -> Word#"
+  forall x. naturalToWord# (NS x) = x
+
+"BigNat# -> Natural -> BigNat#"
+  forall x. naturalToBigNat# (naturalFromBigNat# x) = x
+#-}
diff --git a/src/GHC/Num/Primitives.hs b/src/GHC/Num/Primitives.hs
--- a/src/GHC/Num/Primitives.hs
+++ b/src/GHC/Num/Primitives.hs
@@ -598,7 +598,6 @@
 
 -- Note [ghc-bignum exceptions]
 -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---
 -- `ghc-bignum` package can't depend on `base` package (it would create a cyclic
 -- dependency). Hence it can't import "Control.Exception" and throw exceptions
 -- the usual way. Instead it uses some wired-in functions from `ghc-prim` which
diff --git a/src/GHC/Num/WordArray.hs b/src/GHC/Num/WordArray.hs
--- a/src/GHC/Num/WordArray.hs
+++ b/src/GHC/Num/WordArray.hs
@@ -79,17 +79,17 @@
 
 -- | Create a new WordArray# of the given size (*in Word#*), apply the action to
 -- it, trim its most significant zeroes, then return it frozen
-withNewWordArrayTrimed#
+withNewWordArrayTrimmed#
    :: Int#  -- ^ Size in Word
    -> (MutableWordArray# RealWorld -> State# RealWorld -> State# RealWorld)
    -> WordArray#
-withNewWordArrayTrimed# sz act = withNewWordArray# sz \mwa s ->
+withNewWordArrayTrimmed# sz act = withNewWordArray# sz \mwa s ->
    case act mwa s of
       s' -> mwaTrimZeroes# mwa s'
 
 -- | Create two new WordArray# of the given sizes (*in Word#*), apply the action
 -- to them, trim their most significant zeroes, then return them frozen
-withNewWordArray2Trimed#
+withNewWordArray2Trimmed#
    :: Int#  -- ^ Size in Word
    -> Int#  -- ^ Ditto
    -> (MutableWordArray# RealWorld
@@ -97,7 +97,7 @@
       -> State# RealWorld
       -> State# RealWorld)
    -> (# WordArray#, WordArray# #)
-withNewWordArray2Trimed# sz1 sz2 act = withNewWordArray2# sz1 sz2 \mwa1 mwa2 s ->
+withNewWordArray2Trimmed# sz1 sz2 act = withNewWordArray2# sz1 sz2 \mwa1 mwa2 s ->
    case act mwa1 mwa2 s of
       s' -> case mwaTrimZeroes# mwa1 s' of
          s'' -> mwaTrimZeroes# mwa2 s''
@@ -105,11 +105,11 @@
 -- | Create a new WordArray# of the given size (*in Word#*), apply the action to
 -- it. If the action returns true#, trim its most significant zeroes, then
 -- return it frozen. Otherwise, return ().
-withNewWordArrayTrimedMaybe#
+withNewWordArrayTrimmedMaybe#
    :: Int#  -- ^ Size in Word
    -> (MutableWordArray# RealWorld -> State# RealWorld -> (# State# RealWorld, Bool# #))
    -> (# (# #) | WordArray# #)
-withNewWordArrayTrimedMaybe# sz act = case runRW# io of (# _, a #) -> a
+withNewWordArrayTrimmedMaybe# sz act = case runRW# io of (# _, a #) -> a
    where
       io s =
          case newWordArray# sz s of
