what4-1.0: src/What4/Utils/BVDomain.hs
{-|
Module : What4.Utils.BVDomain
Description : Abstract domains for bitvectors
Copyright : (c) Galois Inc, 2019-2020
License : BSD3
Maintainer : huffman@galois.com
Provides an implementation of abstract domains for bitvectors.
This abstract domain has essentially two modes: arithmetic
and bitvector modes. The arithmetic mode is a fairly straightforward
interval domain, albeit one that is carefully implemented to deal
properly with intervals that "cross zero", as is relatively common
when using 2's complement signed representations. The bitwise
mode tracks the values of individual bits independently in a
3-valued logic (true, false or unknown). The abstract domain
transitions between the two modes when necessary, but attempts
to retain as much precision as possible.
The operations of these domains are formalized in the companion
Cryptol files found together in this package under the \"doc\"
directory, and their soundness properties stated and established.
-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ViewPatterns #-}
module What4.Utils.BVDomain
( -- * Bitvector abstract domains
BVDomain(..)
, proper
, member
, size
-- ** Domain transfer functions
, asArithDomain
, asBitwiseDomain
, asXorDomain
, fromXorDomain
, arithToXorDomain
, bitwiseToXorDomain
, xorToBitwiseDomain
-- ** Projection functions
, asSingleton
, eq
, slt
, ult
, testBit
, domainsOverlap
, ubounds
, sbounds
, A.arithDomainData
, B.bitbounds
-- * Operations
, any
, singleton
, range
, fromAscEltList
, union
, concat
, select
, zext
, sext
-- ** Shifts and rotates
, shl
, lshr
, ashr
, rol
, ror
-- ** Arithmetic
, add
, negate
, scale
, mul
, udiv
, urem
, sdiv
, srem
-- ** Bitwise
, What4.Utils.BVDomain.not
, and
, or
, xor
-- ** Misc
, popcnt
, clz
, ctz
-- * Useful bitvector computations
, bitwiseRoundAbove
, bitwiseRoundBetween
-- * Correctness properties
, genDomain
, genElement
, genPair
, correct_arithToBitwise
, correct_bitwiseToArith
, correct_bitwiseToXorDomain
, correct_arithToXorDomain
, correct_xorToBitwiseDomain
, correct_asXorDomain
, correct_fromXorDomain
, correct_bra1
, correct_bra2
, correct_brb1
, correct_brb2
, correct_any
, correct_ubounds
, correct_sbounds
, correct_singleton
, correct_overlap
, precise_overlap
, correct_union
, correct_zero_ext
, correct_sign_ext
, correct_concat
, correct_select
, correct_add
, correct_neg
, correct_mul
, correct_scale
, correct_udiv
, correct_urem
, correct_sdiv
, correct_srem
, correct_shl
, correct_lshr
, correct_ashr
, correct_rol
, correct_ror
, correct_eq
, correct_ult
, correct_slt
, correct_and
, correct_or
, correct_not
, correct_xor
, correct_testBit
, correct_popcnt
, correct_clz
, correct_ctz
) where
import qualified Data.Bits as Bits
import Data.Bits hiding (testBit, xor)
import qualified Data.List as List
import Data.Parameterized.NatRepr
import Numeric.Natural
import GHC.TypeNats
import GHC.Stack
import qualified Prelude
import Prelude hiding (any, concat, negate, and, or, not)
import qualified What4.Utils.Arithmetic as Arith
import qualified What4.Utils.BVDomain.Arith as A
import qualified What4.Utils.BVDomain.Bitwise as B
import qualified What4.Utils.BVDomain.XOR as X
import Test.Verification ( Property, property, (==>), Gen, chooseBool )
arithToBitwiseDomain :: A.Domain w -> B.Domain w
arithToBitwiseDomain a =
let mask = A.bvdMask a in
case A.arithDomainData a of
Nothing -> B.interval mask 0 mask
Just (alo,_) -> B.interval mask lo hi
where
u = A.unknowns a
hi = alo .|. u
lo = hi `Bits.xor` u
bitwiseToArithDomain :: B.Domain w -> A.Domain w
bitwiseToArithDomain b = A.interval mask lo ((hi - lo) .&. mask)
where
mask = B.bvdMask b
(lo,hi) = B.bitbounds b
bitwiseToXorDomain :: B.Domain w -> X.Domain w
bitwiseToXorDomain b = X.interval mask lo hi
where
mask = B.bvdMask b
(lo,hi) = B.bitbounds b
arithToXorDomain :: A.Domain w -> X.Domain w
arithToXorDomain a =
let mask = A.bvdMask a in
case A.arithDomainData a of
Nothing -> X.BVDXor mask mask mask
Just (alo,_) -> X.BVDXor mask hi u
where
u = A.unknowns a
hi = alo .|. u
xorToBitwiseDomain :: X.Domain w -> B.Domain w
xorToBitwiseDomain x = B.interval mask lo hi
where
mask = X.bvdMask x
(lo, hi) = X.bitbounds x
asXorDomain :: BVDomain w -> X.Domain w
asXorDomain (BVDArith a) = arithToXorDomain a
asXorDomain (BVDBitwise b) = bitwiseToXorDomain b
fromXorDomain :: X.Domain w -> BVDomain w
fromXorDomain x = BVDBitwise (xorToBitwiseDomain x)
asArithDomain :: BVDomain w -> A.Domain w
asArithDomain (BVDArith a) = a
asArithDomain (BVDBitwise b) = bitwiseToArithDomain b
asBitwiseDomain :: BVDomain w -> B.Domain w
asBitwiseDomain (BVDArith a) = arithToBitwiseDomain a
asBitwiseDomain (BVDBitwise b) = b
--------------------------------------------------------------------------------
-- BVDomain definition
-- | A value of type @'BVDomain' w@ represents a set of bitvectors of
-- width @w@. A BVDomain represents either an arithmetic interval, or
-- a bitwise interval.
data BVDomain (w :: Nat)
= BVDArith !(A.Domain w)
| BVDBitwise !(B.Domain w)
deriving Show
-- | Return the bitvector mask value from this domain
bvdMask :: BVDomain w -> Integer
bvdMask x =
case x of
BVDArith a -> A.bvdMask a
BVDBitwise b -> B.bvdMask b
-- | Test if the domain satisfies its invariants
proper :: NatRepr w -> BVDomain w -> Bool
proper w (BVDArith a) = A.proper w a
proper w (BVDBitwise b) = B.proper w b
-- | Test if the given integer value is a member of the abstract domain
member :: BVDomain w -> Integer -> Bool
member (BVDArith a) x = A.member a x
member (BVDBitwise a) x = B.member a x
-- | Compute how many concrete elements are in the abstract domain
size :: BVDomain w -> Integer
size (BVDArith a) = A.size a
size (BVDBitwise b) = B.size b
-- | Generate a random nonempty domain
genDomain :: NatRepr w -> Gen (BVDomain w)
genDomain w =
do b <- chooseBool
if b then
BVDArith <$> A.genDomain w
else
BVDBitwise <$> B.genDomain w
-- | Generate a random element from a domain, which
-- is assumed to be nonempty
genElement :: BVDomain w -> Gen Integer
genElement (BVDArith a) = A.genElement a
genElement (BVDBitwise b) = B.genElement b
-- | Generate a random nonempty domain and an element
-- contained in that domain.
genPair :: NatRepr w -> Gen (BVDomain w, Integer)
genPair w =
do a <- genDomain w
x <- genElement a
return (a,x)
--------------------------------------------------------------------------------
-- Projection functions
-- | Return value if this is a singleton.
asSingleton :: BVDomain w -> Maybe Integer
asSingleton (BVDArith a) = A.asSingleton a
asSingleton (BVDBitwise b) = B.asSingleton b
{- |
Precondition: @x <= lomask@. Find the (arithmetically) smallest
@z@ above @x@ which is bitwise above @lomask@. In other words
find the smallest @z@ such that @x <= z@ and @lomask .|. z == z@.
-}
bitwiseRoundAbove ::
Integer {- ^ @bvmask@, based on the width of the bitvectors in question -} ->
Integer {- ^ @x@ -} ->
Integer {- ^ @lomask@ -} ->
Integer
bitwiseRoundAbove bvmask x lomask = upperbits .|. lowerbits
where
upperbits = x .&. (bvmask `Bits.xor` fillmask)
lowerbits = lomask .&. fillmask
fillmask = A.fillright ((x .|. lomask) `Bits.xor` x)
{- |
Precondition: @lomask <= x <= himask@ and @lomask .|. himask == himask@.
Find the (arithmetically) smallest @z@ above @x@ which is bitwise between
@lomask@ and @himask@. In other words, find the smallest @z@ such that
@x <= z@ and @lomask .|. z = z@ and @z .|. himask == himask@.
-}
bitwiseRoundBetween ::
Integer {- ^ @bvmask@, based on the width of the bitvectors in question -} ->
Integer {- ^ @x@ -} ->
Integer {- ^ @lomask@ -} ->
Integer {- ^ @himask@ -} ->
Integer
bitwiseRoundBetween bvmask x lomask himask = final
-- read these steps bottom up...
where
-- Finally mask out the low bits and only set those required by the lomask
final = (upper .&. (lobits `Bits.xor` bvmask)) .|. lomask
-- add the correcting bit and mask out any extraneous bits set in
-- the previous step
upper = (z + highbit) .&. himask
-- set ourselves up so that when we add the high bit to correct,
-- the carry will ripple until it finds a bit position that we
-- are allowed to set.
z = loup .|. himask'
-- isolate just the highest incorrect bit
highbit = rmask `Bits.xor` lobits
-- a mask for all the bits to the right of the highest incorrect bit
lobits = rmask `shiftR` 1
-- set all the bits to the right of the highest incorrect bit
rmask = A.fillright r
-- now, compute all the bits that are set, but are not
-- allowed to be set according to the himask
r = loup .&. himask'
-- complement of the highmask
himask' = himask `Bits.xor` bvmask
-- first, round up to the lomask
loup = bitwiseRoundAbove bvmask x lomask
-- | Test if an arithmetic domain overlaps with a bitwise domain
mixedDomainsOverlap :: A.Domain a -> B.Domain b -> Bool
mixedDomainsOverlap a b =
case A.arithDomainData a of
Nothing -> B.nonempty b
Just (alo,_) ->
let (lomask,himask) = B.bitbounds b
brb = bitwiseRoundBetween (A.bvdMask a) alo lomask himask
in B.nonempty b && (A.member a lomask || A.member a himask || A.member a brb)
-- | Return true if domains contain a common element.
domainsOverlap :: BVDomain w -> BVDomain w -> Bool
domainsOverlap (BVDBitwise a) (BVDBitwise b) = B.domainsOverlap a b
domainsOverlap (BVDArith a) (BVDArith b) = A.domainsOverlap a b
domainsOverlap (BVDArith a) (BVDBitwise b) = mixedDomainsOverlap a b
domainsOverlap (BVDBitwise b) (BVDArith a) = mixedDomainsOverlap a b
arithDomainLo :: A.Domain w -> Integer
arithDomainLo a =
case A.arithDomainData a of
Nothing -> 0
Just (lo,_) -> lo
mixedCandidates :: A.Domain w -> B.Domain w -> [Integer]
mixedCandidates a b =
case A.arithDomainData a of
Nothing -> [ lomask ]
Just (alo,_) -> [ lomask, himask, bitwiseRoundBetween (A.bvdMask a) alo lomask himask ]
where
(lomask,himask) = B.bitbounds b
-- | Return a list of "candidate" overlap elements. If two domains
-- overlap, then they will definitely share one of the given
-- values.
overlapCandidates :: BVDomain w -> BVDomain w -> [Integer]
overlapCandidates (BVDArith a) (BVDBitwise b) = mixedCandidates a b
overlapCandidates (BVDBitwise b) (BVDArith a) = mixedCandidates a b
overlapCandidates (BVDArith a) (BVDArith b) = [ arithDomainLo a, arithDomainLo b ]
overlapCandidates (BVDBitwise a) (BVDBitwise b) = [ loa .|. lob ]
where
(loa,_) = B.bitbounds a
(lob,_) = B.bitbounds b
eq :: BVDomain w -> BVDomain w -> Maybe Bool
eq a b
| Just x <- asSingleton a
, Just y <- asSingleton b = Just (x == y)
| domainsOverlap a b == False = Just False
| otherwise = Nothing
-- | Check if all elements in one domain are less than all elements in other.
slt :: (1 <= w) => NatRepr w -> BVDomain w -> BVDomain w -> Maybe Bool
slt w a b = A.slt w (asArithDomain a) (asArithDomain b)
-- | Check if all elements in one domain are less than all elements in other.
ult :: (1 <= w) => BVDomain w -> BVDomain w -> Maybe Bool
ult a b = A.ult (asArithDomain a) (asArithDomain b)
-- | Return @Just@ if every bitvector in the domain has the same bit
-- at the given index.
testBit ::
NatRepr w ->
BVDomain w ->
Natural {- ^ Index of bit (least-significant bit has index 0) -} ->
Maybe Bool
testBit _w a i = B.testBit (asBitwiseDomain a) i
ubounds :: BVDomain w -> (Integer, Integer)
ubounds a = A.ubounds (asArithDomain a)
sbounds :: (1 <= w) => NatRepr w -> BVDomain w -> (Integer, Integer)
sbounds w a = A.sbounds w (asArithDomain a)
--------------------------------------------------------------------------------
-- Operations
-- | Represents all values
any :: (1 <= w) => NatRepr w -> BVDomain w
any w = BVDBitwise (B.any w)
-- | Create a bitvector domain representing the integer.
singleton :: (HasCallStack, 1 <= w) => NatRepr w -> Integer -> BVDomain w
singleton w x = BVDArith (A.singleton w x)
-- | @range w l u@ returns domain containing all bitvectors formed
-- from the @w@ low order bits of some @i@ in @[l,u]@. Note that per
-- @testBit@, the least significant bit has index @0@.
range :: NatRepr w -> Integer -> Integer -> BVDomain w
range w al ah = BVDArith (A.range w al ah)
-- | Create an abstract domain from an ascending list of elements.
-- The elements are assumed to be distinct.
fromAscEltList :: (1 <= w) => NatRepr w -> [Integer] -> BVDomain w
fromAscEltList w xs = BVDArith (A.fromAscEltList w xs)
-- | Return union of two domains.
union :: (1 <= w) => BVDomain w -> BVDomain w -> BVDomain w
union (BVDBitwise a) (BVDBitwise b) = BVDBitwise (B.union a b)
union (BVDArith a) (BVDArith b) = BVDArith (A.union a b)
union (BVDBitwise a) (BVDArith b) = mixedUnion b a
union (BVDArith a) (BVDBitwise b) = mixedUnion a b
mixedUnion :: (1 <= w) => A.Domain w -> B.Domain w -> BVDomain w
mixedUnion a b
| Just _ <- A.asSingleton a = BVDBitwise (B.union (arithToBitwiseDomain a) b)
| otherwise = BVDArith (A.union a (bitwiseToArithDomain b))
-- | @concat a y@ returns domain where each element in @a@ has been
-- concatenated with an element in @y@. The most-significant bits
-- are @a@, and the least significant bits are @y@.
concat :: NatRepr u -> BVDomain u -> NatRepr v -> BVDomain v -> BVDomain (u + v)
concat u (BVDArith a) v (BVDArith b) = BVDArith (A.concat u a v b)
concat u (asBitwiseDomain -> a) v (asBitwiseDomain -> b) = BVDBitwise (B.concat u a v b)
-- | @select i n a@ selects @n@ bits starting from index @i@ from @a@.
select ::
(1 <= n, i + n <= w) =>
NatRepr i ->
NatRepr n ->
BVDomain w -> BVDomain n
select i n (BVDArith a) = BVDArith (A.select i n a)
select i n (BVDBitwise b) = BVDBitwise (B.select i n b)
zext :: (1 <= w, w+1 <= u) => BVDomain w -> NatRepr u -> BVDomain u
zext (BVDArith a) u = BVDArith (A.zext a u)
zext (BVDBitwise b) u = BVDBitwise (B.zext b u)
sext ::
forall w u. (1 <= w, w + 1 <= u) =>
NatRepr w ->
BVDomain w ->
NatRepr u ->
BVDomain u
sext w (BVDArith a) u = BVDArith (A.sext w a u)
sext w (BVDBitwise b) u = BVDBitwise (B.sext w b u)
--------------------------------------------------------------------------------
-- Shifts
-- An arbitrary value; if we have to union together more than this many
-- bitwise shifts or rotates we'll fall back on some default instead
shiftBound :: Integer
shiftBound = 16
shl :: (1 <= w) => NatRepr w -> BVDomain w -> BVDomain w -> BVDomain w
shl w (BVDBitwise a) (asArithDomain -> b)
| lo <= hi' && hi' - lo <= shiftBound =
BVDBitwise $ foldl1 B.union [ B.shl w a y | y <- [lo .. hi'] ]
where
(lo, hi) = A.ubounds b
hi' = max hi (intValue w)
shl w (asArithDomain -> a) (asArithDomain -> b) = BVDArith (A.shl w a b)
lshr :: (1 <= w) => NatRepr w -> BVDomain w -> BVDomain w -> BVDomain w
lshr w (BVDBitwise a) (asArithDomain -> b)
| lo <= hi' && hi' - lo <= shiftBound =
BVDBitwise $ foldl1 B.union [ B.lshr w a y | y <- [lo .. hi'] ]
where
(lo, hi) = A.ubounds b
hi' = max hi (intValue w)
lshr w (asArithDomain -> a) (asArithDomain -> b) = BVDArith (A.lshr w a b)
ashr :: (1 <= w) => NatRepr w -> BVDomain w -> BVDomain w -> BVDomain w
ashr w (BVDBitwise a) (asArithDomain -> b)
| lo <= hi' && hi' - lo <= shiftBound =
BVDBitwise $ foldl1 B.union [ B.ashr w a y | y <- [lo .. hi'] ]
where
(lo, hi) = A.ubounds b
hi' = max hi (intValue w)
ashr w (asArithDomain -> a) (asArithDomain -> b) = BVDArith (A.ashr w a b)
rol :: (1 <= w) => NatRepr w -> BVDomain w -> BVDomain w -> BVDomain w
-- Special cases, rotating all 0 or all 1 bits makes no difference
rol _w a@(asSingleton -> Just x) _
| x == 0 = a
| x == bvdMask a = a
rol w (asBitwiseDomain -> a) (asArithDomain -> b) =
if (lo <= hi && hi - lo <= shiftBound) then
BVDBitwise $ foldl1 B.union [ B.rol w a y | y <- [lo .. hi] ]
else
any w
where
(lo, hi) = A.ubounds (A.urem b (A.singleton w (intValue w)))
ror :: (1 <= w) => NatRepr w -> BVDomain w -> BVDomain w -> BVDomain w
-- Special cases, rotating all 0 or all 1 bits makes no difference
ror _w a@(asSingleton -> Just x) _
| x == 0 = a
| x == bvdMask a = a
ror w (asBitwiseDomain -> a) (asArithDomain -> b) =
if (lo <= hi && hi - lo <= shiftBound) then
BVDBitwise $ foldl1 B.union [ B.ror w a y | y <- [lo .. hi] ]
else
any w
where
(lo, hi) = A.ubounds (A.urem b (A.singleton w (intValue w)))
--------------------------------------------------------------------------------
-- Arithmetic
add :: (1 <= w) => BVDomain w -> BVDomain w -> BVDomain w
add a b
| Just 0 <- asSingleton a = b
| Just 0 <- asSingleton b = a
| otherwise = BVDArith (A.add (asArithDomain a) (asArithDomain b))
negate :: (1 <= w) => BVDomain w -> BVDomain w
negate (asArithDomain -> a) = BVDArith (A.negate a)
scale :: (1 <= w) => Integer -> BVDomain w -> BVDomain w
scale k a
| k == 1 = a
| otherwise = BVDArith (A.scale k (asArithDomain a))
mul :: (1 <= w) => BVDomain w -> BVDomain w -> BVDomain w
mul a b
| Just 1 <- asSingleton a = b
| Just 1 <- asSingleton b = a
| otherwise = BVDArith (A.mul (asArithDomain a) (asArithDomain b))
udiv :: (1 <= w) => BVDomain w -> BVDomain w -> BVDomain w
udiv (asArithDomain -> a) (asArithDomain -> b) = BVDArith (A.udiv a b)
urem :: (1 <= w) => BVDomain w -> BVDomain w -> BVDomain w
urem (asArithDomain -> a) (asArithDomain -> b) = BVDArith (A.urem a b)
sdiv :: (1 <= w) => NatRepr w -> BVDomain w -> BVDomain w -> BVDomain w
sdiv w (asArithDomain -> a) (asArithDomain -> b) = BVDArith (A.sdiv w a b)
srem :: (1 <= w) => NatRepr w -> BVDomain w -> BVDomain w -> BVDomain w
srem w (asArithDomain -> a) (asArithDomain -> b) = BVDArith (A.srem w a b)
--------------------------------------------------------------------------------
-- Bitwise logical
-- | Complement bits in range.
not :: BVDomain w -> BVDomain w
not (BVDArith a) = BVDArith (A.not a)
not (BVDBitwise b) = BVDBitwise (B.not b)
and :: BVDomain w -> BVDomain w -> BVDomain w
and a b
| Just x <- asSingleton a, x == mask = b
| Just x <- asSingleton b, x == mask = a
| otherwise = BVDBitwise (B.and (asBitwiseDomain a) (asBitwiseDomain b))
where
mask = bvdMask a
or :: BVDomain w -> BVDomain w -> BVDomain w
or a b
| Just 0 <- asSingleton a = b
| Just 0 <- asSingleton b = a
| otherwise = BVDBitwise (B.or (asBitwiseDomain a) (asBitwiseDomain b))
xor :: BVDomain w -> BVDomain w -> BVDomain w
xor a b
| Just 0 <- asSingleton a = b
| Just 0 <- asSingleton b = a
| otherwise = BVDBitwise (B.xor (asBitwiseDomain a) (asBitwiseDomain b))
-------------------------------------------------------------------------------
-- Misc operations
popcnt :: NatRepr w -> BVDomain w -> BVDomain w
popcnt w (asBitwiseDomain -> b) = BVDArith (A.range w lo hi)
where
(bitlo, bithi) = B.bitbounds b
lo = toInteger (Bits.popCount bitlo)
hi = toInteger (Bits.popCount bithi)
clz :: NatRepr w -> BVDomain w -> BVDomain w
clz w (asBitwiseDomain -> b) = BVDArith (A.range w lo hi)
where
(bitlo, bithi) = B.bitbounds b
lo = Arith.clz w bithi
hi = Arith.clz w bitlo
ctz :: NatRepr w -> BVDomain w -> BVDomain w
ctz w (asBitwiseDomain -> b) = BVDArith (A.range w lo hi)
where
(bitlo, bithi) = B.bitbounds b
lo = Arith.ctz w bithi
hi = Arith.ctz w bitlo
------------------------------------------------------------------
-- Correctness properties
-- | Check that a domain is proper, and that
-- the given value is a member
pmember :: NatRepr n -> BVDomain n -> Integer -> Bool
pmember n a x = proper n a && member a x
correct_arithToBitwise :: NatRepr n -> (A.Domain n, Integer) -> Property
correct_arithToBitwise n (a,x) = A.member a x ==> B.pmember n (arithToBitwiseDomain a) x
correct_bitwiseToArith :: NatRepr n -> (B.Domain n, Integer) -> Property
correct_bitwiseToArith n (b,x) = B.member b x ==> A.pmember n (bitwiseToArithDomain b) x
correct_bitwiseToXorDomain :: NatRepr n -> (B.Domain n, Integer) -> Property
correct_bitwiseToXorDomain n (b,x) = B.member b x ==> X.pmember n (bitwiseToXorDomain b) x
correct_arithToXorDomain :: NatRepr n -> (A.Domain n, Integer) -> Property
correct_arithToXorDomain n (a,x) = A.member a x ==> X.pmember n (arithToXorDomain a) x
correct_xorToBitwiseDomain :: NatRepr n -> (X.Domain n, Integer) -> Property
correct_xorToBitwiseDomain n (a,x) = X.member a x ==> B.pmember n (xorToBitwiseDomain a) x
correct_asXorDomain :: NatRepr n -> (BVDomain n, Integer) -> Property
correct_asXorDomain n (a, x) = member a x ==> X.pmember n (asXorDomain a) x
correct_fromXorDomain :: NatRepr n -> (X.Domain n, Integer) -> Property
correct_fromXorDomain n (a, x) = X.member a x ==> pmember n (fromXorDomain a) x
correct_bra1 :: NatRepr n -> Integer -> Integer -> Property
correct_bra1 n x lomask = lomask <= x ==> (x <= q && B.bitle lomask q)
where
q = bitwiseRoundAbove (maxUnsigned n) x lomask
correct_bra2 :: NatRepr n -> Integer -> Integer -> Integer -> Property
correct_bra2 n x lomask q' = (x <= q' && B.bitle lomask q') ==> q <= q'
where
q = bitwiseRoundAbove (maxUnsigned n) x lomask
correct_brb1 :: NatRepr n -> Integer -> Integer -> Integer -> Property
correct_brb1 n x lomask himask =
(B.bitle lomask himask && lomask <= x && x <= himask) ==>
(x <= q && B.bitle lomask q && B.bitle q himask)
where
q = bitwiseRoundBetween (maxUnsigned n) x lomask himask
correct_brb2 :: NatRepr n -> Integer -> Integer -> Integer -> Integer -> Property
correct_brb2 n x lomask himask q' =
(x <= q' && B.bitle lomask q' && B.bitle q' himask) ==> q <= q'
where
q = bitwiseRoundBetween (maxUnsigned n) x lomask himask
correct_any :: (1 <= n) => NatRepr n -> Integer -> Property
correct_any n x = property (pmember n (any n) x)
correct_ubounds :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> Property
correct_ubounds n (a,x) = member a x' ==> lo <= x' && x' <= hi
where
x' = toUnsigned n x
(lo,hi) = ubounds a
correct_sbounds :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> Property
correct_sbounds n (a,x) = member a x' ==> lo <= x' && x' <= hi
where
x' = toSigned n x
(lo,hi) = sbounds n a
correct_singleton :: (1 <= n) => NatRepr n -> Integer -> Integer -> Property
correct_singleton n x y = property (member (singleton n x') y' == (x' == y'))
where
x' = toUnsigned n x
y' = toUnsigned n y
correct_overlap :: BVDomain n -> BVDomain n -> Integer -> Property
correct_overlap a b x =
member a x && member b x ==> domainsOverlap a b
precise_overlap :: BVDomain n -> BVDomain n -> Property
precise_overlap a b =
domainsOverlap a b ==> List.or [ member a x && member b x | x <- overlapCandidates a b ]
correct_union :: (1 <= n) => NatRepr n -> BVDomain n -> BVDomain n -> Integer -> Property
correct_union n a b x =
(member a x || member b x) ==> pmember n (union a b) x
correct_zero_ext :: (1 <= w, w+1 <= u) => NatRepr w -> BVDomain w -> NatRepr u -> Integer -> Property
correct_zero_ext w a u x = member a x' ==> pmember u (zext a u) x'
where
x' = toUnsigned w x
correct_sign_ext :: (1 <= w, w+1 <= u) => NatRepr w -> BVDomain w -> NatRepr u -> Integer -> Property
correct_sign_ext w a u x = member a x' ==> pmember u (sext w a u) x'
where
x' = toSigned w x
correct_concat :: NatRepr m -> (BVDomain m,Integer) -> NatRepr n -> (BVDomain n,Integer) -> Property
correct_concat m (a,x) n (b,y) =
member a x ==> member b y ==> pmember (addNat m n) (concat m a n b) z
where
z = (x `shiftL` (widthVal n)) .|. y
correct_select :: (1 <= n, i + n <= w) =>
NatRepr i -> NatRepr n -> (BVDomain w, Integer) -> Property
correct_select i n (a, x) = member a x ==> pmember n (select i n a) y
where
y = toUnsigned n ((x .&. bvdMask a) `shiftR` (widthVal i))
correct_add :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_add n (a,x) (b,y) = member a x ==> member b y ==> pmember n (add a b) (x + y)
correct_neg :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> Property
correct_neg n (a,x) = member a x ==> pmember n (negate a) (Prelude.negate x)
correct_mul :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_mul n (a,x) (b,y) = member a x ==> member b y ==> pmember n (mul a b) (x * y)
correct_scale :: (1 <= n) => NatRepr n -> Integer -> (BVDomain n, Integer) -> Property
correct_scale n k (a,x) = member a x ==> pmember n (scale k' a) (k' * x)
where
k' = toSigned n k
correct_udiv :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_udiv n (a,x) (b,y) = member a x' ==> member b y' ==> y' /= 0 ==> pmember n (udiv a b) (x' `quot` y')
where
x' = toUnsigned n x
y' = toUnsigned n y
correct_urem :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_urem n (a,x) (b,y) = member a x' ==> member b y' ==> y' /= 0 ==> pmember n (urem a b) (x' `rem` y')
where
x' = toUnsigned n x
y' = toUnsigned n y
correct_sdiv :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_sdiv n (a,x) (b,y) =
member a x' ==> member b y' ==> y' /= 0 ==> pmember n (sdiv n a b) (x' `quot` y')
where
x' = toSigned n x
y' = toSigned n y
correct_srem :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_srem n (a,x) (b,y) =
member a x' ==> member b y' ==> y' /= 0 ==> pmember n (srem n a b) (x' `rem` y')
where
x' = toSigned n x
y' = toSigned n y
correct_shl :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_shl n (a,x) (b,y) = member a x ==> member b y ==> pmember n (shl n a b) z
where
z = (toUnsigned n x) `shiftL` fromInteger (min (intValue n) y)
correct_lshr :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_lshr n (a,x) (b,y) = member a x ==> member b y ==> pmember n (lshr n a b) z
where
z = (toUnsigned n x) `shiftR` fromInteger (min (intValue n) y)
correct_ashr :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_ashr n (a,x) (b,y) = member a x ==> member b y ==> pmember n (ashr n a b) z
where
z = (toSigned n x) `shiftR` fromInteger (min (intValue n) y)
correct_rol :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_rol n (a,x) (b,y) = member a x ==> member b y ==> pmember n (rol n a b) (Arith.rotateLeft n x y)
correct_ror :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_ror n (a,x) (b,y) = member a x ==> member b y ==> pmember n (ror n a b) (Arith.rotateRight n x y)
correct_eq :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_eq n (a,x) (b,y) =
member a x ==> member b y ==>
case eq a b of
Just True -> toUnsigned n x == toUnsigned n y
Just False -> toUnsigned n x /= toUnsigned n y
Nothing -> True
correct_ult :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_ult n (a,x) (b,y) =
member a x ==> member b y ==>
case ult a b of
Just True -> toUnsigned n x < toUnsigned n y
Just False -> toUnsigned n x >= toUnsigned n y
Nothing -> True
correct_slt :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_slt n (a,x) (b,y) =
member a x ==> member b y ==>
case slt n a b of
Just True -> toSigned n x < toSigned n y
Just False -> toSigned n x >= toSigned n y
Nothing -> True
correct_not :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> Property
correct_not n (a,x) = member a x ==> pmember n (not a) (complement x)
correct_and :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_and n (a,x) (b,y) = member a x ==> member b y ==> pmember n (and a b) (x .&. y)
correct_or :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_or n (a,x) (b,y) = member a x ==> member b y ==> pmember n (or a b) (x .|. y)
correct_xor :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> (BVDomain n, Integer) -> Property
correct_xor n (a,x) (b,y) = member a x ==> member b y ==> pmember n (xor a b) (x `Bits.xor` y)
correct_testBit :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> Natural -> Property
correct_testBit n (a,x) i =
i < natValue n ==>
case testBit n a i of
Just True -> Bits.testBit x (fromIntegral i)
Just False -> Prelude.not (Bits.testBit x (fromIntegral i))
Nothing -> True
correct_popcnt :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> Property
correct_popcnt n (a,x) = member a x ==> pmember n (popcnt n a) (toInteger (Bits.popCount x))
correct_ctz :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> Property
correct_ctz n (a,x) = member a x ==> pmember n (ctz n a) (Arith.ctz n x)
correct_clz :: (1 <= n) => NatRepr n -> (BVDomain n, Integer) -> Property
correct_clz n (a,x) = member a x ==> pmember n (clz n a) (Arith.clz n x)