{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE Trustworthy #-}
{-# OPTIONS_GHC -fplugin=GHC.TypeLits.Extra.Solver #-}
{-# OPTIONS_GHC -fplugin=GHC.TypeLits.Normalise #-}
{-# OPTIONS_GHC -fplugin=GHC.TypeLits.KnownNat.Solver #-}
{-# OPTIONS_HADDOCK show-extensions #-}
#include "MachDeps.h"
module Clash.Class.BitPack.Internal where
import Prelude hiding (map)
import GHC.Float (castDoubleToWord64, castFloatToWord32,
castWord32ToFloat, castWord64ToDouble)
import Data.Char (chr, ord)
import Data.Complex (Complex)
import Data.Functor.Compose (Compose)
import Data.Functor.Const (Const)
import Data.Functor.Identity (Identity)
import Data.Functor.Product (Product)
import Data.Functor.Sum (Sum)
import Data.Int
import Data.Ord (Down)
import Data.Proxy (Proxy)
import Data.Word
import Foreign.C.Types (CUShort)
import GHC.Generics
import GHC.TypeLits (KnownNat, Nat, type (+), type (-))
import GHC.TypeLits.Extra (CLog, Max)
import Numeric.Half (Half (..))
import Clash.Annotations.Primitive (hasBlackBox)
import Clash.Class.BitPack.Internal.TH (deriveBitPackTuples)
import Clash.Class.Resize (zeroExtend, resize)
import Clash.Promoted.Nat (SNat(..), snatToNum)
import Clash.Sized.Internal.BitVector
(pack#, split#, checkUnpackUndef, undefined#, unpack#, unsafeToNatural, isLike#,
BitVector, Bit, (++#), xToBV)
class KnownNat (BitSize a) => BitPack a where
type BitSize a :: Nat
type BitSize a = (CLog 2 (GConstructorCount (Rep a))) + (GFieldSize (Rep a))
pack :: a -> BitVector (BitSize a)
default pack
:: ( Generic a
, GBitPack (Rep a)
, KnownNat (BitSize a)
, KnownNat constrSize
, KnownNat fieldSize
, constrSize ~ CLog 2 (GConstructorCount (Rep a))
, fieldSize ~ GFieldSize (Rep a)
, (constrSize + fieldSize) ~ BitSize a
)
=> a -> BitVector (BitSize a)
pack = packXWith go
where
go a = resize (pack sc) ++# packedFields
where
(sc, packedFields) = gPackFields 0 (from a)
unpack :: BitVector (BitSize a) -> a
default unpack
:: ( Generic a
, GBitPack (Rep a)
, KnownNat constrSize
, KnownNat fieldSize
, constrSize ~ CLog 2 (GConstructorCount (Rep a))
, fieldSize ~ GFieldSize (Rep a)
, (constrSize + fieldSize) ~ BitSize a
)
=> BitVector (BitSize a) -> a
unpack b =
to (gUnpack sc 0 bFields)
where
(checkUnpackUndef unpack . resize -> sc, bFields) = split# b
maybeUnpack :: BitVector (BitSize a) -> Maybe a
default maybeUnpack
:: ( Generic a
, GBitPack (Rep a)
, KnownNat constrSize
, KnownNat fieldSize
, constrSize ~ CLog 2 (GConstructorCount (Rep a))
, fieldSize ~ GFieldSize (Rep a)
, (constrSize + fieldSize) ~ BitSize a
)
=> BitVector (BitSize a) -> Maybe a
maybeUnpack b = to <$> gMaybeUnpack True sc 0 bFields
where
(checkUnpackUndef unpack . resize -> sc, bFields) = split# b
packXWith
:: KnownNat n
=> (a -> BitVector n)
-> a
-> BitVector n
packXWith f = xToBV . f
{-# INLINE packXWith #-}
isLike
:: (BitPack a)
=> a
-> a
-> Bool
isLike x y =
isLike# (pack x) (pack y)
{-# INLINE[1] bitCoerce #-}
bitCoerce
:: (BitPack a, BitPack b, BitSize a ~ BitSize b)
=> a
-> b
bitCoerce = unpack . pack
bitCoerceMap
:: forall a b . (BitPack a, BitPack b, BitSize a ~ BitSize b)
=> (a -> a)
-> b
-> b
bitCoerceMap f = bitCoerce . f . bitCoerce
instance BitPack Bool where
type BitSize Bool = 1
pack = let go b = if b then 1 else 0 in packXWith go
unpack = checkUnpackUndef $ \bv -> if bv == 1 then True else False
maybeUnpack = Just . unpack
instance KnownNat n => BitPack (BitVector n) where
type BitSize (BitVector n) = n
pack = packXWith id
unpack v = v
maybeUnpack = Just . unpack
instance BitPack Bit where
type BitSize Bit = 1
pack = packXWith pack#
unpack = unpack#
maybeUnpack = Just . unpack
instance BitPack Int where
type BitSize Int = WORD_SIZE_IN_BITS
pack = packXWith toEnum
unpack = checkUnpackUndef fromEnum
maybeUnpack = Just . unpack
instance BitPack Int8 where
type BitSize Int8 = 8
pack = packXWith packInt8#
unpack = checkUnpackUndef unpackInt8#
maybeUnpack = Just . unpack
packInt8# :: Int8 -> BitVector 8
packInt8# = fromIntegral
{-# OPAQUE packInt8# #-}
{-# ANN packInt8# hasBlackBox #-}
unpackInt8# :: BitVector 8 -> Int8
unpackInt8# = fromIntegral
{-# OPAQUE unpackInt8# #-}
{-# ANN unpackInt8# hasBlackBox #-}
instance BitPack Int16 where
type BitSize Int16 = 16
pack = packXWith packInt16#
unpack = checkUnpackUndef unpackInt16#
maybeUnpack = Just . unpack
packInt16# :: Int16 -> BitVector 16
packInt16# = fromIntegral
{-# OPAQUE packInt16# #-}
{-# ANN packInt16# hasBlackBox #-}
unpackInt16# :: BitVector 16 -> Int16
unpackInt16# = fromIntegral
{-# OPAQUE unpackInt16# #-}
{-# ANN unpackInt16# hasBlackBox #-}
instance BitPack Int32 where
type BitSize Int32 = 32
pack = packXWith packInt32#
unpack = checkUnpackUndef unpackInt32#
maybeUnpack = Just . unpack
packInt32# :: Int32 -> BitVector 32
packInt32# = fromIntegral
{-# OPAQUE packInt32# #-}
{-# ANN packInt32# hasBlackBox #-}
unpackInt32# :: BitVector 32 -> Int32
unpackInt32# = fromIntegral
{-# OPAQUE unpackInt32# #-}
{-# ANN unpackInt32# hasBlackBox #-}
instance BitPack Int64 where
type BitSize Int64 = 64
pack = packXWith packInt64#
unpack = checkUnpackUndef unpackInt64#
maybeUnpack = Just . unpack
packInt64# :: Int64 -> BitVector 64
packInt64# = fromIntegral
{-# OPAQUE packInt64# #-}
{-# ANN packInt64# hasBlackBox #-}
unpackInt64# :: BitVector 64 -> Int64
unpackInt64# = fromIntegral
{-# OPAQUE unpackInt64# #-}
{-# ANN unpackInt64# hasBlackBox #-}
instance BitPack Word where
type BitSize Word = WORD_SIZE_IN_BITS
pack = packXWith packWord#
unpack = checkUnpackUndef unpackWord#
maybeUnpack = Just . unpack
packWord# :: Word -> BitVector WORD_SIZE_IN_BITS
packWord# = fromIntegral
{-# OPAQUE packWord# #-}
{-# ANN packWord# hasBlackBox #-}
unpackWord# :: BitVector WORD_SIZE_IN_BITS -> Word
unpackWord# = fromIntegral
{-# OPAQUE unpackWord# #-}
{-# ANN unpackWord# hasBlackBox #-}
instance BitPack Word8 where
type BitSize Word8 = 8
pack = packXWith packWord8#
unpack = checkUnpackUndef unpackWord8#
maybeUnpack = Just . unpack
packWord8# :: Word8 -> BitVector 8
packWord8# = fromIntegral
{-# OPAQUE packWord8# #-}
{-# ANN packWord8# hasBlackBox #-}
unpackWord8# :: BitVector 8 -> Word8
unpackWord8# = fromIntegral
{-# OPAQUE unpackWord8# #-}
{-# ANN unpackWord8# hasBlackBox #-}
instance BitPack Word16 where
type BitSize Word16 = 16
pack = packXWith packWord16#
unpack = checkUnpackUndef unpackWord16#
maybeUnpack = Just . unpack
packWord16# :: Word16 -> BitVector 16
packWord16# = fromIntegral
{-# OPAQUE packWord16# #-}
{-# ANN packWord16# hasBlackBox #-}
unpackWord16# :: BitVector 16 -> Word16
unpackWord16# = fromIntegral
{-# OPAQUE unpackWord16# #-}
{-# ANN unpackWord16# hasBlackBox #-}
instance BitPack Word32 where
type BitSize Word32 = 32
pack = packXWith packWord32#
unpack = checkUnpackUndef unpackWord32#
maybeUnpack = Just . unpack
packWord32# :: Word32 -> BitVector 32
packWord32# = fromIntegral
{-# OPAQUE packWord32# #-}
{-# ANN packWord32# hasBlackBox #-}
unpackWord32# :: BitVector 32 -> Word32
unpackWord32# = fromIntegral
{-# OPAQUE unpackWord32# #-}
{-# ANN unpackWord32# hasBlackBox #-}
instance BitPack Word64 where
type BitSize Word64 = 64
pack = packXWith packWord64#
unpack = checkUnpackUndef unpackWord64#
maybeUnpack = Just . unpack
packWord64# :: Word64 -> BitVector 64
packWord64# = fromIntegral
{-# OPAQUE packWord64# #-}
{-# ANN packWord64# hasBlackBox #-}
unpackWord64# :: BitVector 64 -> Word64
unpackWord64# = fromIntegral
{-# OPAQUE unpackWord64# #-}
{-# ANN unpackWord64# hasBlackBox #-}
instance BitPack Float where
type BitSize Float = 32
pack = packXWith packFloat#
unpack = checkUnpackUndef unpackFloat#
maybeUnpack = Just . unpack
packFloat# :: Float -> BitVector 32
packFloat# = fromIntegral . castFloatToWord32
{-# OPAQUE packFloat# #-}
{-# ANN packFloat# hasBlackBox #-}
unpackFloat# :: BitVector 32 -> Float
unpackFloat# (unsafeToNatural -> w) = castWord32ToFloat (fromIntegral w)
{-# OPAQUE unpackFloat# #-}
{-# ANN unpackFloat# hasBlackBox #-}
instance BitPack Double where
type BitSize Double = 64
pack = packXWith packDouble#
unpack = checkUnpackUndef unpackDouble#
maybeUnpack = Just . unpack
packDouble# :: Double -> BitVector 64
packDouble# = fromIntegral . castDoubleToWord64
{-# OPAQUE packDouble# #-}
{-# ANN packDouble# hasBlackBox #-}
unpackDouble# :: BitVector 64 -> Double
unpackDouble# (unsafeToNatural -> w) = castWord64ToDouble (fromIntegral w)
{-# OPAQUE unpackDouble# #-}
{-# ANN unpackDouble# hasBlackBox #-}
instance BitPack CUShort where
type BitSize CUShort = 16
pack = packXWith packCUShort#
unpack = checkUnpackUndef unpackCUShort#
maybeUnpack = Just . unpack
packCUShort# :: CUShort -> BitVector 16
packCUShort# = fromIntegral
{-# OPAQUE packCUShort# #-}
{-# ANN packCUShort# hasBlackBox #-}
unpackCUShort# :: BitVector 16 -> CUShort
unpackCUShort# = fromIntegral
{-# OPAQUE unpackCUShort# #-}
{-# ANN unpackCUShort# hasBlackBox #-}
instance BitPack Half where
type BitSize Half = 16
pack (Half x) = pack x
unpack = checkUnpackUndef $ \x -> Half (unpack x)
maybeUnpack = Just . unpack
instance BitPack () where
type BitSize () = 0
pack _ = minBound
unpack _ = ()
maybeUnpack = Just . unpack
instance BitPack Char where
type BitSize Char = 21
pack = packXWith packChar#
unpack = checkUnpackUndef unpackChar#
maybeUnpack bv
| bv <= maxCharBV = Just (unpack bv)
| otherwise = Nothing
where
maxCharBV = packChar# maxBound
packChar# :: Char -> BitVector 21
packChar# = fromIntegral . ord
{-# OPAQUE packChar# #-}
{-# ANN packChar# hasBlackBox #-}
unpackChar# :: BitVector 21 -> Char
unpackChar# = chr . fromIntegral
{-# OPAQUE unpackChar# #-}
{-# ANN unpackChar# hasBlackBox #-}
instance (BitPack a, BitPack b) => BitPack (a,b) where
type BitSize (a,b) = BitSize a + BitSize b
pack = let go (a,b) = pack a ++# pack b in packXWith go
unpack ab = let (a,b) = split# ab in (unpack a, unpack b)
maybeUnpack ab = let (a,b) = split# ab in (,) <$> maybeUnpack a <*> maybeUnpack b
class GBitPack f where
type GFieldSize f :: Nat
type GConstructorCount f :: Nat
gPackFields
:: Int
-> f a
-> (Int, BitVector (GFieldSize f))
gUnpack
:: Int
-> Int
-> BitVector (GFieldSize f)
-> f a
gMaybeUnpack
:: Bool
-> Int
-> Int
-> BitVector (GFieldSize f)
-> Maybe (f a)
instance GBitPack a => GBitPack (M1 m d a) where
type GFieldSize (M1 m d a) = GFieldSize a
type GConstructorCount (M1 m d a) = GConstructorCount a
gPackFields cc (M1 m1) = gPackFields cc m1
gUnpack c cc b = M1 (gUnpack c cc b)
gMaybeUnpack checkBounds c cc b =
M1 <$> gMaybeUnpack checkBounds c cc b
instance ( KnownNat (GFieldSize g)
, KnownNat (GFieldSize f)
, KnownNat (GConstructorCount f)
, KnownNat (GConstructorCount g)
, GBitPack f
, GBitPack g
) => GBitPack (f :+: g) where
type GFieldSize (f :+: g) = Max (GFieldSize f) (GFieldSize g)
type GConstructorCount (f :+: g) = GConstructorCount f + GConstructorCount g
gPackFields cc (L1 l) =
let (sc, packed) = gPackFields cc l in
let padding = undefined# :: BitVector (Max (GFieldSize f) (GFieldSize g) - GFieldSize f) in
(sc, packed ++# padding)
gPackFields cc (R1 r) =
let cLeft = snatToNum (SNat @(GConstructorCount f)) in
let (sc, packed) = gPackFields (cc + cLeft) r in
let padding = undefined# :: BitVector (Max (GFieldSize f) (GFieldSize g) - GFieldSize g) in
(sc, packed ++# padding)
gUnpack c cc b =
let cLeft = snatToNum (SNat @(GConstructorCount f)) in
if c < cc + cLeft then
L1 (gUnpack c cc f)
else
R1 (gUnpack c (cc + cLeft) g)
where
(f, _ :: BitVector (Max (GFieldSize f) (GFieldSize g) - GFieldSize f)) = split# b
(g, _ :: BitVector (Max (GFieldSize f) (GFieldSize g) - GFieldSize g)) = split# b
gMaybeUnpack checkBounds c cc b =
let
cLeft = snatToNum (SNat @(GConstructorCount f))
cTotal = snatToNum (SNat @(GConstructorCount (f :+: g)))
in
if not checkBounds || c < cc + cTotal
then if c < cc + cLeft
then L1 <$> gMaybeUnpack False c cc f
else R1 <$> gMaybeUnpack False c (cc + cLeft) g
else Nothing
where
(f, _ :: BitVector (Max (GFieldSize f) (GFieldSize g) - GFieldSize f)) = split# b
(g, _ :: BitVector (Max (GFieldSize f) (GFieldSize g) - GFieldSize g)) = split# b
instance (KnownNat (GFieldSize g), KnownNat (GFieldSize f), GBitPack f, GBitPack g) => GBitPack (f :*: g) where
type GFieldSize (f :*: g) = GFieldSize f + GFieldSize g
type GConstructorCount (f :*: g) = 1
gPackFields cc fg =
(cc, packXWith go fg)
where
go (l0 :*: r0) =
let (_, l1) = gPackFields cc l0 in
let (_, r1) = gPackFields cc r0 in
l1 ++# r1
gUnpack c cc b =
gUnpack c cc front :*: gUnpack c cc back
where
(front, back) = split# b
gMaybeUnpack checkBounds c cc b =
(:*:)
<$> gMaybeUnpack checkBounds c cc front
<*> gMaybeUnpack checkBounds c cc back
where
(front, back) = split# b
instance BitPack c => GBitPack (K1 i c) where
type GFieldSize (K1 i c) = BitSize c
type GConstructorCount (K1 i c) = 1
gPackFields cc (K1 i) = (cc, pack i)
gUnpack _c _cc b = K1 (unpack b)
gMaybeUnpack _checkBounds _c _cc b = K1 <$> maybeUnpack b
instance GBitPack U1 where
type GFieldSize U1 = 0
type GConstructorCount U1 = 1
gPackFields cc U1 = (cc, 0)
gUnpack _c _cc _b = U1
gMaybeUnpack _checkBounds _c _cc _b = Just U1
instance BitPack Ordering
instance ( BitPack a
, BitPack b
) => BitPack (Either a b)
instance BitPack a => BitPack (Maybe a)
instance BitPack (Proxy a)
instance BitPack a => BitPack (Complex a)
instance BitPack a => BitPack (Down a)
instance BitPack a => BitPack (Identity a)
instance BitPack a => BitPack (Const a b)
instance (BitPack (f a), BitPack (g a)) => BitPack (Product f g a)
instance (BitPack (f a), BitPack (g a)) => BitPack (Sum f g a)
instance BitPack (f (g a)) => BitPack (Compose f g a)
boolToBV :: KnownNat n => Bool -> BitVector (n + 1)
boolToBV = zeroExtend . pack
boolToBit :: Bool -> Bit
boolToBit = bitCoerce
bitToBool :: Bit -> Bool
bitToBool = bitCoerce
deriveBitPackTuples ''BitPack ''BitSize 'pack 'unpack 'maybeUnpack