crucible-0.7: src/Lang/Crucible/Simulator/RegValue.hs
-----------------------------------------------------------------------
-- |
-- Module : Lang.Crucible.Simulator.RegValue
-- Description : Runtime representation of CFG registers
-- Copyright : (c) Galois, Inc 2014
-- License : BSD3
-- Maintainer : Joe Hendrix <jhendrix@galois.com>
-- Stability : provisional
--
-- RegValue is a type family that defines the runtime representation
-- of crucible types.
------------------------------------------------------------------------
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module Lang.Crucible.Simulator.RegValue
( RegValue
, CanMux(..)
, RegValue'(..)
, MuxFn
-- * Register values
, AnyValue(..)
, FnVal(..)
, fnValType
, RolledType(..)
, SymSequence(..)
, VariantBranch(..)
, injectVariant
-- * Value mux functions
, ValMuxFn
, eqMergeFn
, mergePartExpr
, muxRecursive
, muxStringMap
, muxStruct
, muxVariant
, muxVector
, muxSymSequence
, muxHandle
) where
import Control.Monad
import Control.Monad.Trans.Class
import Data.Kind
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Proxy
import qualified Data.Set as Set
import Data.Text (Text)
import qualified Data.Vector as V
import Data.Word
import GHC.TypeNats (KnownNat)
import qualified Data.Parameterized.Context as Ctx
import What4.FunctionName
import What4.Interface
import What4.InterpretedFloatingPoint
import What4.Partial
import What4.WordMap
import Lang.Crucible.FunctionHandle
import Lang.Crucible.Simulator.Intrinsics
import Lang.Crucible.Simulator.SymSequence
import Lang.Crucible.Types
import Lang.Crucible.Utils.MuxTree
import Lang.Crucible.Backend
type MuxFn p v = p -> v -> v -> IO v
-- | Maps register types to the runtime representation.
type family RegValue (sym :: Type) (tp :: CrucibleType) :: Type where
RegValue sym (BaseToType bt) = SymExpr sym bt
RegValue sym (FloatType fi) = SymInterpretedFloat sym fi
RegValue sym AnyType = AnyValue sym
RegValue sym UnitType = ()
RegValue sym NatType = SymNat sym
RegValue sym CharType = Word16
RegValue sym (FunctionHandleType a r) = FnVal sym a r
RegValue sym (MaybeType tp) = PartExpr (Pred sym) (RegValue sym tp)
RegValue sym (VectorType tp) = V.Vector (RegValue sym tp)
RegValue sym (SequenceType tp) = SymSequence sym (RegValue sym tp)
RegValue sym (StructType ctx) = Ctx.Assignment (RegValue' sym) ctx
RegValue sym (VariantType ctx) = Ctx.Assignment (VariantBranch sym) ctx
RegValue sym (ReferenceType tp) = MuxTree sym (RefCell tp)
RegValue sym (WordMapType w tp) = WordMap sym w tp
RegValue sym (RecursiveType nm ctx) = RolledType sym nm ctx
RegValue sym (IntrinsicType nm ctx) = Intrinsic sym nm ctx
RegValue sym (StringMapType tp) = Map Text (PartExpr (Pred sym) (RegValue sym tp))
-- | A newtype wrapper around RegValue. This is wrapper necessary because
-- RegValue is a type family and, as such, cannot be partially applied.
newtype RegValue' sym tp = RV { unRV :: RegValue sym tp }
------------------------------------------------------------------------
-- FnVal
-- | Represents a function closure.
data FnVal (sym :: Type) (args :: Ctx CrucibleType) (res :: CrucibleType) where
ClosureFnVal ::
!(FnVal sym (args ::> tp) ret) ->
!(TypeRepr tp) ->
!(RegValue sym tp) ->
FnVal sym args ret
VarargsFnVal ::
!(FnHandle (args ::> VectorType AnyType) ret) ->
!(CtxRepr addlArgs) ->
FnVal sym (args <+> addlArgs) ret
HandleFnVal ::
!(FnHandle a r) ->
FnVal sym a r
closureFunctionName :: FnVal sym args res -> FunctionName
closureFunctionName (ClosureFnVal c _ _) = closureFunctionName c
closureFunctionName (HandleFnVal h) = handleName h
closureFunctionName (VarargsFnVal h _) = handleName h
-- | Extract the runtime representation of the type of the given 'FnVal'
fnValType :: FnVal sym args res -> TypeRepr (FunctionHandleType args res)
fnValType (HandleFnVal h) = FunctionHandleRepr (handleArgTypes h) (handleReturnType h)
fnValType (VarargsFnVal h addlArgs) =
case handleArgTypes h of
args Ctx.:> _ -> FunctionHandleRepr (args Ctx.<++> addlArgs) (handleReturnType h)
fnValType (ClosureFnVal fn _ _) =
case fnValType fn of
FunctionHandleRepr allArgs r ->
case allArgs of
args Ctx.:> _ -> FunctionHandleRepr args r
instance Show (FnVal sym a r) where
show = show . closureFunctionName
-- | Version of 'MuxFn' specialized to 'RegValue'
type ValMuxFn sym tp = MuxFn (Pred sym) (RegValue sym tp)
------------------------------------------------------------------------
-- CanMux
-- | A class for 'CrucibleType's that have a
-- mux function.
class CanMux sym (tp :: CrucibleType) where
muxReg :: sym
-> p tp -- ^ Unused type to identify what is being merged.
-> ValMuxFn sym tp
-- | Merge function that checks if two values are equal, and
-- fails if they are not.
{-# INLINE eqMergeFn #-}
eqMergeFn :: (IsExprBuilder sym, Eq v) => sym -> String -> MuxFn p v
eqMergeFn sym nm = \_ x y ->
if x == y then
return x
else
throwUnsupported sym $ "Cannot merge dissimilar " ++ nm ++ "."
------------------------------------------------------------------------
-- RegValue AnyType instance
data AnyValue sym where
AnyValue :: TypeRepr tp -> RegValue sym tp -> AnyValue sym
------------------------------------------------------------------------
-- RegValue () instance
instance CanMux sym UnitType where
muxReg _ = \_ _ x _y -> return x
------------------------------------------------------------------------
-- RegValue instance for base types
instance IsExprBuilder sym => CanMux sym BoolType where
{-# INLINE muxReg #-}
muxReg s = const $ itePred s
instance IsExprBuilder sym => CanMux sym NatType where
{-# INLINE muxReg #-}
muxReg s = \_ -> natIte s
instance IsExprBuilder sym => CanMux sym IntegerType where
{-# INLINE muxReg #-}
muxReg s = \_ -> intIte s
instance IsExprBuilder sym => CanMux sym RealValType where
{-# INLINE muxReg #-}
muxReg s = \_ -> realIte s
instance IsInterpretedFloatExprBuilder sym => CanMux sym (FloatType fi) where
{-# INLINE muxReg #-}
muxReg s = \_ -> iFloatIte @sym @fi s
instance IsExprBuilder sym => CanMux sym ComplexRealType where
{-# INLINE muxReg #-}
muxReg s = \_ -> cplxIte s
instance IsExprBuilder sym => CanMux sym (StringType si) where
{-# INLINE muxReg #-}
muxReg s = \_ -> stringIte s
instance IsExprBuilder sym => CanMux sym (IEEEFloatType fpp) where
muxReg s = \_ -> floatIte s
------------------------------------------------------------------------
-- RegValue Vector instance
{-# INLINE muxVector #-}
muxVector :: IsExprBuilder sym =>
sym -> MuxFn p e -> MuxFn p (V.Vector e)
muxVector sym f p x y
| V.length x == V.length y = V.zipWithM (f p) x y
| otherwise =
throwUnsupported sym "Cannot merge vectors with different dimensions."
instance (IsSymInterface sym, CanMux sym tp) => CanMux sym (VectorType tp) where
{-# INLINE muxReg #-}
muxReg s _ = muxVector s (muxReg s (Proxy :: Proxy tp))
------------------------------------------------------------------------
-- RegValue WordMap instance
instance (IsExprBuilder sym, KnownNat w, KnownRepr BaseTypeRepr tp)
=> CanMux sym (WordMapType w tp) where
{-# INLINE muxReg #-}
muxReg s _ p = muxWordMap s knownNat knownRepr p
------------------------------------------------------------------------
-- RegValue MatlabChar instance
instance IsSymInterface sym => CanMux sym CharType where
{-# INLINE muxReg #-}
muxReg s = \_ -> eqMergeFn s "characters"
------------------------------------------------------------------------
-- RegValue Maybe instance
mergePartExpr :: IsExprBuilder sym
=> sym
-> (Pred sym -> v -> v -> IO v)
-> Pred sym
-> PartExpr (Pred sym) v
-> PartExpr (Pred sym) v
-> IO (PartExpr (Pred sym) v)
mergePartExpr sym fn = mergePartial sym (\c a b -> lift (fn c a b))
instance (IsExprBuilder sym, CanMux sym tp) => CanMux sym (MaybeType tp) where
{-# INLINE muxReg #-}
muxReg s = \_ -> do
let f = muxReg s (Proxy :: Proxy tp)
in mergePartExpr s f
------------------------------------------------------------------------
-- RegValue FunctionHandleType instance
-- TODO: Figure out how to actually compare these.
{-# INLINE muxHandle #-}
muxHandle :: IsExpr (SymExpr sym)
=> sym
-> Pred sym
-> FnVal sym a r
-> FnVal sym a r
-> IO (FnVal sym a r)
muxHandle _ c x y
| Just b <- asConstantPred c = pure $! if b then x else y
| otherwise = return x
instance IsExprBuilder sym => CanMux sym (FunctionHandleType a r) where
{-# INLINE muxReg #-}
muxReg s = \_ c x y -> do
muxHandle s c x y
------------------------------------------------------------------------
-- RegValue IdentValueMap instance
-- | Merge to string maps together.
{-# INLINE muxStringMap #-}
muxStringMap :: IsExprBuilder sym
=> sym
-> MuxFn (Pred sym) e
-> MuxFn (Pred sym) (Map Text (PartExpr (Pred sym) e))
muxStringMap sym = \f c x y -> do
let keys = Set.toList $ Set.union (Map.keysSet x) (Map.keysSet y)
fmap Map.fromList $ forM keys $ \k -> do
let vx = joinMaybePE (Map.lookup k x)
let vy = joinMaybePE (Map.lookup k y)
r <- mergePartExpr sym f c vx vy
return (k,r)
------------------------------------------------------------------------
-- RegValue Recursive instance
newtype RolledType sym nm ctx = RolledType { unroll :: RegValue sym (UnrollType nm ctx) }
{-# INLINE muxRecursive #-}
muxRecursive
:: IsRecursiveType nm
=> (forall tp. TypeRepr tp -> ValMuxFn sym tp)
-> SymbolRepr nm
-> CtxRepr ctx
-> ValMuxFn sym (RecursiveType nm ctx)
muxRecursive recf = \nm ctx p x y -> do
RolledType <$> recf (unrollType nm ctx) p (unroll x) (unroll y)
------------------------------------------------------------------------
-- RegValue Struct instance
{-# INLINE muxStruct #-}
muxStruct
:: (forall tp. TypeRepr tp -> ValMuxFn sym tp)
-> CtxRepr ctx
-> ValMuxFn sym (StructType ctx)
muxStruct recf ctx = \p x y ->
Ctx.generateM (Ctx.size ctx) $ \i -> do
RV <$> recf (ctx Ctx.! i) p (unRV $ x Ctx.! i) (unRV $ y Ctx.! i)
------------------------------------------------------------------------
-- RegValue Variant instance
newtype VariantBranch sym tp = VB { unVB :: PartExpr (Pred sym) (RegValue sym tp) }
-- | Construct a 'VariantType' value by identifying which branch of
-- the variant to construct, and providing a value of the correct type.
injectVariant ::
IsExprBuilder sym =>
sym {- ^ symbolic backend -} ->
CtxRepr ctx {- ^ Types of the variant branches -} ->
Ctx.Index ctx tp {- ^ Which branch -} ->
RegValue sym tp {- ^ The value to inject -} ->
RegValue sym (VariantType ctx)
injectVariant sym ctxRepr idx val =
Ctx.generate (Ctx.size ctxRepr) $ \j ->
case testEquality j idx of
Just Refl -> VB (PE (truePred sym) val)
Nothing -> VB Unassigned
{-# INLINE muxVariant #-}
muxVariant
:: IsExprBuilder sym
=> sym
-> (forall tp. TypeRepr tp -> ValMuxFn sym tp)
-> CtxRepr ctx
-> ValMuxFn sym (VariantType ctx)
muxVariant sym recf ctx = \p x y ->
Ctx.generateM (Ctx.size ctx) $ \i ->
VB <$> mergePartExpr sym
(recf (ctx Ctx.! i))
p
(unVB (x Ctx.! i))
(unVB (y Ctx.! i))