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.Exception (assert)12import Data.Functor ((<&>))13import Data.Maybe (fromMaybe)14import Data.Word (Word8)15import Language.QBE.Simulator.Default.Expression qualified as D16import Language.QBE.Simulator.Expression qualified as E17import Language.QBE.Simulator.Memory qualified as MEM18import Language.QBE.Simulator.Symbolic.Expression qualified as SE1920data Concolic v21 = Concolic22 { concrete :: v,23 symbolic :: Maybe SE.BitVector24 }25 deriving (Show)2627hasSymbolic :: Concolic v -> Bool28hasSymbolic Concolic {symbolic = Just _} = True29hasSymbolic _ = False3031getSymbolicDef :: (v -> SE.BitVector) -> Concolic v -> SE.BitVector32getSymbolicDef conc Concolic {concrete = c, symbolic = s} =33 fromMaybe (conc c) s3435------------------------------------------------------------------------3637instance MEM.Storable (Concolic D.RegVal) (Concolic Word8) where38 toBytes Concolic {concrete = c, symbolic = s} =39 let cbytes = MEM.toBytes c40 nbytes = length cbytes41 sbytes = maybe (replicate nbytes Nothing) (map Just . MEM.toBytes) s42 in assert (nbytes == length sbytes) $43 zipWith Concolic cbytes sbytes4445 fromBytes ty bytes =46 do47 let conBytes = map concrete bytes48 con <- MEM.fromBytes ty conBytes4950 let mkConcolic = Concolic con51 if any hasSymbolic bytes52 then do53 let symBVs = map (getSymbolicDef SE.fromByte) bytes54 MEM.fromBytes ty symBVs <&> mkConcolic . Just55 else Just $ mkConcolic Nothing5657------------------------------------------------------------------------5859unaryOp ::60 (D.RegVal -> Maybe D.RegVal) ->61 (SE.BitVector -> Maybe SE.BitVector) ->62 Concolic D.RegVal ->63 Maybe (Concolic D.RegVal)64unaryOp fnCon fnSym Concolic {concrete = c, symbolic = s} = do65 c' <- fnCon c66 let con = Concolic c'67 case s of68 Just s' -> fnSym s' <&> con . Just69 Nothing -> pure $ con Nothing7071binaryOp ::72 (D.RegVal -> D.RegVal -> Maybe D.RegVal) ->73 (SE.BitVector -> SE.BitVector -> Maybe SE.BitVector) ->74 Concolic D.RegVal ->75 Concolic D.RegVal ->76 Maybe (Concolic D.RegVal)77binaryOp fnCon fnSym lhs rhs =78 do79 c <- concrete lhs `fnCon` concrete rhs80 if hasSymbolic lhs || hasSymbolic rhs81 then82 let lhsS = getSymbolicDef SE.fromReg lhs83 rhsS = getSymbolicDef SE.fromReg rhs84 in (lhsS `fnSym` rhsS) <&> Concolic c . Just85 else pure $ Concolic c Nothing8687instance E.ValueRepr (Concolic D.RegVal) where88 fromLit ty v = Concolic (E.fromLit ty v) Nothing89 fromFloat fl = Concolic (E.fromFloat fl) Nothing90 fromDouble d = Concolic (E.fromDouble d) Nothing91 toWord64 Concolic {concrete = c} = E.toWord64 c92 getType Concolic {concrete = c} = E.getType c9394 extend ty s = unaryOp (E.extend ty s) (E.extend ty s)95 extract ty = unaryOp (E.extract ty) (E.extract ty)9697 -- TODO: Add constraint which enforces concrete value on98 -- symbolic part instead of silently discarding it. See99 -- the address concretization implementation for details.100 floatToInt ty s Concolic {concrete = c} =101 (`Concolic` Nothing) <$> E.floatToInt ty s c102 intToFloat ty s Concolic {concrete = c} =103 (`Concolic` Nothing) <$> E.intToFloat ty s c104 extendFloat Concolic {concrete = c} =105 (`Concolic` Nothing) <$> E.extendFloat c106 truncFloat Concolic {concrete = c} =107 (`Concolic` Nothing) <$> E.truncFloat c108109 add = binaryOp E.add E.add110 sub = binaryOp E.sub E.sub111 mul = binaryOp E.mul E.mul112 div = binaryOp E.div E.div113 or = binaryOp E.or E.or114 xor = binaryOp E.xor E.xor115 and = binaryOp E.and E.and116 urem = binaryOp E.urem E.urem117 srem = binaryOp E.srem E.srem118 udiv = binaryOp E.udiv E.udiv119120 neg = unaryOp E.neg E.neg121122 sar = binaryOp E.sar E.sar123 shr = binaryOp E.shr E.shr124 shl = binaryOp E.shl E.shl125126 eq = binaryOp E.eq E.eq127 ne = binaryOp E.ne E.ne128 sle = binaryOp E.sle E.sle129 slt = binaryOp E.slt E.slt130 sge = binaryOp E.sge E.sge131 sgt = binaryOp E.sgt E.sgt132 ule = binaryOp E.ule E.ule133 ult = binaryOp E.ult E.ult134 uge = binaryOp E.uge E.uge135 ugt = binaryOp E.ugt E.ugt136137 -- TODO: Add constraint which enforces concrete value on138 -- symbolic part instead of silently discarding it. See139 -- the address concretization implementation for details.140 ord lhs rhs = (`Concolic` Nothing) <$> E.ord (concrete lhs) (concrete rhs)