{-| 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)