{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
module Cryptol.Eval.Concrete
( module Cryptol.Eval.Concrete.Value
, evalPrim
, toExpr
) where
import Control.Monad (join, guard, zipWithM)
import Data.Bits (Bits(..))
import Data.Ratio(numerator,denominator)
import MonadLib( ChoiceT, findOne, lift )
import qualified LibBF as FP
import qualified Data.Map.Strict as Map
import Cryptol.TypeCheck.Solver.InfNat (Nat'(..))
import Cryptol.Eval.Backend
import Cryptol.Eval.Concrete.Float(floatPrims)
import Cryptol.Eval.Concrete.FloatHelpers(bfValue)
import Cryptol.Eval.Concrete.Value
import Cryptol.Eval.Generic hiding (logicShift)
import Cryptol.Eval.Monad
import Cryptol.Eval.Type
import Cryptol.Eval.Value
import Cryptol.ModuleSystem.Name
import Cryptol.Testing.Random (randomV)
import Cryptol.TypeCheck.AST as AST
import Cryptol.Utils.Panic (panic)
import Cryptol.Utils.Ident (PrimIdent,prelPrim,floatPrim)
import Cryptol.Utils.PP
import Cryptol.Utils.Logger(logPrint)
import Cryptol.Utils.RecordMap
toExpr :: PrimMap -> AST.Type -> Value -> Eval (Maybe AST.Expr)
toExpr prims t0 v0 = findOne (go t0 v0)
where
prim n = ePrim prims (prelPrim n)
go :: AST.Type -> Value -> ChoiceT Eval Expr
go ty val =
case val of
VRecord vfs ->
do tfs <- maybe mismatch pure (tIsRec ty)
res <- zipRecordsM (\_lbl v t -> go t =<< lift v) vfs tfs
case res of
Left _ -> mismatch
Right efs -> pure (ERec efs)
VTuple tvs ->
do ts <- maybe mismatch pure (tIsTuple ty)
guard (length ts == length tvs)
ETuple <$> (zipWithM go ts =<< lift (sequence tvs))
VBit b ->
pure (prim (if b then "True" else "False"))
VInteger i ->
pure $ ETApp (ETApp (prim "number") (tNum i)) ty
VRational (SRational n d) ->
do let n' = ETApp (ETApp (prim "number") (tNum n)) tInteger
let d' = ETApp (ETApp (prim "number") (tNum d)) tInteger
pure $ EApp (EApp (prim "ratio") n') d'
VFloat i ->
do (eT, pT) <- maybe mismatch pure (tIsFloat ty)
pure (floatToExpr prims eT pT (bfValue i))
VSeq n svs ->
do (_a, b) <- maybe mismatch pure (tIsSeq ty)
ses <- traverse (go b) =<< lift (sequence (enumerateSeqMap n svs))
pure $ EList ses b
VWord _ wval ->
do BV _ v <- lift (asWordVal Concrete =<< wval)
pure $ ETApp (ETApp (prim "number") (tNum v)) ty
VStream _ -> fail "cannot construct infinite expressions"
VFun _ -> fail "cannot convert function values to expressions"
VPoly _ -> fail "cannot convert polymorphic values to expressions"
VNumPoly _ -> fail "cannot convert polymorphic values to expressions"
where
mismatch :: forall a. ChoiceT Eval a
mismatch =
do doc <- lift (ppValue Concrete defaultPPOpts val)
panic "Cryptol.Eval.Concrete.toExpr"
["type mismatch:"
, pretty ty
, render doc
]
floatToExpr :: PrimMap -> AST.Type -> AST.Type -> FP.BigFloat -> AST.Expr
floatToExpr prims eT pT f =
case FP.bfToRep f of
FP.BFNaN -> mkP "fpNaN"
FP.BFRep sign num ->
case (sign,num) of
(FP.Pos, FP.Zero) -> mkP "fpPosZero"
(FP.Neg, FP.Zero) -> mkP "fpNegZero"
(FP.Pos, FP.Inf) -> mkP "fpPosInf"
(FP.Neg, FP.Inf) -> mkP "fpNegInf"
(_, FP.Num m e) ->
let r = toRational m * (2 ^^ e)
in EProofApp $ ePrim prims (prelPrim "fraction")
`ETApp` tNum (numerator r)
`ETApp` tNum (denominator r)
`ETApp` tNum (0 :: Int)
`ETApp` tFloat eT pT
where
mkP n = EProofApp $ ePrim prims (floatPrim n) `ETApp` eT `ETApp` pT
evalPrim :: PrimIdent -> Maybe Value
evalPrim prim = Map.lookup prim primTable
primTable :: Map.Map PrimIdent Value
primTable = let sym = Concrete in
Map.union (floatPrims sym) $
Map.fromList $ map (\(n, v) -> (prelPrim n, v))
[
("True" , VBit (bitLit sym True))
, ("False" , VBit (bitLit sym False))
, ("number" , {-# SCC "Prelude::number" #-}
ecNumberV sym)
, ("ratio" , {-# SCC "Prelude::ratio" #-}
ratioV sym)
, ("fraction" , ecFractionV sym)
, ("zero" , {-# SCC "Prelude::zero" #-}
VPoly (zeroV sym))
, ("&&" , {-# SCC "Prelude::(&&)" #-}
binary (andV sym))
, ("||" , {-# SCC "Prelude::(||)" #-}
binary (orV sym))
, ("^" , {-# SCC "Prelude::(^)" #-}
binary (xorV sym))
, ("complement" , {-# SCC "Prelude::complement" #-}
unary (complementV sym))
, ("fromInteger", {-# SCC "Prelude::fromInteger" #-}
fromIntegerV sym)
, ("+" , {-# SCC "Prelude::(+)" #-}
binary (addV sym))
, ("-" , {-# SCC "Prelude::(-)" #-}
binary (subV sym))
, ("*" , {-# SCC "Prelude::(*)" #-}
binary (mulV sym))
, ("negate" , {-# SCC "Prelude::negate" #-}
unary (negateV sym))
, ("toInteger" , {-# SCC "Prelude::toInteger" #-}
toIntegerV sym)
, ("/" , {-# SCC "Prelude::(/)" #-}
binary (divV sym))
, ("%" , {-# SCC "Prelude::(%)" #-}
binary (modV sym))
, ("^^" , {-# SCC "Prelude::(^^)" #-}
expV sym)
, ("infFrom" , {-# SCC "Prelude::infFrom" #-}
infFromV sym)
, ("infFromThen", {-# SCC "Prelude::infFromThen" #-}
infFromThenV sym)
, ("recip" , {-# SCC "Prelude::recip" #-}
recipV sym)
, ("/." , {-# SCC "Prelude::(/.)" #-}
fieldDivideV sym)
, ("floor" , {-# SCC "Prelude::floor" #-}
unary (floorV sym))
, ("ceiling" , {-# SCC "Prelude::ceiling" #-}
unary (ceilingV sym))
, ("trunc" , {-# SCC "Prelude::trunc" #-}
unary (truncV sym))
, ("roundAway" , {-# SCC "Prelude::roundAway" #-}
unary (roundAwayV sym))
, ("roundToEven", {-# SCC "Prelude::roundToEven" #-}
unary (roundToEvenV sym))
, ("/$" , {-# SCC "Prelude::(/$)" #-}
sdivV sym)
, ("%$" , {-# SCC "Prelude::(%$)" #-}
smodV sym)
, ("lg2" , {-# SCC "Prelude::lg2" #-}
lg2V sym)
, (">>$" , {-# SCC "Prelude::(>>$)" #-}
sshrV)
, ("<" , {-# SCC "Prelude::(<)" #-}
binary (lessThanV sym))
, (">" , {-# SCC "Prelude::(>)" #-}
binary (greaterThanV sym))
, ("<=" , {-# SCC "Prelude::(<=)" #-}
binary (lessThanEqV sym))
, (">=" , {-# SCC "Prelude::(>=)" #-}
binary (greaterThanEqV sym))
, ("==" , {-# SCC "Prelude::(==)" #-}
binary (eqV sym))
, ("!=" , {-# SCC "Prelude::(!=)" #-}
binary (distinctV sym))
, ("<$" , {-# SCC "Prelude::(<$)" #-}
binary (signedLessThanV sym))
, ("fromTo" , {-# SCC "Prelude::fromTo" #-}
fromToV sym)
, ("fromThenTo" , {-# SCC "Prelude::fromThenTo" #-}
fromThenToV sym)
, ("#" , {-# SCC "Prelude::(#)" #-}
nlam $ \ front ->
nlam $ \ back ->
tlam $ \ elty ->
lam $ \ l -> return $
lam $ \ r -> join (ccatV sym front back elty <$> l <*> r))
, ("join" , {-# SCC "Prelude::join" #-}
nlam $ \ parts ->
nlam $ \ (finNat' -> each) ->
tlam $ \ a ->
lam $ \ x ->
joinV sym parts each a =<< x)
, ("split" , {-# SCC "Prelude::split" #-}
ecSplitV sym)
, ("splitAt" , {-# SCC "Prelude::splitAt" #-}
nlam $ \ front ->
nlam $ \ back ->
tlam $ \ a ->
lam $ \ x ->
splitAtV sym front back a =<< x)
, ("reverse" , {-# SCC "Prelude::reverse" #-}
nlam $ \_a ->
tlam $ \_b ->
lam $ \xs -> reverseV sym =<< xs)
, ("transpose" , {-# SCC "Prelude::transpose" #-}
nlam $ \a ->
nlam $ \b ->
tlam $ \c ->
lam $ \xs -> transposeV sym a b c =<< xs)
, ("<<" , {-# SCC "Prelude::(<<)" #-}
logicShift shiftLW shiftLS)
, (">>" , {-# SCC "Prelude::(>>)" #-}
logicShift shiftRW shiftRS)
, ("<<<" , {-# SCC "Prelude::(<<<)" #-}
logicShift rotateLW rotateLS)
, (">>>" , {-# SCC "Prelude::(>>>)" #-}
logicShift rotateRW rotateRS)
, ("@" , {-# SCC "Prelude::(@)" #-}
indexPrim sym indexFront_int indexFront_bits indexFront)
, ("!" , {-# SCC "Prelude::(!)" #-}
indexPrim sym indexBack_int indexBack_bits indexBack)
, ("update" , {-# SCC "Prelude::update" #-}
updatePrim sym updateFront_word updateFront)
, ("updateEnd" , {-# SCC "Prelude::updateEnd" #-}
updatePrim sym updateBack_word updateBack)
, ("parmap" , {-# SCC "Prelude::parmap" #-}
parmapV sym)
, ("fromZ" , {-# SCC "Prelude::fromZ" #-}
fromZV sym)
, ("error" , {-# SCC "Prelude::error" #-}
tlam $ \a ->
nlam $ \_ ->
lam $ \s -> errorV sym a =<< (valueToString sym =<< s))
, ("random" , {-# SCC "Prelude::random" #-}
tlam $ \a ->
wlam sym $ \(bvVal -> x) -> randomV sym a x)
, ("trace" , {-# SCC "Prelude::trace" #-}
nlam $ \_n ->
tlam $ \_a ->
tlam $ \_b ->
lam $ \s -> return $
lam $ \x -> return $
lam $ \y -> do
msg <- valueToString sym =<< s
EvalOpts { evalPPOpts, evalLogger } <- getEvalOpts
doc <- ppValue sym evalPPOpts =<< x
yv <- y
io $ logPrint evalLogger
$ if null msg then doc else text msg <+> doc
return yv)
]
sshrV :: Value
sshrV =
nlam $ \_n ->
tlam $ \ix ->
wlam Concrete $ \(BV w x) -> return $
lam $ \y ->
do idx <- y >>= asIndex Concrete ">>$" ix >>= \case
Left idx -> pure idx
Right wv -> bvVal <$> asWordVal Concrete wv
return $ VWord w $ pure $ WordVal $ mkBv w $ signedShiftRW w x idx
logicShift :: (Integer -> Integer -> Integer -> Integer)
-> (Nat' -> TValue -> SeqMap Concrete -> Integer -> SeqMap Concrete)
-> Value
logicShift opW opS
= nlam $ \ a ->
tlam $ \ _ix ->
tlam $ \ c ->
lam $ \ l -> return $
lam $ \ r -> do
i <- r >>= \case
VInteger i -> pure i
VWord _ wval -> bvVal <$> (asWordVal Concrete =<< wval)
_ -> evalPanic "logicShift" ["not an index"]
l >>= \case
VWord w wv -> return $ VWord w $ wv >>= \case
WordVal (BV _ x) -> return $ WordVal (BV w (opW w x i))
LargeBitsVal n xs -> return $ LargeBitsVal n $ opS (Nat n) c xs i
_ -> mkSeq a c <$> (opS a c <$> (fromSeq "logicShift" =<< l) <*> return i)
shiftLW :: Integer -> Integer -> Integer -> Integer
shiftLW w ival by
| by < 0 = shiftRW w ival (negate by)
| by >= w = 0
| by > toInteger (maxBound :: Int) = panic "shiftLW" ["Shift amount too large", show by]
| otherwise = mask w (shiftL ival (fromInteger by))
shiftRW :: Integer -> Integer -> Integer -> Integer
shiftRW w ival by
| by < 0 = shiftLW w ival (negate by)
| by >= w = 0
| by > toInteger (maxBound :: Int) = panic "shiftRW" ["Shift amount too large", show by]
| otherwise = shiftR ival (fromInteger by)
signedShiftRW :: Integer -> Integer -> Integer -> Integer
signedShiftRW w ival by
| by < 0 = shiftLW w ival (negate by)
| otherwise =
let by' = min w by in
if by' > toInteger (maxBound :: Int) then
panic "signedShiftRW" ["Shift amount too large", show by]
else
shiftR (signedValue w ival) (fromInteger by')
shiftLS :: Nat' -> TValue -> SeqMap Concrete -> Integer -> SeqMap Concrete
shiftLS w ety vs by
| by < 0 = shiftRS w ety vs (negate by)
shiftLS w ety vs by = IndexSeqMap $ \i ->
case w of
Nat len
| i+by < len -> lookupSeqMap vs (i+by)
| i < len -> zeroV Concrete ety
| otherwise -> evalPanic "shiftLS" ["Index out of bounds"]
Inf -> lookupSeqMap vs (i+by)
shiftRS :: Nat' -> TValue -> SeqMap Concrete -> Integer -> SeqMap Concrete
shiftRS w ety vs by
| by < 0 = shiftLS w ety vs (negate by)
shiftRS w ety vs by = IndexSeqMap $ \i ->
case w of
Nat len
| i >= by -> lookupSeqMap vs (i-by)
| i < len -> zeroV Concrete ety
| otherwise -> evalPanic "shiftLS" ["Index out of bounds"]
Inf
| i >= by -> lookupSeqMap vs (i-by)
| otherwise -> zeroV Concrete ety
rotateLW :: Integer -> Integer -> Integer -> Integer
rotateLW 0 i _ = i
rotateLW w i by = mask w $ (i `shiftL` b) .|. (i `shiftR` (fromInteger w - b))
where b = fromInteger (by `mod` w)
rotateLS :: Nat' -> TValue -> SeqMap Concrete -> Integer -> SeqMap Concrete
rotateLS w _ vs by = IndexSeqMap $ \i ->
case w of
Nat len -> lookupSeqMap vs ((by + i) `mod` len)
_ -> panic "Cryptol.Eval.Prim.rotateLS" [ "unexpected infinite sequence" ]
rotateRW :: Integer -> Integer -> Integer -> Integer
rotateRW 0 i _ = i
rotateRW w i by = mask w $ (i `shiftR` b) .|. (i `shiftL` (fromInteger w - b))
where b = fromInteger (by `mod` w)
rotateRS :: Nat' -> TValue -> SeqMap Concrete -> Integer -> SeqMap Concrete
rotateRS w _ vs by = IndexSeqMap $ \i ->
case w of
Nat len -> lookupSeqMap vs ((len - by + i) `mod` len)
_ -> panic "Cryptol.Eval.Prim.rotateRS" [ "unexpected infinite sequence" ]
indexFront :: Nat' -> TValue -> SeqMap Concrete -> TValue -> BV -> Eval Value
indexFront _mblen _a vs _ix (bvVal -> ix) = lookupSeqMap vs ix
indexFront_bits :: Nat' -> TValue -> SeqMap Concrete -> TValue -> [Bool] -> Eval Value
indexFront_bits mblen a vs ix bs = indexFront mblen a vs ix =<< packWord Concrete bs
indexFront_int :: Nat' -> TValue -> SeqMap Concrete -> TValue -> Integer -> Eval Value
indexFront_int _mblen _a vs _ix idx = lookupSeqMap vs idx
indexBack :: Nat' -> TValue -> SeqMap Concrete -> TValue -> BV -> Eval Value
indexBack mblen a vs ix (bvVal -> idx) = indexBack_int mblen a vs ix idx
indexBack_bits :: Nat' -> TValue -> SeqMap Concrete -> TValue -> [Bool] -> Eval Value
indexBack_bits mblen a vs ix bs = indexBack mblen a vs ix =<< packWord Concrete bs
indexBack_int :: Nat' -> TValue -> SeqMap Concrete -> TValue -> Integer -> Eval Value
indexBack_int mblen _a vs _ix idx =
case mblen of
Nat len -> lookupSeqMap vs (len - idx - 1)
Inf -> evalPanic "indexBack" ["unexpected infinite sequence"]
updateFront ::
Nat' ->
TValue ->
SeqMap Concrete ->
Either Integer (WordValue Concrete) ->
Eval Value ->
Eval (SeqMap Concrete)
updateFront _len _eltTy vs (Left idx) val = do
return $ updateSeqMap vs idx val
updateFront _len _eltTy vs (Right w) val = do
idx <- bvVal <$> asWordVal Concrete w
return $ updateSeqMap vs idx val
updateFront_word ::
Nat' ->
TValue ->
WordValue Concrete ->
Either Integer (WordValue Concrete) ->
Eval Value ->
Eval (WordValue Concrete)
updateFront_word _len _eltTy bs (Left idx) val = do
updateWordValue Concrete bs idx (fromVBit <$> val)
updateFront_word _len _eltTy bs (Right w) val = do
idx <- bvVal <$> asWordVal Concrete w
updateWordValue Concrete bs idx (fromVBit <$> val)
updateBack ::
Nat' ->
TValue ->
SeqMap Concrete ->
Either Integer (WordValue Concrete) ->
Eval Value ->
Eval (SeqMap Concrete)
updateBack Inf _eltTy _vs _w _val =
evalPanic "Unexpected infinite sequence in updateEnd" []
updateBack (Nat n) _eltTy vs (Left idx) val = do
return $ updateSeqMap vs (n - idx - 1) val
updateBack (Nat n) _eltTy vs (Right w) val = do
idx <- bvVal <$> asWordVal Concrete w
return $ updateSeqMap vs (n - idx - 1) val
updateBack_word ::
Nat' ->
TValue ->
WordValue Concrete ->
Either Integer (WordValue Concrete) ->
Eval Value ->
Eval (WordValue Concrete)
updateBack_word Inf _eltTy _bs _w _val =
evalPanic "Unexpected infinite sequence in updateEnd" []
updateBack_word (Nat n) _eltTy bs (Left idx) val = do
updateWordValue Concrete bs (n - idx - 1) (fromVBit <$> val)
updateBack_word (Nat n) _eltTy bs (Right w) val = do
idx <- bvVal <$> asWordVal Concrete w
updateWordValue Concrete bs (n - idx - 1) (fromVBit <$> val)