diff --git a/GHC/Classes.hs b/GHC/Classes.hs
--- a/GHC/Classes.hs
+++ b/GHC/Classes.hs
@@ -393,8 +393,30 @@
 --
 -- Note that (7.) and (8.) do /not/ require 'min' and 'max' to return either of
 -- their arguments. The result is merely required to /equal/ one of the
--- arguments in terms of '(==)'.
+-- arguments in terms of '(==)'. Users who expect a stronger guarantee are advised
+-- to write their own min and/or max functions.
 --
+-- The nuance of the above distinction is not always fully internalized by
+-- developers, and in the past (tracing back to the Haskell 1.4 Report) the
+-- specification for 'Ord' asserted the stronger property that @(min x y, max x
+-- y) = (x, y)@ or @(y, x)@, or in other words, that 'min' and 'max' /will/
+-- return one of their arguments, using argument order as the tie-breaker if
+-- the arguments are equal by comparison. A few list and
+-- 'Data.Foldable.Foldable' functions have behavior that is best understood
+-- with this assumption in mind: all variations of @minimumBy@ and @maximumBy@
+-- (which can't use 'min' and 'max' in their implementations) are written such
+-- that @minimumBy 'compare'@ and @maximumBy 'compare'@ are respectively
+-- equivalent to @minimum@ and @maximum@ (which do use 'min' and 'max') only if
+-- 'min' and 'max' adhere to this tie-breaking convention. Otherwise, if there
+-- are multiple least or largest elements in a container, @minimum@ and
+-- @maximum@ may not return the /same/ one that @minimumBy 'compare'@ and
+-- @maximumBy 'compare'@ do (though they should return something that is
+-- /equal/). (This is relevant for types with non-extensional equality, like
+-- 'Data.Semigroup.Arg', but also in cases where the precise reference held
+-- matters for memory-management reasons.) Unless there is a reason to deviate,
+-- it is less confusing for implementors of 'Ord' to respect this same
+-- convention (as the default definitions of 'min' and 'max' do).
+--
 -- Minimal complete definition: either 'compare' or '<='.
 -- Using 'compare' can be more efficient for complex types.
 --
@@ -553,6 +575,8 @@
 (I# x) `ltInt` (I# y) = isTrue# (x <#  y)
 (I# x) `leInt` (I# y) = isTrue# (x <=# y)
 
+-- See GHC.Classes#matching_overloaded_methods_in_rules
+{-# INLINE [1] compareInt #-}
 compareInt :: Int -> Int -> Ordering
 (I# x#) `compareInt` (I# y#) = compareInt# x# y#
 
@@ -580,6 +604,8 @@
 (W# x) `ltWord` (W# y) = isTrue# (x `ltWord#` y)
 (W# x) `leWord` (W# y) = isTrue# (x `leWord#` y)
 
+-- See GHC.Classes#matching_overloaded_methods_in_rules
+{-# INLINE [1] compareWord #-}
 compareWord :: Word -> Word -> Ordering
 (W# x#) `compareWord` (W# y#) = compareWord# x# y#
 
diff --git a/GHC/Magic.hs b/GHC/Magic.hs
--- a/GHC/Magic.hs
+++ b/GHC/Magic.hs
@@ -58,8 +58,8 @@
 -- | The call @noinline f@ arranges that @f@ will not be inlined.
 -- It is removed during CorePrep so that its use imposes no overhead
 -- (besides the fact that it blocks inlining.)
-{-# NOINLINE noinline #-}
 noinline :: a -> a
+{-# NOINLINE noinline #-}  -- noinline is inlined manually in CorePrep
 noinline x = x
 
 -- | The 'lazy' function restrains strictness analysis a little. The
@@ -79,6 +79,7 @@
 -- If 'lazy' were not lazy, 'Control.Parallel.par' would look strict in
 -- @y@ which would defeat the whole purpose of 'Control.Parallel.par'.
 lazy :: a -> a
+{-# NOINLINE lazy #-}  -- lazy is inlined manually in CorePrep
 lazy x = x
 -- Implementation note: its strictness and unfolding are over-ridden
 -- by the definition in GHC.Types.Id.Make; in both cases to nothing at all.
diff --git a/GHC/PrimopWrappers.hs b/GHC/PrimopWrappers.hs
--- a/GHC/PrimopWrappers.hs
+++ b/GHC/PrimopWrappers.hs
@@ -801,6 +801,12 @@
 {-# NOINLINE (<=##) #-}
 (<=##) :: Double# -> Double# -> Int#
 (<=##) a1 a2 = (GHC.Prim.<=##) a1 a2
+{-# NOINLINE minDouble# #-}
+minDouble# :: Double# -> Double# -> Double#
+minDouble# a1 a2 = GHC.Prim.minDouble# a1 a2
+{-# NOINLINE maxDouble# #-}
+maxDouble# :: Double# -> Double# -> Double#
+maxDouble# a1 a2 = GHC.Prim.maxDouble# a1 a2
 {-# NOINLINE (+##) #-}
 (+##) :: Double# -> Double# -> Double#
 (+##) a1 a2 = (GHC.Prim.+##) a1 a2
@@ -909,6 +915,12 @@
 {-# NOINLINE leFloat# #-}
 leFloat# :: Float# -> Float# -> Int#
 leFloat# a1 a2 = GHC.Prim.leFloat# a1 a2
+{-# NOINLINE minFloat# #-}
+minFloat# :: Float# -> Float# -> Float#
+minFloat# a1 a2 = GHC.Prim.minFloat# a1 a2
+{-# NOINLINE maxFloat# #-}
+maxFloat# :: Float# -> Float# -> Float#
+maxFloat# a1 a2 = GHC.Prim.maxFloat# a1 a2
 {-# NOINLINE plusFloat# #-}
 plusFloat# :: Float# -> Float# -> Float#
 plusFloat# a1 a2 = GHC.Prim.plusFloat# a1 a2
@@ -1131,6 +1143,12 @@
 {-# NOINLINE isByteArrayPinned# #-}
 isByteArrayPinned# :: ByteArray# -> Int#
 isByteArrayPinned# a1 = GHC.Prim.isByteArrayPinned# a1
+{-# NOINLINE isByteArrayWeaklyPinned# #-}
+isByteArrayWeaklyPinned# :: ByteArray# -> Int#
+isByteArrayWeaklyPinned# a1 = GHC.Prim.isByteArrayWeaklyPinned# a1
+{-# NOINLINE isMutableByteArrayWeaklyPinned# #-}
+isMutableByteArrayWeaklyPinned# :: MutableByteArray# s -> Int#
+isMutableByteArrayWeaklyPinned# a1 = GHC.Prim.isMutableByteArrayWeaklyPinned# a1
 {-# NOINLINE byteArrayContents# #-}
 byteArrayContents# :: ByteArray# -> Addr#
 byteArrayContents# a1 = GHC.Prim.byteArrayContents# a1
@@ -2085,9 +2103,6 @@
 {-# NOINLINE spark# #-}
 spark# :: a -> State# s -> (# State# s,a #)
 spark# a1 a2 = GHC.Prim.spark# a1 a2
-{-# NOINLINE seq# #-}
-seq# :: a -> State# s -> (# State# s,a #)
-seq# a1 a2 = GHC.Prim.seq# a1 a2
 {-# NOINLINE getSpark# #-}
 getSpark# :: State# s -> (# State# s,Int#,a #)
 getSpark# a1 = GHC.Prim.getSpark# a1
diff --git a/GHC/Types.hs b/GHC/Types.hs
--- a/GHC/Types.hs
+++ b/GHC/Types.hs
@@ -65,7 +65,7 @@
 
         -- * Unboxed tuples
         Unit#,
-        Solo#,
+        Solo#(..),
         Tuple0#,
         Tuple1#,
         Tuple2#,
@@ -283,11 +283,43 @@
 *                                                                      *
 ********************************************************************* -}
 
--- | The type constructor 'Any' is type to which you can unsafely coerce any
--- lifted type, and back. More concretely, for a lifted type @t@ and
--- value @x :: t@, @unsafeCoerce (unsafeCoerce x :: Any) :: t@ is equivalent
--- to @x@.
+-- | The type constructor @Any :: forall k. k@ is a type to which you can unsafely coerce any type, and back.
 --
+-- For @unsafeCoerce@ this means for all lifted types @t@ that
+-- @unsafeCoerce (unsafeCoerce x :: Any) :: t@ is equivalent to @x@ and safe.
+--
+-- The same is true for *all* types when using
+-- @
+--   unsafeCoerce# :: forall (r1 :: RuntimeRep) (r2 :: RuntimeRep)
+--                   (a :: TYPE r1) (b :: TYPE r2).
+--                   a -> b
+-- @
+-- but /only/ if you instantiate @r1@ and @r2@ to the /same/ runtime representation.
+-- For example using @(unsafeCoerce# :: forall (a :: TYPE IntRep) (b :: TYPE IntRep). a -> b) x@
+-- is fine, but @(unsafeCoerce# :: forall (a :: TYPE IntRep) (b :: TYPE FloatRep). a -> b)@
+-- will likely cause seg-faults or worse.
+-- For this resason, users should always prefer unsafeCoerce over unsafeCoerce# when possible.
+--
+-- Here are some more examples:
+-- @
+--    bad_a1 :: Any @(TYPE 'IntRep)
+--    bad_a1 = unsafeCoerce# True
+--
+--    bad_a2 :: Any @(TYPE ('BoxedRep 'UnliftedRep))
+--    bad_a2 = unsafeCoerce# True
+-- @
+-- Here @bad_a1@ is bad because we started with @True :: (Bool :: Type)@, represented by a boxed heap pointer,
+-- and coerced it to @a1 :: Any @(TYPE 'IntRep)@, whose representation is a non-pointer integer.
+-- That's why we had to use `unsafeCoerce#`; it is really unsafe because it can change representations.
+-- Similarly @bad_a2@ is bad because although both @True@ and @bad_a2@ are represented by a heap pointer,
+-- @True@ is lifted but @bad_a2@ is not; bugs here may be rather subtle.
+--
+-- If you must use unsafeCoerce# to cast to `Any`, type annotations are recommended
+-- to make sure that @Any@ has the correct kind. As casting between different runtimereps is
+-- unsound. For example to cast a @ByteArray#@ to @Any@ you might use:
+-- @
+--    unsafeCoerce# b :: (Any :: TYPE ('BoxedRep 'Unlifted))
+-- @
 type family Any :: k where { }
 -- See Note [Any types] in GHC.Builtin.Types. Also, for a bit of history on Any see
 -- #10886. Note that this must be a *closed* type family: we need to ensure
@@ -889,7 +921,7 @@
 data Unit# = (# #)
 
 type Solo# :: TYPE rep -> TYPE (TupleRep '[rep])
-data Solo# a = (# a #)
+data Solo# a = MkSolo# a
 
 type Tuple0# = Unit#
 type Tuple1# = Solo#
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,12 @@
+## 0.13.0
+
+- Shipped with GHC 9.12.1
+
+- Add primops that allow users to distinguish weakly pinned byte arrays from unpinned ones.
+
+         isMutableByteArrayWeaklyPinned# :: MutableByteArray# s -> Int#
+         isByteArrayWeaklyPinned# :: ByteArray# s -> Int#
+
 ## 0.12.0
 
 - Shipped with GHC 9.10.1
diff --git a/ghc-prim.cabal b/ghc-prim.cabal
--- a/ghc-prim.cabal
+++ b/ghc-prim.cabal
@@ -1,6 +1,6 @@
 cabal-version:  2.2
 name:           ghc-prim
-version:        0.12.0
+version:        0.13.0
 -- NOTE: Don't forget to update ./changelog.md
 license:        BSD-3-Clause
 license-file:   LICENSE
