{-# LANGUAGE CPP #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UnliftedFFITypes #-}
{-# LANGUAGE NegativeLiterals #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE BinaryLiterals #-}
{-# OPTIONS_GHC -fexpose-all-unfoldings #-}

module GHC.Num.Primitives
   (
   -- * Bool#
   Bool#
   , (&&#)
   , (||#)
   , notB#
   -- * Int#
   , testBitI#
   , minI#
   , maxI#
   , sgnI#
   , absI#
   , cmpI#
   , intEncodeDouble#
   , popCntI#
   -- * Word#
   , andNot#
   , cmpW#
   , bitW#
   , maxW#
   , minW#
   , testBitW#
   , shiftRW#
   , plusWord3#
   , plusWord12#
   , quotRemWord3#
   , wordFromAbsInt#
   , wordLog2#
   , wordLogBase#
   , wordSizeInBase#
   , wordIsPowerOf2#
   , wordEncodeDouble#
   , wordReverseBits#
   , wordReverseBits32#
   , wordReverseBytes#
   -- ** Addr import/export
   , wordFromAddr#
   , wordFromAddrLE#
   , wordFromAddrBE#
   , wordToAddr#
   , wordToAddrLE#
   , wordToAddrBE#
   , wordWriteAddrLE#
   , wordWriteAddrBE#
   -- ** ByteArray import/export
   , wordFromByteArray#
   , wordFromByteArrayLE#
   , wordFromByteArrayBE#
   , wordToMutableByteArray#
   , wordToMutableByteArrayLE#
   , wordToMutableByteArrayBE#
   , wordWriteMutableByteArrayLE#
   , wordWriteMutableByteArrayBE#
   -- * Exception
   , raiseUnderflow
   , raiseUnderflow_Word#
   , raiseDivZero
   , raiseDivZero_Word#
   , unexpectedValue
   , unexpectedValue_Int#
   , unexpectedValue_Word#
   -- * IO
   , ioWord#
   , ioInt#
   , ioVoid
   , ioBool
   )
where

#include "MachDeps.h"
#include "WordSize.h"

-- Required for WORDS_BIGENDIAN
#include <ghcautoconf.h>

#if (__GLASGOW_HASKELL__ < 811)
import GHC.Magic
#else
import GHC.Prim.Exception
#endif

import GHC.Prim
import GHC.Types
import GHC.Tuple () -- See Note [Depend on GHC.Tuple] in GHC.Base

default ()

----------------------------------
-- Bool#
----------------------------------

type Bool# = Int#

(&&#) :: Bool# -> Bool# -> Bool#
(&&#) = andI#

(||#) :: Bool# -> Bool# -> Bool#
(||#) = orI#

notB# :: Bool# -> Bool#
notB# x = x `xorI#` 1#

infixr 3  &&#
infixr 2  ||#


----------------------------------
-- Int#
----------------------------------

-- | Branchless `abs`
absI# :: Int# -> Int#
absI# i# = (i# `xorI#` nsign) -# nsign
  where
    -- nsign = negateInt# (i# <# 0#)
    nsign = uncheckedIShiftRA# i# (WORD_SIZE_IN_BITS# -# 1#)

-- | Branchless `signum`
sgnI# :: Int# -> Int#
sgnI# x# = (x# ># 0#) -# (x# <# 0#)

-- | Population count
popCntI# :: Int# -> Word#
popCntI# i = popCnt# (int2Word# i)

-- | Branchless comparison
cmpI# :: Int# -> Int# -> Int#
cmpI# x# y# = (x# ># y#) -# (x# <# y#)

testBitI# :: Int# -> Word# -> Bool#
testBitI# x i = ((uncheckedIShiftL# 1# (word2Int# i)) `andI#` x) /=# 0#

minI# :: Int# -> Int# -> Int#
minI# x y | isTrue# (x <=# y) = x
          | True              = y

maxI# :: Int# -> Int# -> Int#
maxI# x y | isTrue# (x >=# y) = x
          | True              = y

-- | Encode (# Int# mantissa, Int# exponent #) into a Double#.
--
-- (provided by GHC's RTS)
foreign import ccall unsafe "__int_encodeDouble"
   intEncodeDouble# :: Int# -> Int# -> Double#

----------------------------------
-- Word#
----------------------------------

andNot# :: Word# -> Word# -> Word#
andNot# x y = x `and#` (not# y)

cmpW# :: Word# -> Word# -> Ordering
{-# INLINE cmpW# #-}
cmpW# x# y#
  | isTrue# (x# `ltWord#` y#) = LT
  | isTrue# (x# `eqWord#` y#) = EQ
  | True                      = GT

-- | Return the absolute value of the Int# in a Word#
wordFromAbsInt# :: Int# -> Word#
wordFromAbsInt# i
   | isTrue# (i >=# 0#) = int2Word# i
   | True               = int2Word# (negateInt# i)

minW# :: Word# -> Word# -> Word#
minW# x# y# | isTrue# (x# `leWord#` y#) = x#
            | True                      = y#

maxW# :: Word# -> Word# -> Word#
maxW# x# y# | isTrue# (x# `gtWord#` y#) = x#
            | True                      = y#

bitW# :: Int# -> Word#
bitW# k = 1## `uncheckedShiftL#` k

testBitW# :: Word# -> Word# -> Bool#
testBitW# w k = w `and#` (1## `uncheckedShiftL#` word2Int# k) `neWord#` 0##

-- | Safe right shift for Word#
shiftRW# :: Word# -> Word# -> Word#
shiftRW# a b
   | isTrue# (b `geWord#` WORD_SIZE_IN_BITS##) = 0##
   | True                                      = a `uncheckedShiftRL#` (word2Int# b)

-- | (h,l) <- a + (hb,lb)
plusWord12# :: Word# -> (# Word#,Word# #) -> (# Word#,Word# #)
{-# INLINABLE plusWord12# #-}
plusWord12# a0 (# b1,b0 #) = (# m1, m0 #)
   where
      !(# t, m0 #) = plusWord2# a0 b0
      !m1          = plusWord# t b1

-- | Add 3 values together
plusWord3# :: Word# -> Word# -> Word# -> (# Word#, Word# #)
{-# INLINABLE plusWord3# #-}
plusWord3# a b c = (# r1, r0 #)
   where
      !(# t1, t0 #) = plusWord2# a b
      !(# t2, r0 #) = plusWord2# t0 c
      !r1           = plusWord# t1 t2


-- | 2-by-1 large division
--
-- Requires:
--    b0 /= 0
--    a1 >= b0 (not required, but if not q1=0)
quotRemWord3# :: (# Word#,Word# #) -> Word# -> (# (# Word#,Word# #),Word# #)
quotRemWord3# (# a1,a0 #) b0 = (# (# q1, q0 #), r0 #)
   where
      !(# q1, r' #) = quotRemWord# a1 b0
      !(# q0, r0 #) = quotRemWord2# r' a0 b0



-- | Encode (# Word# mantissa, Int# exponent #) into a Double#.
--
-- (provided by GHC's RTS)
foreign import ccall unsafe "__word_encodeDouble"
   wordEncodeDouble# :: Word# -> Int# -> Double#

-- | Compute base-2 log of 'Word#'
--
-- This is internally implemented as count-leading-zeros machine instruction.
wordLog2# :: Word# -> Word#
wordLog2# w   = (WORD_SIZE_IN_BITS## `minusWord#` 1##) `minusWord#` (clz# w)

-- | Logarithm for an arbitrary base
wordLogBase# :: Word# -> Word# -> Word#
wordLogBase# base a
   | isTrue# (base `leWord#` 1##)
   = unexpectedValue_Word# (# #)

   | 2## <- base
   = wordLog2# a

   | True
   = case go base of (# _, e' #) -> e'
   where
      goSqr pw = case timesWord2# pw pw of
         (# 0##, l #) -> go l
         (# _  , _ #) -> (# a, 0## #)
      go pw = if isTrue# (a `ltWord#` pw)
         then (# a, 0## #)
         else case goSqr pw of
            (# q, e #) -> if isTrue# (q `ltWord#` pw)
               then (# q, 2## `timesWord#` e #)
               else (# q `quotWord#` pw
                    , 2## `timesWord#` e `plusWord#` 1## #)

wordSizeInBase# :: Word# -> Word# -> Word#
wordSizeInBase# _    0## = 0##
wordSizeInBase# base w   = 1## `plusWord#` wordLogBase# base w

-- | Indicate if the value is a power of two and which one
wordIsPowerOf2# :: Word# -> (# () | Word# #)
wordIsPowerOf2# w
   | isTrue# (popCnt# w `neWord#` 1##) = (# () | #)
   | True                              = (# | ctz# w #)

-- | Reverse bytes in a Word#
wordReverseBytes# :: Word# -> Word#
wordReverseBytes# x0 = r
   where
#if WORD_SIZE_IN_BITS == 64
      x1 = ((x0 `and#` 0x00FF00FF00FF00FF##) `uncheckedShiftL#`  8#) `or#` ((x0 `and#` 0xFF00FF00FF00FF00##) `uncheckedShiftRL#`  8#)
      x2 = ((x1 `and#` 0x0000FFFF0000FFFF##) `uncheckedShiftL#` 16#) `or#` ((x1 `and#` 0xFFFF0000FFFF0000##) `uncheckedShiftRL#` 16#)
      r  = ((x2 `and#` 0x00000000FFFFFFFF##) `uncheckedShiftL#` 32#) `or#` ((x2 `and#` 0xFFFFFFFF00000000##) `uncheckedShiftRL#` 32#)
#else
      x1 = ((x0 `and#` 0x00FF00FF##) `uncheckedShiftL#`  8#) `or#` ((x0 `and#` 0xFF00FF00##) `uncheckedShiftRL#`  8#)
      r  = ((x1 `and#` 0x0000FFFF##) `uncheckedShiftL#` 16#) `or#` ((x1 `and#` 0xFFFF0000##) `uncheckedShiftRL#` 16#)
#endif


-- | Reverse bits in a Word#
wordReverseBits# :: Word# -> Word#
wordReverseBits# x0 = r
   where
#if WORD_SIZE_IN_BITS == 64
      x1 = ((x0 `and#` 0x5555555555555555##) `uncheckedShiftL#`  1#) `or#` ((x0 `and#` 0xAAAAAAAAAAAAAAAA##) `uncheckedShiftRL#`  1#)
      x2 = ((x1 `and#` 0x3333333333333333##) `uncheckedShiftL#`  2#) `or#` ((x1 `and#` 0xCCCCCCCCCCCCCCCC##) `uncheckedShiftRL#`  2#)
      x3 = ((x2 `and#` 0x0F0F0F0F0F0F0F0F##) `uncheckedShiftL#`  4#) `or#` ((x2 `and#` 0xF0F0F0F0F0F0F0F0##) `uncheckedShiftRL#`  4#)
      x4 = ((x3 `and#` 0x00FF00FF00FF00FF##) `uncheckedShiftL#`  8#) `or#` ((x3 `and#` 0xFF00FF00FF00FF00##) `uncheckedShiftRL#`  8#)
      x5 = ((x4 `and#` 0x0000FFFF0000FFFF##) `uncheckedShiftL#` 16#) `or#` ((x4 `and#` 0xFFFF0000FFFF0000##) `uncheckedShiftRL#` 16#)
      r  = ((x5 `and#` 0x00000000FFFFFFFF##) `uncheckedShiftL#` 32#) `or#` ((x5 `and#` 0xFFFFFFFF00000000##) `uncheckedShiftRL#` 32#)
#else
      x1 = ((x0 `and#` 0x55555555##) `uncheckedShiftL#`  1#) `or#` ((x0 `and#` 0xAAAAAAAA##) `uncheckedShiftRL#`  1#)
      x2 = ((x1 `and#` 0x33333333##) `uncheckedShiftL#`  2#) `or#` ((x1 `and#` 0xCCCCCCCC##) `uncheckedShiftRL#`  2#)
      x3 = ((x2 `and#` 0x0F0F0F0F##) `uncheckedShiftL#`  4#) `or#` ((x2 `and#` 0xF0F0F0F0##) `uncheckedShiftRL#`  4#)
      x4 = ((x3 `and#` 0x00FF00FF##) `uncheckedShiftL#`  8#) `or#` ((x3 `and#` 0xFF00FF00##) `uncheckedShiftRL#`  8#)
      r  = ((x4 `and#` 0x0000FFFF##) `uncheckedShiftL#` 16#) `or#` ((x4 `and#` 0xFFFF0000##) `uncheckedShiftRL#` 16#)
#endif

-- | Reverse bits in the Word32 subwords composing a Word#
wordReverseBits32# :: Word# -> Word#
#if WORD_SIZE_IN_BITS == 64
wordReverseBits32# x0 = r
   where
      x1 = ((x0 `and#` 0x5555555555555555##) `uncheckedShiftL#`  1#) `or#` ((x0 `and#` 0xAAAAAAAAAAAAAAAA##) `uncheckedShiftRL#`  1#)
      x2 = ((x1 `and#` 0x3333333333333333##) `uncheckedShiftL#`  2#) `or#` ((x1 `and#` 0xCCCCCCCCCCCCCCCC##) `uncheckedShiftRL#`  2#)
      x3 = ((x2 `and#` 0x0F0F0F0F0F0F0F0F##) `uncheckedShiftL#`  4#) `or#` ((x2 `and#` 0xF0F0F0F0F0F0F0F0##) `uncheckedShiftRL#`  4#)
      x4 = ((x3 `and#` 0x00FF00FF00FF00FF##) `uncheckedShiftL#`  8#) `or#` ((x3 `and#` 0xFF00FF00FF00FF00##) `uncheckedShiftRL#`  8#)
      r  = ((x4 `and#` 0x0000FFFF0000FFFF##) `uncheckedShiftL#` 16#) `or#` ((x4 `and#` 0xFFFF0000FFFF0000##) `uncheckedShiftRL#` 16#)
#else
wordReverseBits32# x0 = wordReverseBits# x0
#endif


-- | Write a Word to @/addr/@ in base-256 little-endian representation and
-- return the number of bytes written.
wordToAddrLE# :: Word# -> Addr# -> State# s -> (# State# s, Word# #)
wordToAddrLE# x addr = go x 0#
   where
      go w c s
         | 0## <- w
         = (# s, int2Word# c #)

         | True
         = case writeWord8OffAddr# addr c (w `and#` 0xFF##) s of
            s' -> go (w `uncheckedShiftRL#` 8#) (c +# 1#) s'

-- | Write a Word to @/addr/@ in base-256 big-endian representation and
-- return the number of bytes written.
wordToAddrBE# :: Word# -> Addr# -> State# s -> (# State# s, Word# #)
wordToAddrBE# w addr = go 0# (WORD_SIZE_IN_BITS# -# clz)
   where
     !clz = word2Int# (clz# w `and#` (not# 0b0111##)) -- keep complete bytes

     go c sh s
      | 0# <- sh
      = (# s, int2Word# c #)

      | True
      , w' <- (w `uncheckedShiftRL#` (sh -# 8#)) `and#` 0xFF##
      = case writeWord8OffAddr# addr c w' s of
         s' -> go (c +# 1#) (sh -# 8#) s'

-- | Write a Word to @/addr/@ in base-256 representation and
-- return the number of bytes written.
--
-- The endianness is selected with the Bool# parameter: write most significant
-- byte first (big-endian) if @1#@ or least significant byte first
-- (little-endian) if @0#@.
wordToAddr# :: Word# -> Addr# -> Bool# -> State# s -> (# State# s, Word# #)
wordToAddr# a addr 0# s = wordToAddrLE# a addr s
wordToAddr# a addr _  s = wordToAddrBE# a addr s


-- | Read a Word from @/addr/@ in base-256 little-endian representation.
--
-- @'n' is the number of bytes to read.
wordFromAddrLE# :: Word# -> Addr# -> State# s -> (# State# s, Word# #)
wordFromAddrLE# n addr s
   -- Optimize when we read a full word
   | WORD_SIZE_IN_BYTES## <- n
   = case readWordOffAddr# addr 0# s of
#if defined(WORDS_BIGENDIAN)
      (# s', w #) -> (# s', wordReverseBytes# w #)
#else
      (# s', w #) -> (# s', w #)
#endif

wordFromAddrLE# n addr s0 = go 0## 0# s0
   where
      go w c s
         | isTrue# (c ==# word2Int# n)
         = (# s, w #)

         | True
         = case readWord8OffAddr# addr c s of
            (# s', b #) -> go (w `or#` (b `uncheckedShiftL#` (c `uncheckedIShiftL#` 3#)))
                              (c +# 1#)
                              s'

-- | Read a Word from @/addr/@ in base-256 big-endian representation.
--
-- @'n' is the number of bytes to read.
wordFromAddrBE# :: Word# -> Addr# -> State# s -> (# State# s, Word# #)
wordFromAddrBE# n addr s
   -- Optimize when we read a full word
   | WORD_SIZE_IN_BYTES## <- n
   = case readWordOffAddr# addr 0# s of
#if defined(WORDS_BIGENDIAN)
      (# s', w #) -> (# s', w #)
#else
      (# s', w #) -> (# s', wordReverseBytes# w #)
#endif

wordFromAddrBE# n addr s0 = go 0## 0# s0
   where
      go w c s
         | isTrue# (c ==# word2Int# n)
         = (# s, w #)

         | True
         = case readWord8OffAddr# addr c s of
            (# s', b #) -> go ((w `uncheckedShiftL#` 8#) `or#` b)
                              (c +# 1#)
                              s'

-- | Read a Word from @/addr/@ in base-256 representation.
--
-- @'n' is the number of bytes to read.
--
-- The endianness is selected with the Bool# parameter: write most significant
-- byte first (big-endian) if @1#@ or least significant byte first
-- (little-endian) if @0#@.
wordFromAddr# :: Word# -> Addr# -> Bool# -> State# s -> (# State# s, Word# #)
wordFromAddr# a addr 0# s = wordFromAddrLE# a addr s
wordFromAddr# a addr _  s = wordFromAddrBE# a addr s



-- | Write a full word with little-endian encoding
wordWriteAddrLE# :: Word# -> Addr# -> State# s -> State# s
wordWriteAddrLE# w addr = writeWordOffAddr# addr 0#
#if defined(WORDS_BIGENDIAN)
   (wordReverseBytes# w)
#else
   w
#endif

-- | Write a full word with little-endian encoding
wordWriteAddrBE# :: Word# -> Addr# -> State# s -> State# s
wordWriteAddrBE# w addr = writeWordOffAddr# addr 0#
#if defined(WORDS_BIGENDIAN)
   w
#else
   (wordReverseBytes# w)
#endif

-- | Write a Word to @/MutableByteArray/@ in base-256 little-endian
-- representation and return the number of bytes written.
--
-- The offset is in bytes.
wordToMutableByteArrayLE# :: Word# -> MutableByteArray# s -> Word# -> State# s -> (# State# s, Word# #)
wordToMutableByteArrayLE# x mba off = go x 0#
   where
      go w c s
         | 0## <- w
         = (# s, int2Word# c #)

         | True
         = case writeWord8Array# mba (word2Int# off +# c) (w `and#` 0xFF##) s of
            s' -> go (w `uncheckedShiftRL#` 8#) (c +# 1#) s'

-- | Write a Word to @/MutableByteArray/@ in base-256 big-endian representation and
-- return the number of bytes written.
--
-- The offset is in bytes.
wordToMutableByteArrayBE# :: Word# -> MutableByteArray# s -> Word# -> State# s -> (# State# s, Word# #)
wordToMutableByteArrayBE# w mba off = go 0# (WORD_SIZE_IN_BITS# -# clz)
   where
     !clz = word2Int# (clz# w `and#` (not# 0b0111##)) -- keep complete bytes

     go c sh s
      | 0# <- sh
      = (# s, int2Word# c #)

      | True
      , w' <- (w `uncheckedShiftRL#` (sh -# 8#)) `and#` 0xFF##
      = case writeWord8Array# mba (word2Int# off +# c) w' s of
         s' -> go (c +# 1#) (sh -# 8#) s'

-- | Write a Word to @/MutableByteArray/@ in base-256 representation and
-- return the number of bytes written.
--
-- The endianness is selected with the Bool# parameter: write most significant
-- byte first (big-endian) if @1#@ or least significant byte first
-- (little-endian) if @0#@.
--
-- The offset is in bytes.
wordToMutableByteArray# :: Word# -> MutableByteArray# s -> Word# -> Bool# -> State# s -> (# State# s, Word# #)
wordToMutableByteArray# a mba off 0# s = wordToMutableByteArrayLE# a mba off s
wordToMutableByteArray# a mba off _  s = wordToMutableByteArrayBE# a mba off s

-- | Write a full word with little-endian encoding
wordWriteMutableByteArrayLE# :: Word# -> MutableByteArray# s -> Word# -> State# s -> State# s
wordWriteMutableByteArrayLE# w mba off = writeWord8ArrayAsWord# mba (word2Int# off)
#if defined(WORDS_BIGENDIAN)
   (wordReverseBytes# w)
#else
   w
#endif

-- | Write a full word with little-endian encoding
wordWriteMutableByteArrayBE# :: Word# -> MutableByteArray# s -> Word# -> State# s -> State# s
wordWriteMutableByteArrayBE# w mba off = writeWord8ArrayAsWord# mba (word2Int# off)
#if defined(WORDS_BIGENDIAN)
   w
#else
   (wordReverseBytes# w)
#endif

-- | Read a Word from @/ByteArray/@ in base-256 little-endian representation.
--
-- @'n' is the number of bytes to read.
wordFromByteArrayLE# :: Word# -> ByteArray# -> Word# -> Word#
wordFromByteArrayLE# n ba off =
   case n of
      -- Optimize when we read a full word
      WORD_SIZE_IN_BYTES## -> case indexWord8ArrayAsWord# ba (word2Int# off) of
#if defined(WORDS_BIGENDIAN)
         w -> wordReverseBytes# w
#else
         w -> w
#endif

      _ -> let
            go w c
               | isTrue# (c ==# word2Int# n)
               = w

               | True
               = case indexWord8Array# ba (word2Int# off +# c) of
                  b -> go (w `or#` (b `uncheckedShiftL#` (c `uncheckedIShiftL#` 3#)))
                          (c +# 1#)
           in go 0## 0#

-- | Read a Word from @/ByteArray/@ in base-256 big-endian representation.
--
-- @'n' is the number of bytes to read.
wordFromByteArrayBE# :: Word# -> ByteArray# -> Word# -> Word#
wordFromByteArrayBE# n ba off
   -- Optimize when we read a full word
   | WORD_SIZE_IN_BYTES## <- n
   = case indexWord8ArrayAsWord# ba (word2Int# off) of
#if defined(WORDS_BIGENDIAN)
      w -> w
#else
      w -> wordReverseBytes# w
#endif

wordFromByteArrayBE# n ba off = go 0## 0#
   where
      go w c
         | isTrue# (c ==# word2Int# n)
         = w

         | True
         = case indexWord8Array# ba (word2Int# off +# c) of
            b -> go ((w `uncheckedShiftL#` 8#) `or#` b) (c +# 1#)

-- | Read a Word from @/ByteArray/@ in base-256 representation.
--
-- @'n' is the number of bytes to read.
--
-- The endianness is selected with the Bool# parameter: write most significant
-- byte first (big-endian) if @1#@ or least significant byte first
-- (little-endian) if @0#@.
wordFromByteArray# :: Word# -> ByteArray# -> Word# -> Bool# -> Word#
wordFromByteArray# a ba off 0# = wordFromByteArrayLE# a ba off
wordFromByteArray# a ba off _  = wordFromByteArrayBE# a ba off

----------------------------------
-- IO
----------------------------------

ioVoid :: IO a -> State# RealWorld -> State# RealWorld
ioVoid (IO io) s = case io s of
                  (# s', _ #) -> s'

ioWord# :: IO Word -> State# RealWorld -> (# State# RealWorld, Word# #)
ioWord# (IO io) s = case io s of
   (# s', W# w #) -> (# s', w #)

ioInt# :: IO Int -> State# RealWorld -> (# State# RealWorld, Int# #)
ioInt# (IO io) s = case io s of
   (# s', I# i #) -> (# s', i #)

ioBool :: IO Bool -> State# RealWorld -> (# State# RealWorld, Bool# #)
ioBool (IO io) s = case io s of
   (# s', False #) -> (# s', 0# #)
   (# s', True #) -> (# s', 1# #)


----------------------------------
-- Exception
----------------------------------

-- 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
-- themselves call wired-in functions from the RTS: raiseOverflow,
-- raiseUnderflow, raiseDivZero.
--
-- We have to be careful when we want to throw an exception instead of returning
-- an unlifted value (e.g. Word#, unboxed tuple, etc.). We have to ensure the
-- evaluation of the exception throwing function before returning a dummy value,
-- otherwise it will be removed by the simplifier as dead-code.
--
--    foo :: ... -> Word#
--    foo = ... case raiseDivZero of
--                !_ -> 0## -- the bang-pattern is necessary!
--                          -- 0## is a dummy value (unreachable code)
--

unexpectedValue_Int# :: (# #) -> Int#
unexpectedValue_Int# _ = case unexpectedValue of
   !_ -> 0# -- see Note [ghc-bignum exceptions]

unexpectedValue_Word# :: (# #) -> Word#
unexpectedValue_Word# _ = case unexpectedValue of
   !_ -> 0## -- see Note [ghc-bignum exceptions]

raiseDivZero_Word# :: (# #) -> Word#
raiseDivZero_Word# _ = case raiseDivZero of
   !_ -> 0## -- see Note [ghc-bignum exceptions]

raiseUnderflow_Word# :: (# #) -> Word#
raiseUnderflow_Word# _ = case raiseUnderflow of
   !_ -> 0## -- see Note [ghc-bignum exceptions]

#if (__GLASGOW_HASKELL__ >= 811)

unexpectedValue :: a
unexpectedValue = raiseOverflow

#else

-- Before GHC 8.11 we use the exception trick taken from #14664
exception :: a
{-# NOINLINE exception #-}
exception = runRW# \s ->
   case atomicLoop s of
      (# _, a #) -> a
   where
      atomicLoop s = atomically# atomicLoop s

raiseUnderflow :: a
raiseUnderflow = exception

raiseDivZero :: a
raiseDivZero = exception

unexpectedValue :: a
unexpectedValue = exception

#endif