packages feed

crucible-0.7: src/Lang/Crucible/Simulator/Evaluation.hs

-----------------------------------------------------------------------
-- |
-- Module           : Lang.Crucible.Simulator.Evaluation
-- Description      : Evaluation functions for Crucible core expressions
-- Copyright        : (c) Galois, Inc 2014-2016
-- License          : BSD3
-- Maintainer       : Joe Hendrix <jhendrix@galois.com>
-- Stability        : provisional
--
-- This module provides operations evaluating Crucible expressions.
------------------------------------------------------------------------
{-# LANGUAGE DoAndIfThenElse #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ViewPatterns #-}
module Lang.Crucible.Simulator.Evaluation
  ( EvalAppFunc
  , evalApp
  , selectedIndices
  , indexSymbolic
  , integerAsChar
  , complexRealAsChar
  , indexVectorWithSymNat
  , adjustVectorWithSymNat
  , updateVectorWithSymNat
  ) where

import           Prelude hiding (pred)

import qualified Control.Exception as Ex
import           Control.Lens
import           Control.Monad
import qualified Data.BitVector.Sized as BV
import qualified Data.Map.Strict as Map
import           Data.Maybe
import qualified Data.Text as Text
import qualified Data.Vector as V
import           Data.Word
import           Numeric ( showHex )
import           Numeric.Natural
import           GHC.Stack

import           Data.Parameterized.Classes
import           Data.Parameterized.Context as Ctx
import           Data.Parameterized.TraversableFC

import           What4.Interface
import           What4.InterpretedFloatingPoint
import           What4.Partial (pattern PE, pattern Unassigned, joinMaybePE)
import           What4.Utils.Complex
import           What4.WordMap

import           Lang.Crucible.Backend
import           Lang.Crucible.CFG.Expr
import           Lang.Crucible.Simulator.Intrinsics
import           Lang.Crucible.Simulator.RegMap
import           Lang.Crucible.Simulator.SimError
import           Lang.Crucible.Simulator.SymSequence
import           Lang.Crucible.Types

------------------------------------------------------------------------
-- Utilities


-- | Given a list of Booleans l, @selectedIndices@ returns the indices of
-- true values in @l@.
selectedIndices :: [Bool] -> [Natural]
selectedIndices l = catMaybes $ Prelude.zipWith selectIndex l [1..]
  where selectIndex True i  = Just i
        selectIndex False _ = Nothing

------------------------------------------------------------------------
-- Coercion functions

integerAsChar :: Integer -> Word16
integerAsChar i = fromInteger ((i `max` 0) `min` (2^(16::Int)-1))

complexRealAsChar :: (MonadFail m, IsExpr val)
                  => val BaseComplexType
                  -> m Word16
complexRealAsChar v = do
  case cplxExprAsRational v of
    -- Check number is printable.
    Just r | otherwise -> return (integerAsChar (floor r))
    Nothing -> fail "Symbolic value cannot be interpreted as a character."
    -- XXX: Should this be a panic?
    -- XXX: We should move this to crucible-matlab

------------------------------------------------------------------------
-- Evaluating expressions


-- | Helper method for implementing 'indexSymbolic'
indexSymbolic' :: IsSymBackend sym bak
               => bak
               -> (Pred sym -> a -> a -> IO a)
                  -- ^ Function for merging valeus
               -> ([Natural] -> IO a) -- ^ Concrete index function.
               -> [Natural] -- ^ Values of processed indices (in reverse order)
               -> [(Natural,Natural)] -- ^ Bounds on remaining indices.
               -> [SymNat sym] -- ^ Remaining indices.
               -> IO a
indexSymbolic' _ _ f p [] _ = f (reverse p)
indexSymbolic' _ _ f p _ [] = f (reverse p)
indexSymbolic' bak iteFn f p ((l,h):nl) (si:il) = do
  let subIndex idx = indexSymbolic' bak iteFn f (idx:p) nl il
  case asNat si of
    Just i
      | l <= i && i <= h -> subIndex i
      | otherwise ->
          addFailedAssertion bak (AssertFailureSimError msg details)
        where msg = "Index outside matrix dimensions." ++ show (l,i,h)
              details = unwords ["Index", show i, "is outside of range", show (l, h)]
    Nothing ->
      do let sym = backendGetSym bak
         ensureInRange bak l h si "Index outside matrix dimensions."
         let predFn i = natEq sym si =<< natLit sym i
         muxRange predFn iteFn subIndex l h


ensureInRange ::
  IsSymBackend sym bak =>
  bak ->
  Natural ->
  Natural ->
  SymNat sym ->
  String ->
  IO ()
ensureInRange bak l h si msg =
  do let sym = backendGetSym bak
     l_sym <- natLit sym l
     h_sym <- natLit sym h
     inRange <- join $ andPred sym <$> natLe sym l_sym si <*> natLe sym si h_sym
     assert bak inRange (AssertFailureSimError msg details)
  where details = unwords ["Range is", show (l, h)]



-- | Lookup a value in an array that may be at a symbolic offset.
--
-- This function takes a list of symbolic indices as natural numbers
-- along with a pair of lower and upper bounds for each index.
-- It assumes that the indices are all in range.
indexSymbolic :: IsSymBackend sym bak
              => bak
              -> (Pred sym -> a  -> a -> IO a)
                 -- ^ Function for combining results together.
              -> ([Natural] -> IO a) -- ^ Concrete index function.
              -> [(Natural,Natural)] -- ^ High and low bounds at the indices.
              -> [SymNat sym]
              -> IO a
indexSymbolic sym iteFn f = indexSymbolic' sym iteFn f []

-- | Evaluate an indexTermterm to an index value.
evalBase :: IsSymInterface sym =>
            sym
         -> (forall utp . f utp -> IO (RegValue sym utp))
         -> BaseTerm f vtp
         -> IO (SymExpr sym vtp)
evalBase _ evalSub (BaseTerm _tp e) = evalSub e

-- | Get value stored in vector at a symbolic index.
indexVectorWithSymNat :: IsSymBackend sym bak
                      => bak
                      -> (Pred sym -> a -> a -> IO a)
                         -- ^ Ite function
                      -> V.Vector a
                      -> SymNat sym
                      -> IO a
indexVectorWithSymNat bak iteFn v si =
  Ex.assert (n > 0) $
  case asNat si of
    Just i | 0 <= i && i < n -> return (v V.! fromIntegral i)
           | otherwise -> addFailedAssertion bak (AssertFailureSimError msg details)
    Nothing ->
      do let sym = backendGetSym bak
         let predFn i = natEq sym si =<< natLit sym i
         let getElt i = return (v V.! fromIntegral i)
         ensureInRange bak 0 (n - 1) si msg
         muxRange predFn iteFn getElt 0 (n - 1)
  where
  n   = fromIntegral (V.length v)
  msg = "Vector index out of range"
  details = unwords ["Range is", show (0 :: Natural, n)]



-- | Update a vector at a given natural number index.
updateVectorWithSymNat :: IsSymBackend sym bak
                       => bak
                          -- ^ Symbolic backend
                       -> (Pred sym -> a -> a -> IO a)
                          -- ^ Ite function
                       -> V.Vector a
                          -- ^ Vector to update
                       -> SymNat sym
                          -- ^ Index to update
                       -> a
                          -- ^ New value to assign
                       -> IO (V.Vector a)
updateVectorWithSymNat bak iteFn v si new_val = do
  adjustVectorWithSymNat bak iteFn v si (\_ -> return new_val)

-- | Update a vector at a given natural number index.
adjustVectorWithSymNat :: IsSymBackend sym bak
                       => bak
                          -- ^ Symbolic backend
                       -> (Pred sym -> a -> a -> IO a)
                          -- ^ Ite function
                       -> V.Vector a
                          -- ^ Vector to update
                       -> SymNat sym
                          -- ^ Index to update
                       -> (a -> IO a)
                          -- ^ Adjustment function to apply
                       -> IO (V.Vector a)
adjustVectorWithSymNat bak iteFn v si adj =
  case asNat si of
    Just i

      | i < fromIntegral n ->
        do new_val <- adj (v V.! fromIntegral i)
           return $ v V.// [(fromIntegral i, new_val)]

      | otherwise ->
        addFailedAssertion bak $ AssertFailureSimError msg (details i)

    Nothing ->
      do ensureInRange bak 0 (fromIntegral (n-1)) si msg
         V.generateM n setFn
      where
      setFn j =
        do  let sym = backendGetSym bak
            -- Compare si and j.
            c <- natEq sym si =<< natLit sym (fromIntegral j)
            -- Select old value or new value
            case asConstantPred c of
              Just True  -> adj (v V.! j)
              Just False -> return (v V.! j)
              Nothing ->
                do new_val <- adj (v V.! j)
                   iteFn c new_val (v V.! j)

  where
  n = V.length v
  msg = "Illegal vector index"
  details i = "Illegal index " ++ show i ++ "given to updateVectorWithSymNat"

type EvalAppFunc sym app = forall f.
  (forall tp. f tp -> IO (RegValue sym tp)) ->
  (forall tp. app f tp -> IO (RegValue sym tp))

{-# INLINE evalApp #-}
-- | Evaluate the application.
evalApp :: forall sym bak ext.
           IsSymBackend sym bak
        => bak
        -> IntrinsicTypes sym
        -> (Int -> String -> IO ())
           -- ^ Function for logging messages.
        -> EvalAppFunc sym (ExprExtension ext)
        -> EvalAppFunc sym (App ext)
evalApp bak itefns _logFn evalExt (evalSub :: forall tp. f tp -> IO (RegValue sym tp)) a0 = do
  let sym = backendGetSym bak
  case a0 of

    BaseIsEq tp xe ye -> do
      x <- evalBase sym evalSub (BaseTerm tp xe)
      y <- evalBase sym evalSub (BaseTerm tp ye)
      isEq sym x y

    BaseIte tp ce xe ye -> do
      c <- evalSub ce
      case asConstantPred c of
        Just True  -> evalSub xe
        Just False -> evalSub ye
        Nothing -> do
          x <- evalBase sym evalSub (BaseTerm tp xe)
          y <- evalBase sym evalSub (BaseTerm tp ye)
          baseTypeIte sym c x y

    ----------------------------------------------------------------------
    ExtensionApp x -> evalExt evalSub x

    ----------------------------------------------------------------------
    -- ()

    EmptyApp -> return ()

    ----------------------------------------------------------------------
    -- Any

    PackAny tp x -> do
      xv <- evalSub x
      return (AnyValue tp xv)

    UnpackAny tp x -> do
      xv <- evalSub x
      case xv of
        AnyValue tpv v
          | Just Refl <- testEquality tp tpv ->
               return $! PE (truePred sym) v
          | otherwise ->
               return Unassigned

    ----------------------------------------------------------------------
    -- Bool

    BoolLit b -> return $ backendPred sym b
    Not x -> do
      r <- evalSub x
      notPred sym r
    And x y -> do
      xv <- evalSub x
      yv <- evalSub y
      andPred sym xv yv
    Or x y -> do
      xv <- evalSub x
      yv <- evalSub y
      orPred sym xv yv
    BoolXor x y -> do
      xv <- evalSub x
      yv <- evalSub y
      xorPred sym xv yv

    ----------------------------------------------------------------------
    -- Nat

    NatLit n -> natLit sym n
    NatIte pe xe ye -> do
      p <- evalSub pe
      x <- evalSub xe
      y <- evalSub ye
      natIte sym p x y
    NatEq xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      natEq sym x y
    NatLt xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      natLt sym x y
    NatLe xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      natLe sym x y
    NatAdd xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      natAdd sym x y
    NatSub xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      natSub sym x y
    NatMul xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      natMul sym x y
    NatDiv xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      natDiv sym x y
    NatMod xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      natMod sym x y

    ----------------------------------------------------------------------
    -- Int

    IntLit n -> intLit sym n
    IntLe xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      intLe sym x y
    IntLt xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      intLt sym x y
    IntNeg xe -> do
      x <- evalSub xe
      intNeg sym x
    IntAbs xe -> do
      x <- evalSub xe
      intAbs sym x
    IntAdd xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      intAdd sym x y
    IntSub xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      intSub sym x y
    IntMul xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      intMul sym x y
    IntDiv xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      intDiv sym x y
    IntMod xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      intMod sym x y

    --------------------------------------------------------------------
    -- Maybe

    JustValue _ e -> do
      r <- evalSub e
      return $! PE (truePred sym) r
    NothingValue _ -> do
      return Unassigned
    FromJustValue _ maybe_expr msg_expr -> do
      maybe_val <- evalSub maybe_expr
      case maybe_val of
        -- Special case to avoid forcing evaluation of msg.
        PE (asConstantPred -> Just True) v -> return v
        _ -> do
          msg <- evalSub msg_expr
          case asString msg of
            Just (UnicodeLiteral msg') -> readPartExpr bak maybe_val (GenericSimError (Text.unpack msg'))
            Nothing ->
              addFailedAssertion bak $
                Unsupported callStack "Symbolic string in fromJustValue"

    ----------------------------------------------------------------------
    -- Recursive Types

    RollRecursive _ _ e   -> RolledType <$> evalSub e
    UnrollRecursive _ _ e -> unroll <$> evalSub e

    ----------------------------------------------------------------------
    -- Vector

    VectorLit _ v -> traverse evalSub v
    VectorReplicate _ n_expr e_expr -> do
      ne <- evalSub n_expr
      case asNat ne of
        Nothing -> addFailedAssertion bak $
                      Unsupported callStack "vectors with symbolic length"
        Just n -> do
          e <- evalSub e_expr
          return $ V.replicate (fromIntegral n) e
    VectorIsEmpty r -> do
      v <- evalSub r
      return $ backendPred sym (V.null v)
    VectorSize v_expr -> do
      v <- evalSub v_expr
      natLit sym (fromIntegral (V.length v))
    VectorGetEntry rtp v_expr i_expr -> do
      v <- evalSub v_expr
      i <- evalSub i_expr
      indexVectorWithSymNat bak (muxRegForType sym itefns rtp) v i
    VectorSetEntry rtp v_expr i_expr n_expr -> do
      v <- evalSub v_expr
      i <- evalSub i_expr
      n <- evalSub n_expr
      updateVectorWithSymNat bak (muxRegForType sym itefns rtp) v i n
    VectorCons _ e_expr v_expr -> do
      e <- evalSub e_expr
      v <- evalSub v_expr
      return $ V.cons e v

    --------------------------------------------------------------------
    -- Sequence

    SequenceNil _tpr -> nilSymSequence sym
    SequenceCons _tpr x xs ->
      join $ consSymSequence sym <$> evalSub x <*> evalSub xs
    SequenceAppend _tpr xs ys ->
      join $ appendSymSequence sym <$> evalSub xs <*> evalSub ys
    SequenceIsNil _tpr xs ->
      isNilSymSequence sym =<< evalSub xs
    SequenceLength _tpr xs ->
      lengthSymSequence sym =<< evalSub xs
    SequenceHead tpr xs ->
      headSymSequence sym (muxRegForType sym itefns tpr) =<< evalSub xs
    SequenceTail _tpr xs ->
      tailSymSequence sym =<< evalSub xs
    SequenceUncons tpr xs ->
      do xs' <- evalSub xs
         mu <- unconsSymSequence sym (muxRegForType sym itefns tpr) xs'
         traverse (\ (h,tl) -> pure (Ctx.Empty Ctx.:> RV h Ctx.:> RV tl)) mu

    --------------------------------------------------------------------
    -- Symbolic Arrays

    SymArrayLookup _ a i -> do
      join $ arrayLookup sym <$> evalSub a <*> traverseFC (evalBase sym evalSub) i

    SymArrayUpdate  _ a i v -> do
      join $ arrayUpdate sym
        <$> evalSub a
        <*> traverseFC (evalBase sym evalSub) i
        <*> evalSub v

    ----------------------------------------------------------------------
    -- Handle

    HandleLit h -> return (HandleFnVal h)

    Closure _ _ h_expr tp v_expr -> do
      h <- evalSub h_expr
      v <- evalSub v_expr
      return $! ClosureFnVal h tp v

    ----------------------------------------------------------------------
    -- RealVal

    RationalLit d -> realLit sym d
    RealNeg xe -> do
      x <- evalSub xe
      realNeg sym x
    RealAdd xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      realAdd sym x y
    RealSub xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      realSub sym x y
    RealMul xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      realMul sym x y
    RealDiv xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      realDiv sym x y
    RealMod xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      realMod sym x y
    RealLt x_expr y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      realLt sym x y
    RealLe x_expr y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      realLe sym x y
    RealIsInteger x_expr -> do
      x <- evalSub x_expr
      isInteger sym x

    ----------------------------------------------------------------------
    -- Float

    -- This is not necessarily considered correct, see crucible#366
    FloatUndef f -> freshConstant sym emptySymbol (iFloatBaseTypeRepr sym f)

    FloatLit f -> iFloatLitSingle sym f
    DoubleLit d -> iFloatLitDouble sym d
    X86_80Lit ld -> iFloatLitLongDouble sym ld
    FloatNaN fi -> iFloatNaN sym fi
    FloatPInf fi -> iFloatPInf sym fi
    FloatNInf fi -> iFloatNInf sym fi
    FloatPZero fi -> iFloatPZero sym fi
    FloatNZero fi -> iFloatNZero sym fi
    FloatNeg _ (x_expr :: f (FloatType fi)) ->
      iFloatNeg @_ @fi sym =<< evalSub x_expr
    FloatAbs _ (x_expr :: f (FloatType fi)) ->
      iFloatAbs @_ @fi sym =<< evalSub x_expr
    FloatSqrt _ rm (x_expr :: f (FloatType fi)) ->
      iFloatSqrt @_ @fi sym rm =<< evalSub x_expr
    FloatAdd _ rm (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatAdd @_ @fi sym rm x y
    FloatSub _ rm (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatSub @_ @fi sym rm x y
    FloatMul _ rm (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatMul @_ @fi sym rm x y
    FloatDiv _ rm (x_expr :: f (FloatType fi)) y_expr -> do
      -- TODO: handle division by zero
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatDiv @_ @fi sym rm x y
    FloatRem _ (x_expr :: f (FloatType fi)) y_expr -> do
      -- TODO: handle division by zero
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatRem @_ @fi sym x y
    FloatMin _ (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatMin @_ @fi sym x y
    FloatMax _ (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatMax @_ @fi sym x y
    FloatFMA _ rm (x_expr :: f (FloatType fi)) y_expr z_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      z <- evalSub z_expr
      iFloatFMA @_ @fi sym rm x y z
    FloatEq (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatEq @_ @fi sym x y
    FloatFpEq (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatFpEq @_ @fi sym x y
    FloatIte _ c_expr (x_expr :: f (FloatType fi)) y_expr -> do
      c <- evalSub c_expr
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatIte @_ @fi sym c x y
    FloatLt (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatLt @_ @fi sym x y
    FloatLe (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatLe @_ @fi sym x y
    FloatGt (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatGt @_ @fi sym x y
    FloatGe (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatGe @_ @fi sym x y
    FloatNe (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatNe @_ @fi sym x y
    FloatFpApart (x_expr :: f (FloatType fi)) y_expr -> do
      x <- evalSub x_expr
      y <- evalSub y_expr
      iFloatFpApart @_ @fi sym x y
    FloatCast fi rm (x_expr :: f (FloatType fi')) ->
      iFloatCast @_ @_ @fi' sym fi rm =<< evalSub x_expr
    FloatFromBinary fi x_expr -> iFloatFromBinary sym fi =<< evalSub x_expr
    FloatToBinary fi x_expr -> iFloatToBinary sym fi =<< evalSub x_expr
    FloatFromBV fi rm x_expr -> iBVToFloat sym fi rm =<< evalSub x_expr
    FloatFromSBV fi rm x_expr -> iSBVToFloat sym fi rm =<< evalSub x_expr
    FloatFromReal fi rm x_expr -> iRealToFloat sym fi rm =<< evalSub x_expr
    FloatToBV w rm (x_expr :: f (FloatType fi)) ->
      iFloatToBV @_ @_ @fi sym w rm =<< evalSub x_expr
    FloatToSBV w rm (x_expr :: f (FloatType fi)) ->
      iFloatToSBV @_ @_ @fi sym w rm =<< evalSub x_expr
    FloatToReal (x_expr :: f (FloatType fi)) ->
      iFloatToReal @_ @fi sym =<< evalSub x_expr
    FloatIsNaN (x_expr :: f (FloatType fi)) ->
      iFloatIsNaN @_ @fi sym =<< evalSub x_expr
    FloatIsInfinite (x_expr :: f (FloatType fi)) ->
      iFloatIsInf @_ @fi sym =<< evalSub x_expr
    FloatIsZero (x_expr :: f (FloatType fi)) ->
      iFloatIsZero @_ @fi sym =<< evalSub x_expr
    FloatIsPositive (x_expr :: f (FloatType fi)) ->
      iFloatIsPos @_ @fi sym =<< evalSub x_expr
    FloatIsNegative (x_expr :: f (FloatType fi)) ->
      iFloatIsNeg @_ @fi sym =<< evalSub x_expr
    FloatIsSubnormal (x_expr :: f (FloatType fi)) ->
      iFloatIsSubnorm @_ @fi sym =<< evalSub x_expr
    FloatIsNormal (x_expr :: f (FloatType fi)) ->
      iFloatIsNorm @_ @fi sym =<< evalSub x_expr

    ----------------------------------------------------------------------
    -- Conversions

    NatToInteger x_expr -> do
      x <- evalSub x_expr
      natToInteger sym x
    IntegerToReal x_expr -> do
      x <- evalSub x_expr
      integerToReal sym x
    RealToNat x_expr -> do
      x <- evalSub x_expr
      realToNat sym x
    BvToNat _ xe -> do
      bvToNat sym =<< evalSub xe
    BvToInteger _ xe -> do
      bvToInteger sym =<< evalSub xe
    SbvToInteger _ xe -> do
      sbvToInteger sym =<< evalSub xe
    RealFloor xe ->
      realFloor sym =<< evalSub xe
    RealCeil xe ->
      realCeil sym =<< evalSub xe
    RealRound xe ->
      realRound sym =<< evalSub xe
    IntegerToBV w xe -> do
      x <- evalSub xe
      integerToBV sym x w

    ----------------------------------------------------------------------
    -- ComplexReal

    Complex r_expr i_expr -> do
      r <- evalSub r_expr
      i <- evalSub i_expr
      mkComplex sym (r :+ i)
    RealPart c_expr -> getRealPart sym =<< evalSub c_expr
    ImagPart c_expr -> getImagPart sym =<< evalSub c_expr

    --------------------------------------------------------------------
    -- BVs

    -- This is not necessarily considered correct, see crucible#366
    BVUndef w ->
      freshConstant sym emptySymbol (BaseBVRepr w)

    BVLit w bv -> bvLit sym w bv

    BVConcat _ _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvConcat sym x y
    -- FIXME: there are probably some worthwhile special cases to exploit in "BVSelect"
    BVSelect idx n _ xe -> do
      x <- evalSub xe
      bvSelect sym idx n x
    BVTrunc w' _ xe -> do
      x <- evalSub xe
      bvTrunc sym w' x
    BVZext w' _ xe -> do
      x <- evalSub xe
      bvZext sym w' x
    BVSext w' _ xe -> do
      x <- evalSub xe
      bvSext sym w' x
    BVNot _ xe ->
      bvNotBits sym =<< evalSub xe
    BVAnd _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvAndBits sym x y
    BVOr _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvOrBits sym x y
    BVXor _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvXorBits sym x y
    BVAdd _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvAdd sym x y
    BVNeg _ xe -> do
      x <- evalSub xe
      bvNeg sym x
    BVSub _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvSub sym x y
    BVMul _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvMul sym x y
    BVUdiv _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvUdiv sym x y
    BVSdiv _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvSdiv sym x y
    BVUrem _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvUrem sym x y
    BVSrem _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvSrem sym x y

    BVUlt _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvUlt sym x y
    BVSlt _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvSlt sym x y
    BoolToBV w xe -> do
      x <- evalSub xe
      one <- bvLit sym w (BV.one w)
      zro <- bvLit sym w (BV.zero w)
      bvIte sym x one zro
    BVNonzero _ xe -> do
      x <- evalSub xe
      bvIsNonzero sym x
    BVShl _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvShl sym x y
    BVLshr _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvLshr sym x y
    BVAshr _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvAshr sym x y
    BVRol _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvRol sym x y
    BVRor _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvRor sym x y
    BVCountTrailingZeros _ xe -> do
      x <- evalSub xe
      bvCountTrailingZeros sym x
    BVCountLeadingZeros _ xe -> do
      x <- evalSub xe
      bvCountLeadingZeros sym x
    BVPopcount _ xe -> do
      x <- evalSub xe
      bvPopcount sym x
    BVCarry _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      fst <$> addUnsignedOF sym x y
    BVSCarry _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      fst <$> addSignedOF sym x y
    BVSBorrow _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      fst <$> subSignedOF sym x y
    BVUle _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvUle sym x y
    BVSle _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      bvSle sym x y
    BVUMin _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      c <- bvUle sym x y
      bvIte sym c x y
    BVUMax _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      c <- bvUgt sym x y
      bvIte sym c x y
    BVSMin _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      c <- bvSle sym x y
      bvIte sym c x y
    BVSMax _ xe ye -> do
      x <- evalSub xe
      y <- evalSub ye
      c <- bvSgt sym x y
      bvIte sym c x y

    --------------------------------------------------------------------
    -- Word Maps

    EmptyWordMap w tp -> do
      emptyWordMap sym w tp

    InsertWordMap w tp ie ve me -> do
      i <- evalSub ie
      v <- evalSub ve
      m <- evalSub me
      insertWordMap sym w tp i v m

    LookupWordMap tp ie me -> do
      i <- evalSub ie
      m <- evalSub me
      x <- lookupWordMap sym (bvWidth i) tp i m
      let msg = "WordMap: read an undefined index" ++
                case asBV i of
                   Nothing  -> ""
                   Just (BV.BV idx) -> " 0x" ++ showHex idx ""
      let ex = ReadBeforeWriteSimError msg
      readPartExpr bak x ex

    LookupWordMapWithDefault tp ie me de -> do
      i <- evalSub ie
      m <- evalSub me
      d <- evalSub de
      x <- lookupWordMap sym (bvWidth i) tp i m
      case x of
        Unassigned -> return d
        PE p v -> do
          muxRegForType sym itefns (baseToType tp) p v d

    ---------------------------------------------------------------------
    -- Struct

    MkStruct _ exprs -> traverseFC (\x -> RV <$> evalSub x) exprs

    GetStruct st idx _ -> do
      struct <- evalSub st
      return $ unRV $ struct Ctx.! idx

    SetStruct _ st idx x -> do
      struct <- evalSub st
      v <- evalSub x
      return $ struct & ixF idx .~ RV v

    ----------------------------------------------------------------------
    -- Variant

    InjectVariant ctx idx ve -> do
         v <- evalSub ve
         return $ injectVariant sym ctx idx v

    ProjectVariant _ctx idx ve -> do
         v <- evalSub ve
         return $ unVB $ v Ctx.! idx

    ----------------------------------------------------------------------
    -- IdentValueMap

    EmptyStringMap _ -> return Map.empty

    LookupStringMapEntry _ m_expr i_expr -> do
      i <- evalSub i_expr
      m <- evalSub m_expr
      case asString i of
        Just (UnicodeLiteral i') -> return $ joinMaybePE (Map.lookup i' m)
        Nothing -> addFailedAssertion bak $
                    Unsupported callStack "Symbolic string in lookupStringMapEntry"

    InsertStringMapEntry _ m_expr i_expr v_expr -> do
      m <- evalSub m_expr
      i <- evalSub i_expr
      v <- evalSub v_expr
      case asString i of
        Just (UnicodeLiteral i') -> return $ Map.insert i' v m
        Nothing -> addFailedAssertion bak $
                     Unsupported callStack "Symbolic string in insertStringMapEntry"

    --------------------------------------------------------------------
    -- Strings

    StringLit x -> stringLit sym x
    ShowValue _bt x_expr -> do
      x <- evalSub x_expr
      stringLit sym (UnicodeLiteral (Text.pack (show (printSymExpr x))))
    ShowFloat _fi x_expr -> do
      x <- evalSub x_expr
      stringLit sym (UnicodeLiteral (Text.pack (show (printSymExpr x))))
    StringConcat _si x y -> do
      x' <- evalSub x
      y' <- evalSub y
      stringConcat sym x' y'
    StringEmpty si ->
      stringEmpty sym si
    StringLength x -> do
      x' <- evalSub x
      stringLength sym x'
    StringContains x y -> do
      x' <- evalSub x
      y' <- evalSub y
      stringContains sym x' y'
    StringIsPrefixOf x y -> do
      x' <- evalSub x
      y' <- evalSub y
      stringIsPrefixOf sym x' y'
    StringIsSuffixOf x y -> do
      x' <- evalSub x
      y' <- evalSub y
      stringIsSuffixOf sym x' y'
    StringIndexOf x y k -> do
      x' <- evalSub x
      y' <- evalSub y
      k' <- evalSub k
      stringIndexOf sym x' y' k'
    StringSubstring _si x off len -> do
      x' <- evalSub x
      off' <- evalSub off
      len' <- evalSub len
      stringSubstring sym x' off' len'

    ---------------------------------------------------------------------
    -- Introspection

    IsConcrete _ v -> do
      x <- baseIsConcrete <$> evalSub v
      return $! if x then truePred sym else falsePred sym

    ---------------------------------------------------------------------
    -- References

    ReferenceEq _ ref1 ref2 -> do
      cell1 <- evalSub ref1
      cell2 <- evalSub ref2
      eqReference sym cell1 cell2