-- | -- Module : Cryptol.Eval.SBV -- Copyright : (c) 2013-2016 Galois, Inc. -- License : BSD3 -- Maintainer : cryptol@galois.com -- Stability : provisional -- Portability : portable {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE ViewPatterns #-} module Cryptol.Eval.SBV ( primTable ) where import qualified Control.Exception as X import Control.Monad (join) import Control.Monad.IO.Class (MonadIO(..)) import Data.Bits (bit, shiftL) import qualified Data.Map as Map import qualified Data.Text as T import Data.SBV.Dynamic as SBV import Cryptol.Backend import Cryptol.Backend.Monad ( EvalError(..), Unsupported(..) ) import Cryptol.Backend.SBV import Cryptol.Eval.Type (TValue(..), finNat') import Cryptol.Eval.Generic import Cryptol.Eval.Value import Cryptol.TypeCheck.Solver.InfNat (Nat'(..), widthInteger) import Cryptol.Utils.Ident -- Values ---------------------------------------------------------------------- type Value = GenValue SBV -- Primitives ------------------------------------------------------------------ -- See also Cryptol.Eval.Concrete.primTable primTable :: SBV -> Map.Map PrimIdent Value primTable sym = Map.fromList $ map (\(n, v) -> (prelPrim (T.pack n), v)) [ -- Literals ("True" , VBit (bitLit sym True)) , ("False" , VBit (bitLit sym False)) , ("number" , ecNumberV sym) -- Converts a numeric type into its corresponding value. -- { val, rep } (Literal val rep) => rep , ("fraction" , ecFractionV sym) , ("ratio" , ratioV sym) -- Zero , ("zero" , VPoly (zeroV sym)) -- Logic , ("&&" , binary (andV sym)) , ("||" , binary (orV sym)) , ("^" , binary (xorV sym)) , ("complement" , unary (complementV sym)) -- Ring , ("fromInteger" , fromIntegerV sym) , ("+" , binary (addV sym)) , ("-" , binary (subV sym)) , ("negate" , unary (negateV sym)) , ("*" , binary (mulV sym)) -- Integral , ("toInteger" , toIntegerV sym) , ("/" , binary (divV sym)) , ("%" , binary (modV sym)) , ("^^" , expV sym) , ("infFrom" , infFromV sym) , ("infFromThen" , infFromThenV sym) -- Field , ("recip" , recipV sym) , ("/." , fieldDivideV sym) -- Round , ("floor" , unary (floorV sym)) , ("ceiling" , unary (ceilingV sym)) , ("trunc" , unary (truncV sym)) , ("roundAway" , unary (roundAwayV sym)) , ("roundToEven" , unary (roundToEvenV sym)) -- Word operations , ("/$" , sdivV sym) , ("%$" , smodV sym) , ("lg2" , lg2V sym) , (">>$" , sshrV sym) -- Cmp , ("<" , binary (lessThanV sym)) , (">" , binary (greaterThanV sym)) , ("<=" , binary (lessThanEqV sym)) , (">=" , binary (greaterThanEqV sym)) , ("==" , binary (eqV sym)) , ("!=" , binary (distinctV sym)) -- SignedCmp , ("<$" , binary (signedLessThanV sym)) -- Finite enumerations , ("fromTo" , fromToV sym) , ("fromThenTo" , fromThenToV sym) -- Sequence manipulations , ("#" , -- {a,b,d} (fin a) => [a] d -> [b] d -> [a + b] d nlam $ \ front -> nlam $ \ back -> tlam $ \ elty -> lam $ \ l -> return $ lam $ \ r -> join (ccatV sym front back elty <$> l <*> r)) , ("join" , nlam $ \ parts -> nlam $ \ (finNat' -> each) -> tlam $ \ a -> lam $ \ x -> joinV sym parts each a =<< x) , ("split" , ecSplitV sym) , ("splitAt" , nlam $ \ front -> nlam $ \ back -> tlam $ \ a -> lam $ \ x -> splitAtV sym front back a =<< x) , ("reverse" , nlam $ \_a -> tlam $ \_b -> lam $ \xs -> reverseV sym =<< xs) , ("transpose" , nlam $ \a -> nlam $ \b -> tlam $ \c -> lam $ \xs -> transposeV sym a b c =<< xs) -- Shifts and rotates , ("<<" , logicShift sym "<<" shiftShrink (\x y -> pure (shl x y)) (\x y -> pure (lshr x y)) shiftLeftReindex shiftRightReindex) , (">>" , logicShift sym ">>" shiftShrink (\x y -> pure (lshr x y)) (\x y -> pure (shl x y)) shiftRightReindex shiftLeftReindex) , ("<<<" , logicShift sym "<<<" rotateShrink (\x y -> pure (SBV.svRotateLeft x y)) (\x y -> pure (SBV.svRotateRight x y)) rotateLeftReindex rotateRightReindex) , (">>>" , logicShift sym ">>>" rotateShrink (\x y -> pure (SBV.svRotateRight x y)) (\x y -> pure (SBV.svRotateLeft x y)) rotateRightReindex rotateLeftReindex) -- Indexing and updates , ("@" , indexPrim sym (indexFront sym) (indexFront_bits sym) (indexFront sym)) , ("!" , indexPrim sym (indexBack sym) (indexBack_bits sym) (indexBack sym)) , ("update" , updatePrim sym (updateFrontSym_word sym) (updateFrontSym sym)) , ("updateEnd" , updatePrim sym (updateBackSym_word sym) (updateBackSym sym)) -- Misc , ("fromZ" , fromZV sym) , ("foldl" , foldlV sym) , ("foldl'" , foldl'V sym) , ("deepseq" , tlam $ \_a -> tlam $ \_b -> lam $ \x -> pure $ lam $ \y -> do _ <- forceValue =<< x y) , ("parmap" , parmapV sym) -- {at,len} (fin len) => [len][8] -> at , ("error" , tlam $ \a -> nlam $ \_ -> VFun $ \s -> errorV sym a =<< (valueToString sym =<< s)) , ("random" , tlam $ \a -> wlam sym $ \x -> case integerAsLit sym x of Just i -> randomV sym a i Nothing -> cryUserError sym "cannot evaluate 'random' with symbolic inputs") -- The trace function simply forces its first two -- values before returing the third in the symbolic -- evaluator. , ("trace", nlam $ \_n -> tlam $ \_a -> tlam $ \_b -> lam $ \s -> return $ lam $ \x -> return $ lam $ \y -> do _ <- s _ <- x y) ] indexFront :: SBV -> Nat' -> TValue -> SeqMap SBV -> TValue -> SVal -> SEval SBV Value indexFront sym mblen a xs _ix idx | Just i <- SBV.svAsInteger idx = lookupSeqMap xs i | Nat n <- mblen , TVSeq wlen TVBit <- a = do wvs <- traverse (fromWordVal "indexFront" =<<) (enumerateSeqMap n xs) case asWordList wvs of Just ws -> do z <- wordLit sym wlen 0 return $ VWord wlen $ pure $ WordVal $ SBV.svSelect ws z idx Nothing -> folded | otherwise = folded where k = SBV.kindOf idx def = zeroV sym a f n y = iteValue sym (SBV.svEqual idx (SBV.svInteger k n)) (lookupSeqMap xs n) y folded = case k of KBounded _ w -> case mblen of Nat n | n < 2^w -> foldr f def [0 .. n-1] _ -> foldr f def [0 .. 2^w - 1] _ -> case mblen of Nat n -> foldr f def [0 .. n-1] Inf -> liftIO (X.throw (UnsupportedSymbolicOp "unbounded integer indexing")) indexBack :: SBV -> Nat' -> TValue -> SeqMap SBV -> TValue -> SWord SBV -> SEval SBV Value indexBack sym (Nat n) a xs ix idx = indexFront sym (Nat n) a (reverseSeqMap n xs) ix idx indexBack _ Inf _ _ _ _ = evalPanic "Expected finite sequence" ["indexBack"] indexFront_bits :: SBV -> Nat' -> TValue -> SeqMap SBV -> TValue -> [SBit SBV] -> SEval SBV Value indexFront_bits sym mblen _a xs _ix bits0 = go 0 (length bits0) bits0 where go :: Integer -> Int -> [SBit SBV] -> SEval SBV Value go i _k [] -- For indices out of range, fail | Nat n <- mblen , i >= n = raiseError sym (InvalidIndex (Just i)) | otherwise = lookupSeqMap xs i go i k (b:bs) -- Fail early when all possible indices we could compute from here -- are out of bounds | Nat n <- mblen , (i `shiftL` k) >= n = raiseError sym (InvalidIndex Nothing) | otherwise = iteValue sym b (go ((i `shiftL` 1) + 1) (k-1) bs) (go (i `shiftL` 1) (k-1) bs) indexBack_bits :: SBV -> Nat' -> TValue -> SeqMap SBV -> TValue -> [SBit SBV] -> SEval SBV Value indexBack_bits sym (Nat n) a xs ix idx = indexFront_bits sym (Nat n) a (reverseSeqMap n xs) ix idx indexBack_bits _ Inf _ _ _ _ = evalPanic "Expected finite sequence" ["indexBack_bits"] -- | Compare a symbolic word value with a concrete integer. wordValueEqualsInteger :: SBV -> WordValue SBV -> Integer -> SEval SBV (SBit SBV) wordValueEqualsInteger sym wv i | wordValueSize sym wv < widthInteger i = return SBV.svFalse | otherwise = case wv of WordVal w -> return $ SBV.svEqual w (literalSWord (SBV.intSizeOf w) i) _ -> bitsAre i <$> enumerateWordValueRev sym wv -- little-endian where bitsAre :: Integer -> [SBit SBV] -> SBit SBV bitsAre n [] = SBV.svBool (n == 0) bitsAre n (b : bs) = SBV.svAnd (bitIs (odd n) b) (bitsAre (n `div` 2) bs) bitIs :: Bool -> SBit SBV -> SBit SBV bitIs b x = if b then x else SBV.svNot x updateFrontSym :: SBV -> Nat' -> TValue -> SeqMap SBV -> Either (SInteger SBV) (WordValue SBV) -> SEval SBV (GenValue SBV) -> SEval SBV (SeqMap SBV) updateFrontSym sym _len _eltTy vs (Left idx) val = case SBV.svAsInteger idx of Just i -> return $ updateSeqMap vs i val Nothing -> return $ IndexSeqMap $ \i -> do b <- intEq sym idx =<< integerLit sym i iteValue sym b val (lookupSeqMap vs i) updateFrontSym sym _len _eltTy vs (Right wv) val = case wv of WordVal w | Just j <- SBV.svAsInteger w -> return $ updateSeqMap vs j val _ -> return $ IndexSeqMap $ \i -> do b <- wordValueEqualsInteger sym wv i iteValue sym b val (lookupSeqMap vs i) updateFrontSym_word :: SBV -> Nat' -> TValue -> WordValue SBV -> Either (SInteger SBV) (WordValue SBV) -> SEval SBV (GenValue SBV) -> SEval SBV (WordValue SBV) updateFrontSym_word _ Inf _ _ _ _ = evalPanic "Expected finite sequence" ["updateFrontSym_bits"] updateFrontSym_word sym (Nat _) eltTy (LargeBitsVal n bv) idx val = LargeBitsVal n <$> updateFrontSym sym (Nat n) eltTy bv idx val updateFrontSym_word sym (Nat n) eltTy (WordVal bv) (Left idx) val = do idx' <- wordFromInt sym n idx updateFrontSym_word sym (Nat n) eltTy (WordVal bv) (Right (WordVal idx')) val updateFrontSym_word sym (Nat n) eltTy bv (Right wv) val = case wv of WordVal idx | Just j <- SBV.svAsInteger idx -> updateWordValue sym bv j (fromVBit <$> val) | WordVal bw <- bv -> WordVal <$> do b <- fromVBit <$> val let sz = SBV.intSizeOf bw let z = literalSWord sz 0 let znot = SBV.svNot z let q = SBV.svSymbolicMerge (SBV.kindOf bw) True b znot z let msk = SBV.svShiftRight (literalSWord sz (bit (sz-1))) idx let bw' = SBV.svAnd bw (SBV.svNot msk) return $! SBV.svXOr bw' (SBV.svAnd q msk) _ -> LargeBitsVal n <$> updateFrontSym sym (Nat n) eltTy (asBitsMap sym bv) (Right wv) val updateBackSym :: SBV -> Nat' -> TValue -> SeqMap SBV -> Either (SInteger SBV) (WordValue SBV) -> SEval SBV (GenValue SBV) -> SEval SBV (SeqMap SBV) updateBackSym _ Inf _ _ _ _ = evalPanic "Expected finite sequence" ["updateBackSym"] updateBackSym sym (Nat n) _eltTy vs (Left idx) val = case SBV.svAsInteger idx of Just i -> return $ updateSeqMap vs (n - 1 - i) val Nothing -> return $ IndexSeqMap $ \i -> do b <- intEq sym idx =<< integerLit sym (n - 1 - i) iteValue sym b val (lookupSeqMap vs i) updateBackSym sym (Nat n) _eltTy vs (Right wv) val = case wv of WordVal w | Just j <- SBV.svAsInteger w -> return $ updateSeqMap vs (n - 1 - j) val _ -> return $ IndexSeqMap $ \i -> do b <- wordValueEqualsInteger sym wv (n - 1 - i) iteValue sym b val (lookupSeqMap vs i) updateBackSym_word :: SBV -> Nat' -> TValue -> WordValue SBV -> Either (SInteger SBV) (WordValue SBV) -> SEval SBV (GenValue SBV) -> SEval SBV (WordValue SBV) updateBackSym_word _ Inf _ _ _ _ = evalPanic "Expected finite sequence" ["updateBackSym_bits"] updateBackSym_word sym (Nat _) eltTy (LargeBitsVal n bv) idx val = LargeBitsVal n <$> updateBackSym sym (Nat n) eltTy bv idx val updateBackSym_word sym (Nat n) eltTy (WordVal bv) (Left idx) val = do idx' <- wordFromInt sym n idx updateBackSym_word sym (Nat n) eltTy (WordVal bv) (Right (WordVal idx')) val updateBackSym_word sym (Nat n) eltTy bv (Right wv) val = do case wv of WordVal idx | Just j <- SBV.svAsInteger idx -> updateWordValue sym bv (n - 1 - j) (fromVBit <$> val) | WordVal bw <- bv -> WordVal <$> do b <- fromVBit <$> val let sz = SBV.intSizeOf bw let z = literalSWord sz 0 let znot = SBV.svNot z let q = SBV.svSymbolicMerge (SBV.kindOf bw) True b znot z let msk = SBV.svShiftLeft (literalSWord sz 1) idx let bw' = SBV.svAnd bw (SBV.svNot msk) return $! SBV.svXOr bw' (SBV.svAnd q msk) _ -> LargeBitsVal n <$> updateBackSym sym (Nat n) eltTy (asBitsMap sym bv) (Right wv) val asWordList :: [WordValue SBV] -> Maybe [SWord SBV] asWordList = go id where go :: ([SWord SBV] -> [SWord SBV]) -> [WordValue SBV] -> Maybe [SWord SBV] go f [] = Just (f []) go f (WordVal x :vs) = go (f . (x:)) vs go _f (LargeBitsVal _ _ : _) = Nothing sshrV :: SBV -> Value sshrV sym = nlam $ \n -> tlam $ \ix -> wlam sym $ \x -> return $ lam $ \y -> y >>= asIndex sym ">>$" ix >>= \case Left idx -> do let w = toInteger (SBV.intSizeOf x) let pneg = svLessThan idx (svInteger KUnbounded 0) zneg <- shl x . svFromInteger w <$> shiftShrink sym n ix (SBV.svUNeg idx) zpos <- ashr x . svFromInteger w <$> shiftShrink sym n ix idx let z = svSymbolicMerge (kindOf x) True pneg zneg zpos return . VWord w . pure . WordVal $ z Right wv -> do z <- ashr x <$> asWordVal sym wv return . VWord (toInteger (SBV.intSizeOf x)) . pure . WordVal $ z