{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DoAndIfThenElse #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE PartialTypeSignatures #-}
module What4.SWord
( SWord(..)
, bvAsSignedInteger
, bvAsUnsignedInteger
, integerToBV
, bvToInteger
, sbvToInteger
, freshBV
, bvWidth
, bvLit
, bvFill
, bvAtBE
, bvAtLE
, bvSetBE
, bvSetLE
, bvSliceBE
, bvSliceLE
, bvJoin
, bvIte
, bvPackBE
, bvPackLE
, bvUnpackBE
, bvUnpackLE
, bvForall
, unsignedBVBounds
, signedBVBounds
, bvNot
, bvAnd
, bvOr
, bvXor
, bvNeg
, bvAdd
, bvSub
, bvMul
, bvUDiv
, bvURem
, bvSDiv
, bvSRem
, bvEq
, bvsle
, bvslt
, bvule
, bvult
, bvsge
, bvsgt
, bvuge
, bvugt
, bvIsNonzero
, bvPopcount
, bvCountLeadingZeros
, bvCountTrailingZeros
, bvLg2
, bvShl
, bvLshr
, bvAshr
, bvRol
, bvRor
) where
import Data.Vector (Vector)
import qualified Data.Vector as V
import Numeric.Natural
import GHC.TypeNats
import qualified Data.BitVector.Sized as BV
import Data.Parameterized.NatRepr
import Data.Parameterized.Some(Some(..))
import What4.Interface(SymBV,Pred,SymInteger,IsExpr,SymExpr,IsExprBuilder,IsSymExprBuilder)
import qualified What4.Interface as W
import What4.Panic (panic)
data SWord sym where
DBV :: (IsExpr (SymExpr sym), 1<=w) => SymBV sym w -> SWord sym
ZBV :: SWord sym
instance Show (SWord sym) where
show (DBV bv) = show $ W.printSymExpr bv
show ZBV = "0:[0]"
bvAsSignedInteger :: forall sym. IsExprBuilder sym => SWord sym -> Maybe Integer
bvAsSignedInteger ZBV = Just 0
bvAsSignedInteger (DBV (bv :: SymBV sym w)) =
BV.asSigned (W.bvWidth bv) <$> W.asBV bv
bvAsUnsignedInteger :: forall sym. IsExprBuilder sym => SWord sym -> Maybe Integer
bvAsUnsignedInteger ZBV = Just 0
bvAsUnsignedInteger (DBV (bv :: SymBV sym w)) =
BV.asUnsigned <$> W.asBV bv
unsignedBVBounds :: forall sym. IsExprBuilder sym => SWord sym -> Maybe (Integer, Integer)
unsignedBVBounds ZBV = Just (0, 0)
unsignedBVBounds (DBV bv) = W.unsignedBVBounds bv
signedBVBounds :: forall sym. IsExprBuilder sym => SWord sym -> Maybe (Integer, Integer)
signedBVBounds ZBV = Just (0, 0)
signedBVBounds (DBV bv) = W.signedBVBounds bv
integerToBV :: forall sym width. (Show width, Integral width, IsExprBuilder sym) =>
sym -> SymInteger sym -> width -> IO (SWord sym)
integerToBV sym i w
| Just (Some wr) <- someNat w
, Just LeqProof <- isPosNat wr
= DBV <$> W.integerToBV sym i wr
| 0 == toInteger w
= return ZBV
| otherwise
= panic "integerToBV" ["invalid bit-width", show w]
bvToInteger :: forall sym. (IsExprBuilder sym) =>
sym -> SWord sym -> IO (SymInteger sym)
bvToInteger sym ZBV = W.intLit sym 0
bvToInteger sym (DBV bv) = W.bvToInteger sym bv
sbvToInteger :: forall sym. (IsExprBuilder sym) =>
sym -> SWord sym -> IO (SymInteger sym)
sbvToInteger sym ZBV = W.intLit sym 0
sbvToInteger sym (DBV bv) = W.sbvToInteger sym bv
bvWidth :: forall sym. SWord sym -> Integer
bvWidth (DBV x) = fromInteger (intValue (W.bvWidth x))
bvWidth ZBV = 0
bvLit :: forall sym. IsExprBuilder sym =>
sym -> Integer -> Integer -> IO (SWord sym)
bvLit _ w _
| w == 0
= return ZBV
bvLit sym w dat
| Just (Some rw) <- someNat w
, Just LeqProof <- isPosNat rw
= DBV <$> W.bvLit sym rw (BV.mkBV rw dat)
| otherwise
= panic "bvLit" ["size of bitvector is < 0 or >= maxInt", show w]
freshBV :: forall sym. IsSymExprBuilder sym =>
sym -> W.SolverSymbol -> Integer -> IO (SWord sym)
freshBV sym nm w
| w == 0
= return ZBV
| Just (Some rw) <- someNat w
, Just LeqProof <- isPosNat rw
= DBV <$> W.freshConstant sym nm (W.BaseBVRepr rw)
| otherwise
= panic "freshBV" ["size of bitvector is < 0 or >= maxInt", show w]
bvFill :: forall sym. IsExprBuilder sym =>
sym -> Integer -> Pred sym -> IO (SWord sym)
bvFill sym w p
| w == 0
= return ZBV
| Just (Some rw) <- someNat w
, Just LeqProof <- isPosNat rw
= DBV <$> W.bvFill sym rw p
| otherwise
= panic "bvFill" ["size of bitvector is < 0 or >= maxInt", show w]
bvAtBE :: forall sym.
IsExprBuilder sym =>
sym ->
SWord sym ->
Integer ->
IO (Pred sym)
bvAtBE sym (DBV bv) i = do
let w = natValue (W.bvWidth bv)
let idx = w - 1 - fromInteger i
W.testBitBV sym idx bv
bvAtBE _ ZBV _ = panic "bvAtBE" ["cannot index into empty bitvector"]
bvAtLE :: forall sym.
IsExprBuilder sym =>
sym ->
SWord sym ->
Integer ->
IO (Pred sym)
bvAtLE sym (DBV bv) i =
W.testBitBV sym (fromInteger i) bv
bvAtLE _ ZBV _ = panic "bvAtLE" ["cannot index into empty bitvector"]
bvSetBE :: forall sym.
IsExprBuilder sym =>
sym ->
SWord sym ->
Integer ->
Pred sym ->
IO (SWord sym)
bvSetBE _ ZBV _ _ = return ZBV
bvSetBE sym (DBV bv) i b =
do let w = natValue (W.bvWidth bv)
let idx = w - 1 - fromInteger i
DBV <$> W.bvSet sym bv idx b
bvSetLE :: forall sym.
IsExprBuilder sym =>
sym ->
SWord sym ->
Integer ->
Pred sym ->
IO (SWord sym)
bvSetLE _ ZBV _ _ = return ZBV
bvSetLE sym (DBV bv) i b =
DBV <$> W.bvSet sym bv (fromInteger i) b
bvJoin :: forall sym. IsExprBuilder sym => sym
-> SWord sym
-> SWord sym
-> IO (SWord sym)
bvJoin _ x ZBV = return x
bvJoin _ ZBV x = return x
bvJoin sym (DBV bv1) (DBV bv2)
| LeqProof <- leqAddPos (W.bvWidth bv1) (W.bvWidth bv2)
= DBV <$> W.bvConcat sym bv1 bv2
bvSliceBE :: forall sym. IsExprBuilder sym => sym
-> Integer
-> Integer
-> SWord sym -> IO (SWord sym)
bvSliceBE sym m n (DBV bv)
| Just (Some nr) <- someNat n,
Just LeqProof <- isPosNat nr,
Just (Some mr) <- someNat m,
let wr = W.bvWidth bv,
Just LeqProof <- testLeq (addNat mr nr) wr,
let idx = subNat wr (addNat mr nr),
Just LeqProof <- testLeq (addNat idx nr) wr
= DBV <$> W.bvSelect sym idx nr bv
| otherwise
= panic "bvSliceBE"
["invalid arguments to slice: " ++ show m ++ " " ++ show n
++ " from vector of length " ++ show (W.bvWidth bv)]
bvSliceBE _ _ _ ZBV = return ZBV
bvSliceLE :: forall sym. IsExprBuilder sym => sym
-> Integer
-> Integer
-> SWord sym -> IO (SWord sym)
bvSliceLE sym m n (DBV bv)
| Just (Some nr) <- someNat n,
Just LeqProof <- isPosNat nr,
Just (Some mr) <- someNat m,
let wr = W.bvWidth bv,
Just LeqProof <- testLeq (addNat mr nr) wr
= DBV <$> W.bvSelect sym mr nr bv
| otherwise
= panic "bvSliceLE"
["invalid arguments to slice: " ++ show m ++ " " ++ show n
++ " from vector of length " ++ show (W.bvWidth bv)]
bvSliceLE _ _ _ ZBV = return ZBV
w_bvLg2 :: forall sym w. (IsExprBuilder sym, 1 <= w) =>
sym -> SymBV sym w -> IO (SymBV sym w)
w_bvLg2 sym x = go 0
where
w = W.bvWidth x
size :: Integer
size = intValue w
lit :: Integer -> IO (SymBV sym w)
lit n = W.bvLit sym w (BV.mkBV w n)
go :: Integer -> IO (SymBV sym w)
go i | i < size = do
x' <- lit (2 ^ i)
b' <- W.bvUle sym x x'
th <- lit i
el <- go (i + 1)
W.bvIte sym b' th el
| otherwise = lit i
bvIte :: forall sym. IsExprBuilder sym =>
sym -> Pred sym -> SWord sym -> SWord sym -> IO (SWord sym)
bvIte _ _ ZBV ZBV
= return ZBV
bvIte sym p (DBV bv1) (DBV bv2)
| Just Refl <- testEquality (W.exprType bv1) (W.exprType bv2)
= DBV <$> W.bvIte sym p bv1 bv2
bvIte _ _ x y
= panic "bvIte" ["bit-vectors don't have same length", show (bvWidth x), show (bvWidth y)]
bvUnpackBE :: forall sym. IsExprBuilder sym =>
sym -> SWord sym -> IO (Vector (Pred sym))
bvUnpackBE _ ZBV = return V.empty
bvUnpackBE sym (DBV bv) = do
let w :: Natural
w = natValue (W.bvWidth bv)
V.generateM (fromIntegral w)
(\i -> W.testBitBV sym (w - 1 - fromIntegral i) bv)
bvUnpackLE :: forall sym. IsExprBuilder sym =>
sym -> SWord sym -> IO (Vector (Pred sym))
bvUnpackLE _ ZBV = return V.empty
bvUnpackLE sym (DBV bv) = do
let w :: Natural
w = natValue (W.bvWidth bv)
V.generateM (fromIntegral w)
(\i -> W.testBitBV sym (fromIntegral i) bv)
bvPackBE :: forall sym. (W.IsExpr (W.SymExpr sym), IsExprBuilder sym) =>
sym -> Vector (Pred sym) -> IO (SWord sym)
bvPackBE sym vec = do
vec' <- V.mapM (\p -> do
v1 <- bvLit sym 1 1
v2 <- bvLit sym 1 0
bvIte sym p v1 v2) vec
V.foldM (\x y -> bvJoin sym x y) ZBV vec'
bvPackLE :: forall sym. (W.IsExpr (W.SymExpr sym), IsExprBuilder sym) =>
sym -> Vector (Pred sym) -> IO (SWord sym)
bvPackLE sym vec = do
vec' <- V.mapM (\p -> do
v1 <- bvLit sym 1 1
v2 <- bvLit sym 1 0
bvIte sym p v1 v2) vec
V.foldM (\x y -> bvJoin sym y x) ZBV vec'
type SWordUn =
forall sym. IsExprBuilder sym =>
sym -> SWord sym -> IO (SWord sym)
bvUn :: forall sym. IsExprBuilder sym =>
(forall w. 1 <= w => sym -> SymBV sym w -> IO (SymBV sym w)) ->
sym -> SWord sym -> IO (SWord sym)
bvUn f sym (DBV bv) = DBV <$> f sym bv
bvUn _ _ ZBV = return ZBV
type SWordBin =
forall sym. IsExprBuilder sym =>
sym -> SWord sym -> SWord sym -> IO (SWord sym)
type PredBin =
forall sym. IsExprBuilder sym =>
sym -> SWord sym -> SWord sym -> IO (Pred sym)
bvBin :: forall sym. IsExprBuilder sym =>
(forall w. 1 <= w => sym -> SymBV sym w -> SymBV sym w -> IO (SymBV sym w)) ->
sym -> SWord sym -> SWord sym -> IO (SWord sym)
bvBin f sym (DBV bv1) (DBV bv2)
| Just Refl <- testEquality (W.exprType bv1) (W.exprType bv2)
= DBV <$> f sym bv1 bv2
bvBin _ _ ZBV ZBV
= return ZBV
bvBin _ _ x y
= panic "bvBin" ["bit-vectors don't have same length", show (bvWidth x), show (bvWidth y)]
bvBinPred :: forall sym. IsExprBuilder sym =>
Bool ->
(forall w. 1 <= w => sym -> SymBV sym w -> SymBV sym w -> IO (Pred sym)) ->
sym -> SWord sym -> SWord sym -> IO (Pred sym)
bvBinPred _ f sym x@(DBV bv1) y@(DBV bv2)
| Just Refl <- testEquality (W.exprType bv1) (W.exprType bv2)
= f sym bv1 bv2
| otherwise
= panic "bvBinPred" ["bit-vectors don't have same length", show (bvWidth x), show (bvWidth y)]
bvBinPred b _ sym ZBV ZBV
= pure (W.backendPred sym b)
bvBinPred _ _ _ x y
= panic "bvBinPred" ["bit-vectors don't have same length", show (bvWidth x), show (bvWidth y)]
bvNot :: SWordUn
bvNot = bvUn W.bvNotBits
bvAnd :: SWordBin
bvAnd = bvBin W.bvAndBits
bvOr :: SWordBin
bvOr = bvBin W.bvOrBits
bvXor :: SWordBin
bvXor = bvBin W.bvXorBits
bvNeg :: SWordUn
bvNeg = bvUn W.bvNeg
bvAdd :: SWordBin
bvAdd = bvBin W.bvAdd
bvSub :: SWordBin
bvSub = bvBin W.bvSub
bvMul :: SWordBin
bvMul = bvBin W.bvMul
bvPopcount :: SWordUn
bvPopcount = bvUn W.bvPopcount
bvCountLeadingZeros :: SWordUn
bvCountLeadingZeros = bvUn W.bvCountLeadingZeros
bvCountTrailingZeros :: SWordUn
bvCountTrailingZeros = bvUn W.bvCountTrailingZeros
bvForall :: W.IsSymExprBuilder sym =>
sym -> Natural -> (SWord sym -> IO (Pred sym)) -> IO (Pred sym)
bvForall sym n f =
case W.userSymbol "i" of
Left err -> panic "bvForall" [show err]
Right indexSymbol ->
case mkNatRepr n of
Some w
| Just LeqProof <- testLeq (knownNat @1) w ->
do i <- W.freshBoundVar sym indexSymbol $ W.BaseBVRepr w
body <- f . DBV $ W.varExpr sym i
W.forallPred sym i body
| otherwise -> f ZBV
bvUDiv :: SWordBin
bvUDiv = bvBin W.bvUdiv
bvURem :: SWordBin
bvURem = bvBin W.bvUrem
bvSDiv :: SWordBin
bvSDiv = bvBin W.bvSdiv
bvSRem :: SWordBin
bvSRem = bvBin W.bvSrem
bvLg2 :: SWordUn
bvLg2 = bvUn w_bvLg2
bvEq :: PredBin
bvEq = bvBinPred True W.bvEq
bvsle :: PredBin
bvsle = bvBinPred True W.bvSle
bvslt :: PredBin
bvslt = bvBinPred False W.bvSlt
bvule :: PredBin
bvule = bvBinPred True W.bvUle
bvult :: PredBin
bvult = bvBinPred False W.bvUlt
bvsge :: PredBin
bvsge = bvBinPred True W.bvSge
bvsgt :: PredBin
bvsgt = bvBinPred False W.bvSgt
bvuge :: PredBin
bvuge = bvBinPred True W.bvUge
bvugt :: PredBin
bvugt = bvBinPred False W.bvUgt
bvIsNonzero :: IsExprBuilder sym => sym -> SWord sym -> IO (Pred sym)
bvIsNonzero sym ZBV = return (W.falsePred sym)
bvIsNonzero sym (DBV x) = W.bvIsNonzero sym x
bvMax ::
(IsExprBuilder sym, 1 <= w) =>
sym ->
W.SymBV sym w ->
W.SymBV sym w ->
IO (W.SymBV sym w)
bvMax sym x y =
do p <- W.bvUge sym x y
W.bvIte sym p x y
reduceShift ::
IsExprBuilder sym =>
(forall w. (1 <= w) => sym -> W.SymBV sym w -> W.SymBV sym w -> IO (W.SymBV sym w)) ->
sym -> SWord sym -> SWord sym -> IO (SWord sym)
reduceShift _wop _sym ZBV _ = return ZBV
reduceShift _wop _sym x ZBV = return x
reduceShift wop sym (DBV x) (DBV y) =
case compareNat (W.bvWidth x) (W.bvWidth y) of
NatEQ -> DBV <$> wop sym x y
NatGT _diff ->
do y' <- W.bvZext sym (W.bvWidth x) y
DBV <$> wop sym x y'
NatLT _diff ->
do wx <- W.bvLit sym (W.bvWidth y) (BV.mkBV (W.bvWidth y) (intValue (W.bvWidth x)))
y' <- W.bvTrunc sym (W.bvWidth x) =<< bvMax sym y wx
DBV <$> wop sym x y'
reduceRotate ::
IsExprBuilder sym =>
(forall w. (1 <= w) => sym -> W.SymBV sym w -> W.SymBV sym w -> IO (W.SymBV sym w)) ->
sym -> SWord sym -> SWord sym -> IO (SWord sym)
reduceRotate _wop _sym ZBV _ = return ZBV
reduceRotate _wop _sym x ZBV = return x
reduceRotate wop sym (DBV x) (DBV y) =
case compareNat (W.bvWidth x) (W.bvWidth y) of
NatEQ -> DBV <$> wop sym x y
NatGT _diff ->
do y' <- W.bvZext sym (W.bvWidth x) y
DBV <$> wop sym x y'
NatLT _diff ->
do wx <- W.bvLit sym (W.bvWidth y) (BV.mkBV (W.bvWidth y) (intValue (W.bvWidth x)))
y' <- W.bvTrunc sym (W.bvWidth x) =<< W.bvUrem sym y wx
DBV <$> wop sym x y'
bvShl :: W.IsExprBuilder sym => sym -> SWord sym -> SWord sym -> IO (SWord sym)
bvShl = reduceShift W.bvShl
bvLshr :: W.IsExprBuilder sym => sym -> SWord sym -> SWord sym -> IO (SWord sym)
bvLshr = reduceShift W.bvLshr
bvAshr :: W.IsExprBuilder sym => sym -> SWord sym -> SWord sym -> IO (SWord sym)
bvAshr = reduceShift W.bvAshr
bvRol :: W.IsExprBuilder sym => sym -> SWord sym -> SWord sym -> IO (SWord sym)
bvRol = reduceRotate W.bvRol
bvRor :: W.IsExprBuilder sym => sym -> SWord sym -> SWord sym -> IO (SWord sym)
bvRor = reduceRotate W.bvRor