{-# 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 Data.Binary.IEEE754 (doubleToWord, floatToWord, wordToDouble,
wordToFloat)
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
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
instance KnownNat n => BitPack (BitVector n) where
type BitSize (BitVector n) = n
pack = packXWith id
unpack v = v
instance BitPack Bit where
type BitSize Bit = 1
pack = packXWith pack#
unpack = unpack#
instance BitPack Int where
type BitSize Int = WORD_SIZE_IN_BITS
pack = packXWith toEnum
unpack = checkUnpackUndef fromEnum
instance BitPack Int8 where
type BitSize Int8 = 8
pack = packXWith packInt8#
unpack = checkUnpackUndef unpackInt8#
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#
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#
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#
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#
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#
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#
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#
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#
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#
packFloat# :: Float -> BitVector 32
packFloat# = fromIntegral . floatToWord
{-# OPAQUE packFloat# #-}
{-# ANN packFloat# hasBlackBox #-}
unpackFloat# :: BitVector 32 -> Float
unpackFloat# (unsafeToNatural -> w) = wordToFloat (fromIntegral w)
{-# OPAQUE unpackFloat# #-}
{-# ANN unpackFloat# hasBlackBox #-}
instance BitPack Double where
type BitSize Double = 64
pack = packXWith packDouble#
unpack = checkUnpackUndef unpackDouble#
packDouble# :: Double -> BitVector 64
packDouble# = fromIntegral . doubleToWord
{-# OPAQUE packDouble# #-}
{-# ANN packDouble# hasBlackBox #-}
unpackDouble# :: BitVector 64 -> Double
unpackDouble# (unsafeToNatural -> w) = wordToDouble (fromIntegral w)
{-# OPAQUE unpackDouble# #-}
{-# ANN unpackDouble# hasBlackBox #-}
instance BitPack CUShort where
type BitSize CUShort = 16
pack = packXWith packCUShort#
unpack = checkUnpackUndef unpackCUShort#
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)
instance BitPack () where
type BitSize () = 0
pack _ = minBound
unpack _ = ()
instance BitPack Char where
type BitSize Char = 21
pack = packXWith packChar#
unpack = checkUnpackUndef unpackChar#
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)
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
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)
instance ( KnownNat (GFieldSize g)
, KnownNat (GFieldSize f)
, KnownNat (GConstructorCount f)
, 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
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
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)
instance GBitPack U1 where
type GFieldSize U1 = 0
type GConstructorCount U1 = 1
gPackFields cc U1 = (cc, 0)
gUnpack _c _cc _b = 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