{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DoAndIfThenElse #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
module Cryptol.Eval.Value
(
GenValue(..)
, forceWordValue
, forceValue
, Backend(..)
, asciiMode
, word
, lam
, wlam
, flam
, tlam
, nlam
, ilam
, toStream
, toFinSeq
, toSeq
, mkSeq
, fromVBit
, fromVInteger
, fromVRational
, fromVFloat
, fromVSeq
, fromSeq
, fromWordVal
, asIndex
, fromVWord
, vWordLen
, tryFromBits
, fromVFun
, fromVPoly
, fromVNumPoly
, fromVTuple
, fromVRecord
, lookupRecord
, defaultPPOpts
, ppValue
, SeqMap (..)
, lookupSeqMap
, finiteSeqMap
, infiniteSeqMap
, enumerateSeqMap
, streamSeqMap
, reverseSeqMap
, updateSeqMap
, dropSeqMap
, concatSeqMap
, splitSeqMap
, memoMap
, zipSeqMap
, mapSeqMap
, largeBitSize
, WordValue(..)
, asWordVal
, asBitsMap
, enumerateWordValue
, enumerateWordValueRev
, wordValueSize
, indexWordValue
, updateWordValue
) where
import Control.Monad.IO.Class
import Data.Bits
import Data.IORef
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import MonadLib
import qualified Cryptol.Eval.Arch as Arch
import Cryptol.Eval.Backend
import Cryptol.Eval.Monad
import Cryptol.Eval.Type
import Cryptol.TypeCheck.Solver.InfNat(Nat'(..))
import Cryptol.Utils.Ident (Ident)
import Cryptol.Utils.Panic(panic)
import Cryptol.Utils.PP
import Cryptol.Utils.RecordMap
import Data.List(genericIndex)
import GHC.Generics (Generic)
data SeqMap sym
= IndexSeqMap !(Integer -> SEval sym (GenValue sym))
| UpdateSeqMap !(Map Integer (SEval sym (GenValue sym)))
!(Integer -> SEval sym (GenValue sym))
lookupSeqMap :: SeqMap sym -> Integer -> SEval sym (GenValue sym)
lookupSeqMap (IndexSeqMap f) i = f i
lookupSeqMap (UpdateSeqMap m f) i =
case Map.lookup i m of
Just x -> x
Nothing -> f i
largeBitSize :: Integer
largeBitSize = 1 `shiftL` 16
finiteSeqMap :: Backend sym => sym -> [SEval sym (GenValue sym)] -> SeqMap sym
finiteSeqMap sym xs =
UpdateSeqMap
(Map.fromList (zip [0..] xs))
(invalidIndex sym)
infiniteSeqMap :: Backend sym => [SEval sym (GenValue sym)] -> SEval sym (SeqMap sym)
infiniteSeqMap xs =
memoMap (IndexSeqMap $ \i -> genericIndex xs i)
enumerateSeqMap :: (Integral n) => n -> SeqMap sym -> [SEval sym (GenValue sym)]
enumerateSeqMap n m = [ lookupSeqMap m i | i <- [0 .. (toInteger n)-1] ]
streamSeqMap :: SeqMap sym -> [SEval sym (GenValue sym)]
streamSeqMap m = [ lookupSeqMap m i | i <- [0..] ]
reverseSeqMap :: Integer
-> SeqMap sym
-> SeqMap sym
reverseSeqMap n vals = IndexSeqMap $ \i -> lookupSeqMap vals (n - 1 - i)
updateSeqMap :: SeqMap sym -> Integer -> SEval sym (GenValue sym) -> SeqMap sym
updateSeqMap (UpdateSeqMap m sm) i x = UpdateSeqMap (Map.insert i x m) sm
updateSeqMap (IndexSeqMap f) i x = UpdateSeqMap (Map.singleton i x) f
concatSeqMap :: Integer -> SeqMap sym -> SeqMap sym -> SeqMap sym
concatSeqMap n x y =
IndexSeqMap $ \i ->
if i < n
then lookupSeqMap x i
else lookupSeqMap y (i-n)
splitSeqMap :: Integer -> SeqMap sym -> (SeqMap sym, SeqMap sym)
splitSeqMap n xs = (hd,tl)
where
hd = xs
tl = IndexSeqMap $ \i -> lookupSeqMap xs (i+n)
dropSeqMap :: Integer -> SeqMap sym -> SeqMap sym
dropSeqMap 0 xs = xs
dropSeqMap n xs = IndexSeqMap $ \i -> lookupSeqMap xs (i+n)
memoMap :: (MonadIO m, Backend sym) => SeqMap sym -> m (SeqMap sym)
memoMap x = do
cache <- liftIO $ newIORef $ Map.empty
return $ IndexSeqMap (memo cache)
where
memo cache i = do
mz <- liftIO (Map.lookup i <$> readIORef cache)
case mz of
Just z -> return z
Nothing -> doEval cache i
doEval cache i = do
v <- lookupSeqMap x i
liftIO $ modifyIORef' cache (Map.insert i v)
return v
zipSeqMap ::
Backend sym =>
(GenValue sym -> GenValue sym -> SEval sym (GenValue sym)) ->
SeqMap sym ->
SeqMap sym ->
SEval sym (SeqMap sym)
zipSeqMap f x y =
memoMap (IndexSeqMap $ \i -> join (f <$> lookupSeqMap x i <*> lookupSeqMap y i))
mapSeqMap ::
Backend sym =>
(GenValue sym -> SEval sym (GenValue sym)) ->
SeqMap sym -> SEval sym (SeqMap sym)
mapSeqMap f x =
memoMap (IndexSeqMap $ \i -> f =<< lookupSeqMap x i)
data WordValue sym
= WordVal !(SWord sym)
| LargeBitsVal !Integer !(SeqMap sym)
deriving (Generic)
asWordVal :: Backend sym => sym -> WordValue sym -> SEval sym (SWord sym)
asWordVal _ (WordVal w) = return w
asWordVal sym (LargeBitsVal n xs) = packWord sym =<< traverse (fromVBit <$>) (enumerateSeqMap n xs)
asBitsMap :: Backend sym => sym -> WordValue sym -> SeqMap sym
asBitsMap sym (WordVal w) = IndexSeqMap $ \i -> VBit <$> (wordBit sym w i)
asBitsMap _ (LargeBitsVal _ xs) = xs
enumerateWordValue :: Backend sym => sym -> WordValue sym -> SEval sym [SBit sym]
enumerateWordValue sym (WordVal w) = unpackWord sym w
enumerateWordValue _ (LargeBitsVal n xs) = traverse (fromVBit <$>) (enumerateSeqMap n xs)
enumerateWordValueRev :: Backend sym => sym -> WordValue sym -> SEval sym [SBit sym]
enumerateWordValueRev sym (WordVal w) = reverse <$> unpackWord sym w
enumerateWordValueRev _ (LargeBitsVal n xs) = traverse (fromVBit <$>) (enumerateSeqMap n (reverseSeqMap n xs))
wordValueSize :: Backend sym => sym -> WordValue sym -> Integer
wordValueSize sym (WordVal w) = wordLen sym w
wordValueSize _ (LargeBitsVal n _) = n
indexWordValue :: Backend sym => sym -> WordValue sym -> Integer -> SEval sym (SBit sym)
indexWordValue sym (WordVal w) idx
| 0 <= idx && idx < wordLen sym w = wordBit sym w idx
| otherwise = invalidIndex sym idx
indexWordValue sym (LargeBitsVal n xs) idx
| 0 <= idx && idx < n = fromVBit <$> lookupSeqMap xs idx
| otherwise = invalidIndex sym idx
updateWordValue :: Backend sym => sym -> WordValue sym -> Integer -> SEval sym (SBit sym) -> SEval sym (WordValue sym)
updateWordValue sym (WordVal w) idx b
| idx < 0 || idx >= wordLen sym w = invalidIndex sym idx
| isReady sym b = WordVal <$> (wordUpdate sym w idx =<< b)
updateWordValue sym wv idx b
| 0 <= idx && idx < wordValueSize sym wv =
pure $ LargeBitsVal (wordValueSize sym wv) $ updateSeqMap (asBitsMap sym wv) idx (VBit <$> b)
| otherwise = invalidIndex sym idx
data GenValue sym
= VRecord !(RecordMap Ident (SEval sym (GenValue sym)))
| VTuple ![SEval sym (GenValue sym)]
| VBit !(SBit sym)
| VInteger !(SInteger sym)
| VRational !(SRational sym)
| VFloat !(SFloat sym)
| VSeq !Integer !(SeqMap sym)
| VWord !Integer !(SEval sym (WordValue sym))
| VStream !(SeqMap sym)
| VFun (SEval sym (GenValue sym) -> SEval sym (GenValue sym))
| VPoly (TValue -> SEval sym (GenValue sym))
| VNumPoly (Nat' -> SEval sym (GenValue sym))
deriving Generic
forceWordValue :: Backend sym => WordValue sym -> SEval sym ()
forceWordValue (WordVal w) = seq w (return ())
forceWordValue (LargeBitsVal n xs) = mapM_ (\x -> const () <$> x) (enumerateSeqMap n xs)
forceValue :: Backend sym => GenValue sym -> SEval sym ()
forceValue v = case v of
VRecord fs -> mapM_ (forceValue =<<) fs
VTuple xs -> mapM_ (forceValue =<<) xs
VSeq n xs -> mapM_ (forceValue =<<) (enumerateSeqMap n xs)
VBit b -> seq b (return ())
VInteger i -> seq i (return ())
VRational q -> seq q (return ())
VFloat f -> seq f (return ())
VWord _ wv -> forceWordValue =<< wv
VStream _ -> return ()
VFun _ -> return ()
VPoly _ -> return ()
VNumPoly _ -> return ()
instance Backend sym => Show (GenValue sym) where
show v = case v of
VRecord fs -> "record:" ++ show (displayOrder fs)
VTuple xs -> "tuple:" ++ show (length xs)
VBit _ -> "bit"
VInteger _ -> "integer"
VRational _ -> "rational"
VFloat _ -> "float"
VSeq n _ -> "seq:" ++ show n
VWord n _ -> "word:" ++ show n
VStream _ -> "stream"
VFun _ -> "fun"
VPoly _ -> "poly"
VNumPoly _ -> "numpoly"
ppValue :: forall sym.
Backend sym =>
sym ->
PPOpts ->
GenValue sym ->
SEval sym Doc
ppValue x opts = loop
where
loop :: GenValue sym -> SEval sym Doc
loop val = case val of
VRecord fs -> do fs' <- traverse (>>= loop) fs
return $ braces (sep (punctuate comma (map ppField (displayFields fs'))))
where
ppField (f,r) = pp f <+> char '=' <+> r
VTuple vals -> do vals' <- traverse (>>=loop) vals
return $ parens (sep (punctuate comma vals'))
VBit b -> return $ ppBit x b
VInteger i -> return $ ppInteger x opts i
VRational q -> return $ ppRational x opts q
VFloat i -> return $ ppFloat x opts i
VSeq sz vals -> ppWordSeq sz vals
VWord _ wv -> ppWordVal =<< wv
VStream vals -> do vals' <- traverse (>>=loop) $ enumerateSeqMap (useInfLength opts) vals
return $ brackets $ fsep
$ punctuate comma
( vals' ++ [text "..."]
)
VFun _ -> return $ text "<function>"
VPoly _ -> return $ text "<polymorphic value>"
VNumPoly _ -> return $ text "<polymorphic value>"
ppWordVal :: WordValue sym -> SEval sym Doc
ppWordVal w = ppWord x opts <$> asWordVal x w
ppWordSeq :: Integer -> SeqMap sym -> SEval sym Doc
ppWordSeq sz vals = do
ws <- sequence (enumerateSeqMap sz vals)
case ws of
w : _
| Just l <- vWordLen w
, asciiMode opts l
-> do vs <- traverse (fromVWord x "ppWordSeq") ws
case traverse (wordAsChar x) vs of
Just str -> return $ text (show str)
_ -> return $ brackets (fsep (punctuate comma $ map (ppWord x opts) vs))
_ -> do ws' <- traverse loop ws
return $ brackets (fsep (punctuate comma ws'))
asciiMode :: PPOpts -> Integer -> Bool
asciiMode opts width = useAscii opts && (width == 7 || width == 8)
word :: Backend sym => sym -> Integer -> Integer -> GenValue sym
word sym n i
| n >= Arch.maxBigIntWidth = wordTooWide n
| otherwise = VWord n (WordVal <$> wordLit sym n i)
lam :: (SEval sym (GenValue sym) -> SEval sym (GenValue sym)) -> GenValue sym
lam = VFun
wlam :: Backend sym => sym -> (SWord sym -> SEval sym (GenValue sym)) -> GenValue sym
wlam sym f = VFun (\arg -> arg >>= fromVWord sym "wlam" >>= f)
flam :: Backend sym =>
(SFloat sym -> SEval sym (GenValue sym)) -> GenValue sym
flam f = VFun (\arg -> arg >>= f . fromVFloat)
tlam :: Backend sym => (TValue -> GenValue sym) -> GenValue sym
tlam f = VPoly (return . f)
nlam :: Backend sym => (Nat' -> GenValue sym) -> GenValue sym
nlam f = VNumPoly (return . f)
ilam :: Backend sym => (Integer -> GenValue sym) -> GenValue sym
ilam f = nlam (\n -> case n of
Nat i -> f i
Inf -> panic "ilam" [ "Unexpected `inf`" ])
toStream :: Backend sym => [GenValue sym] -> SEval sym (GenValue sym)
toStream vs =
VStream <$> infiniteSeqMap (map pure vs)
toFinSeq ::
Backend sym =>
sym -> Integer -> TValue -> [GenValue sym] -> GenValue sym
toFinSeq sym len elty vs
| isTBit elty = VWord len (WordVal <$> packWord sym (map fromVBit vs))
| otherwise = VSeq len $ finiteSeqMap sym (map pure vs)
toSeq ::
Backend sym =>
sym -> Nat' -> TValue -> [GenValue sym] -> SEval sym (GenValue sym)
toSeq sym len elty vals = case len of
Nat n -> return $ toFinSeq sym n elty vals
Inf -> toStream vals
mkSeq :: Backend sym => Nat' -> TValue -> SeqMap sym -> GenValue sym
mkSeq len elty vals = case len of
Nat n
| isTBit elty -> VWord n $ pure $ LargeBitsVal n vals
| otherwise -> VSeq n vals
Inf -> VStream vals
fromVBit :: GenValue sym -> SBit sym
fromVBit val = case val of
VBit b -> b
_ -> evalPanic "fromVBit" ["not a Bit"]
fromVInteger :: GenValue sym -> SInteger sym
fromVInteger val = case val of
VInteger i -> i
_ -> evalPanic "fromVInteger" ["not an Integer"]
fromVRational :: GenValue sym -> SRational sym
fromVRational val = case val of
VRational q -> q
_ -> evalPanic "fromVRational" ["not a Rational"]
fromVSeq :: GenValue sym -> SeqMap sym
fromVSeq val = case val of
VSeq _ vs -> vs
_ -> evalPanic "fromVSeq" ["not a sequence"]
fromSeq :: Backend sym => String -> GenValue sym -> SEval sym (SeqMap sym)
fromSeq msg val = case val of
VSeq _ vs -> return vs
VStream vs -> return vs
_ -> evalPanic "fromSeq" ["not a sequence", msg]
fromWordVal :: Backend sym => String -> GenValue sym -> SEval sym (WordValue sym)
fromWordVal _msg (VWord _ wval) = wval
fromWordVal msg _ = evalPanic "fromWordVal" ["not a word value", msg]
asIndex :: Backend sym =>
sym -> String -> TValue -> GenValue sym -> SEval sym (Either (SInteger sym) (WordValue sym))
asIndex _sym _msg TVInteger (VInteger i) = pure (Left i)
asIndex _sym _msg _ (VWord _ wval) = Right <$> wval
asIndex _sym msg _ _ = evalPanic "asIndex" ["not an index value", msg]
fromVWord :: Backend sym => sym -> String -> GenValue sym -> SEval sym (SWord sym)
fromVWord sym _msg (VWord _ wval) = wval >>= asWordVal sym
fromVWord _ msg _ = evalPanic "fromVWord" ["not a word", msg]
vWordLen :: Backend sym => GenValue sym -> Maybe Integer
vWordLen val = case val of
VWord n _wv -> Just n
_ -> Nothing
tryFromBits :: Backend sym => sym -> [SEval sym (GenValue sym)] -> Maybe (SEval sym (SWord sym))
tryFromBits sym = go id
where
go f [] = Just (packWord sym =<< sequence (f []))
go f (v : vs) | isReady sym v = go (f . ((fromVBit <$> v):)) vs
go _ (_ : _) = Nothing
fromVFun :: GenValue sym -> (SEval sym (GenValue sym) -> SEval sym (GenValue sym))
fromVFun val = case val of
VFun f -> f
_ -> evalPanic "fromVFun" ["not a function"]
fromVPoly :: GenValue sym -> (TValue -> SEval sym (GenValue sym))
fromVPoly val = case val of
VPoly f -> f
_ -> evalPanic "fromVPoly" ["not a polymorphic value"]
fromVNumPoly :: GenValue sym -> (Nat' -> SEval sym (GenValue sym))
fromVNumPoly val = case val of
VNumPoly f -> f
_ -> evalPanic "fromVNumPoly" ["not a polymorphic value"]
fromVTuple :: GenValue sym -> [SEval sym (GenValue sym)]
fromVTuple val = case val of
VTuple vs -> vs
_ -> evalPanic "fromVTuple" ["not a tuple"]
fromVRecord :: GenValue sym -> RecordMap Ident (SEval sym (GenValue sym))
fromVRecord val = case val of
VRecord fs -> fs
_ -> evalPanic "fromVRecord" ["not a record"]
fromVFloat :: GenValue sym -> SFloat sym
fromVFloat val =
case val of
VFloat x -> x
_ -> evalPanic "fromVFloat" ["not a Float"]
lookupRecord :: Ident -> GenValue sym -> SEval sym (GenValue sym)
lookupRecord f val =
case lookupField f (fromVRecord val) of
Just x -> x
Nothing -> evalPanic "lookupRecord" ["malformed record"]