1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>2--3-- SPDX-License-Identifier: GPL-3.0-only45module Language.QBE.Simulator.Concolic.Expression6 ( Concolic (..),7 hasSymbolic,8 )9where1011import Control.DeepSeq (NFData, NFData1)12import Control.Exception (assert)13import Data.Functor ((<&>))14import Data.Maybe (fromMaybe)15import Data.Word (Word8)16import GHC.Generics (Generic, Generic1)17import Language.QBE.Simulator.Default.Expression qualified as D18import Language.QBE.Simulator.Expression qualified as E19import Language.QBE.Simulator.Memory qualified as MEM20import Language.QBE.Simulator.Symbolic.Expression qualified as SE2122data Concolic v23 = Concolic24 { concrete :: v,25 symbolic :: Maybe SE.BitVector26 }27 deriving (Show, Generic, Generic1)2829instance (NFData a) => NFData (Concolic a)3031instance NFData1 Concolic3233hasSymbolic :: Concolic v -> Bool34hasSymbolic Concolic {symbolic = Just _} = True35hasSymbolic _ = False3637getSymbolicDef :: (v -> SE.BitVector) -> Concolic v -> SE.BitVector38getSymbolicDef conc Concolic {concrete = c, symbolic = s} =39 fromMaybe (conc c) s4041------------------------------------------------------------------------4243instance MEM.Storable (Concolic D.RegVal) (Concolic Word8) where44 toBytes Concolic {concrete = c, symbolic = s} =45 let cbytes = MEM.toBytes c46 nbytes = length cbytes47 sbytes = maybe (replicate nbytes Nothing) (map Just . MEM.toBytes) s48 in assert (nbytes == length sbytes) $49 zipWith Concolic cbytes sbytes5051 fromBytes ty bytes =52 do53 let conBytes = map concrete bytes54 con <- MEM.fromBytes ty conBytes5556 let mkConcolic = Concolic con57 if any hasSymbolic bytes58 then do59 let symBVs = map (getSymbolicDef SE.fromByte) bytes60 MEM.fromBytes ty symBVs <&> mkConcolic . Just61 else Just $ mkConcolic Nothing6263------------------------------------------------------------------------6465unaryOp ::66 (D.RegVal -> Maybe D.RegVal) ->67 (SE.BitVector -> Maybe SE.BitVector) ->68 Concolic D.RegVal ->69 Maybe (Concolic D.RegVal)70unaryOp fnCon fnSym Concolic {concrete = c, symbolic = s} = do71 c' <- fnCon c72 let con = Concolic c'73 case s of74 Just s' -> fnSym s' <&> con . Just75 Nothing -> pure $ con Nothing7677binaryOp ::78 (D.RegVal -> D.RegVal -> Maybe D.RegVal) ->79 (SE.BitVector -> SE.BitVector -> Maybe SE.BitVector) ->80 Concolic D.RegVal ->81 Concolic D.RegVal ->82 Maybe (Concolic D.RegVal)83binaryOp fnCon fnSym lhs rhs =84 do85 c <- concrete lhs `fnCon` concrete rhs86 if hasSymbolic lhs || hasSymbolic rhs87 then88 let lhsS = getSymbolicDef SE.fromReg lhs89 rhsS = getSymbolicDef SE.fromReg rhs90 in (lhsS `fnSym` rhsS) <&> Concolic c . Just91 else pure $ Concolic c Nothing9293instance E.ValueRepr (Concolic D.RegVal) where94 fromLit ty v = Concolic (E.fromLit ty v) Nothing95 fromFloat fl = Concolic (E.fromFloat fl) Nothing96 fromDouble d = Concolic (E.fromDouble d) Nothing97 toWord64 Concolic {concrete = c} = E.toWord64 c98 getType Concolic {concrete = c} = E.getType c99100 extend ty s = unaryOp (E.extend ty s) (E.extend ty s)101 extract ty = unaryOp (E.extract ty) (E.extract ty)102103 -- TODO: Add constraint which enforces concrete value on104 -- symbolic part instead of silently discarding it. See105 -- the address concretization implementation for details.106 floatToInt ty s Concolic {concrete = c} =107 (`Concolic` Nothing) <$> E.floatToInt ty s c108 intToFloat ty s Concolic {concrete = c} =109 (`Concolic` Nothing) <$> E.intToFloat ty s c110 extendFloat Concolic {concrete = c} =111 (`Concolic` Nothing) <$> E.extendFloat c112 truncFloat Concolic {concrete = c} =113 (`Concolic` Nothing) <$> E.truncFloat c114115 add = binaryOp E.add E.add116 sub = binaryOp E.sub E.sub117 mul = binaryOp E.mul E.mul118 div = binaryOp E.div E.div119 or = binaryOp E.or E.or120 xor = binaryOp E.xor E.xor121 and = binaryOp E.and E.and122 urem = binaryOp E.urem E.urem123 srem = binaryOp E.srem E.srem124 udiv = binaryOp E.udiv E.udiv125126 neg = unaryOp E.neg E.neg127128 sar = binaryOp E.sar E.sar129 shr = binaryOp E.shr E.shr130 shl = binaryOp E.shl E.shl131132 eq = binaryOp E.eq E.eq133 ne = binaryOp E.ne E.ne134 sle = binaryOp E.sle E.sle135 slt = binaryOp E.slt E.slt136 sge = binaryOp E.sge E.sge137 sgt = binaryOp E.sgt E.sgt138 ule = binaryOp E.ule E.ule139 ult = binaryOp E.ult E.ult140 uge = binaryOp E.uge E.uge141 ugt = binaryOp E.ugt E.ugt142143 -- TODO: Add constraint which enforces concrete value on144 -- symbolic part instead of silently discarding it. See145 -- the address concretization implementation for details.146 ord lhs rhs = (`Concolic` Nothing) <$> E.ord (concrete lhs) (concrete rhs)