1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>2--3-- SPDX-License-Identifier: GPL-3.0-only45module Language.QBE.Simulator.Symbolic.Expression6 ( BitVector,7 fromByte,8 fromReg,9 toSExpr,10 symbolic,11 bitSize,12 toCond,13 )14where1516import Control.Exception (assert)17import Data.Bits (shiftL, (.&.))18import Data.Word (Word64, Word8)19import Language.QBE.Simulator.Default.Expression qualified as D20import Language.QBE.Simulator.Expression qualified as E21import Language.QBE.Simulator.Memory qualified as MEM22import Language.QBE.Types qualified as QBE23import SimpleBV qualified as SMT2425-- TODO: Floating point support.26newtype BitVector = BitVector SMT.SExpr27 deriving (Show, Eq)2829fromByte :: Word8 -> BitVector30fromByte byte = BitVector (SMT.bvLit 8 $ fromIntegral byte)3132fromReg :: D.RegVal -> BitVector33fromReg (D.VByte v) = BitVector (SMT.bvLit 8 $ fromIntegral v)34fromReg (D.VHalf v) = BitVector (SMT.bvLit 16 $ fromIntegral v)35fromReg (D.VWord v) = BitVector (SMT.bvLit 32 $ fromIntegral v)36fromReg (D.VLong v) = BitVector (SMT.bvLit 64 $ fromIntegral v)37fromReg (D.VSingle _) = error "symbolic floats not supported"38fromReg (D.VDouble _) = error "symbolic doubles not supported"3940toSExpr :: BitVector -> SMT.SExpr41toSExpr (BitVector s) = s4243symbolic :: String -> QBE.ExtType -> BitVector44symbolic name ty = BitVector (SMT.const name $ QBE.extTypeBitSize ty)4546bitSize :: BitVector -> Int47bitSize = SMT.width . toSExpr4849-- In the QBE a condition (see `jnz`) is true if the Word value is not zero.50toCond :: Bool -> BitVector -> SMT.SExpr51toCond isTrue bv =52 -- Equality is only defined for Words.53 assert (bitSize bv == QBE.baseTypeBitSize QBE.Word) $54 let zeroSExpr = toSExpr (fromReg $ E.fromLit (QBE.Base QBE.Word) 0)55 in toCond' (toSExpr bv) zeroSExpr56 where57 toCond' lhs rhs58 | isTrue = SMT.not (SMT.eq lhs rhs) -- /= 059 | otherwise = SMT.eq lhs rhs -- == 06061------------------------------------------------------------------------6263instance MEM.Storable BitVector BitVector where64 toBytes (BitVector s) =65 assert (size `mod` 8 == 0) $66 map (BitVector . nthByte s) [1 .. fromIntegral size `div` 8]67 where68 size :: Integer69 size = fromIntegral $ SMT.width s7071 nthByte :: SMT.SExpr -> Int -> SMT.SExpr72 nthByte expr n = SMT.extract expr ((n - 1) * 8) 87374 fromBytes _ [] = Nothing75 fromBytes ty bytes@(BitVector s : xs) =76 if length bytes /= fromIntegral (QBE.loadByteSize ty)77 then Nothing78 else case (ty, bytes) of79 (QBE.LSubWord QBE.UnsignedByte, [_]) ->80 Just (BitVector (SMT.zeroExtend 24 concated))81 (QBE.LSubWord QBE.SignedByte, [_]) ->82 Just (BitVector (SMT.signExtend 24 concated))83 (QBE.LSubWord QBE.SignedHalf, [_, _]) ->84 Just (BitVector (SMT.signExtend 16 concated))85 (QBE.LSubWord QBE.UnsignedHalf, [_, _]) ->86 Just (BitVector (SMT.zeroExtend 16 concated))87 (QBE.LBase QBE.Word, [_, _, _, _]) ->88 Just (BitVector concated)89 (QBE.LBase QBE.Long, [_, _, _, _, _, _, _, _]) ->90 Just (BitVector concated)91 (QBE.LBase QBE.Single, [_, _, _, _]) ->92 error "float loading not implemented"93 (QBE.LBase QBE.Double, [_, _, _, _, _, _, _, _]) ->94 error "double loading not implemented"95 _ -> Nothing96 where97 concated :: SMT.SExpr98 concated = foldl concatBV s xs99100 concatBV :: SMT.SExpr -> BitVector -> SMT.SExpr101 concatBV acc (BitVector byte) =102 assert (SMT.width byte == 8) $103 SMT.concat byte acc104105------------------------------------------------------------------------106107binaryOp :: (SMT.SExpr -> SMT.SExpr -> SMT.SExpr) -> BitVector -> BitVector -> Maybe BitVector108binaryOp op (BitVector lhs) (BitVector rhs)109 | SMT.width lhs == SMT.width rhs = Just $ BitVector (lhs `op` rhs)110 | otherwise = Nothing111112-- TODO: Move this into the expression abstraction.113toShiftAmount :: Word64 -> BitVector -> Maybe BitVector114toShiftAmount size amount = amount `E.urem` E.fromLit (QBE.Base QBE.Word) size115116shiftOp :: (SMT.SExpr -> SMT.SExpr -> SMT.SExpr) -> BitVector -> BitVector -> Maybe BitVector117shiftOp op value amount@(BitVector SMT.Word) =118 case bitSize value of119 32 -> toShiftAmount 32 amount >>= binaryOp op value120 64 -> do121 shiftAmount <- toShiftAmount 64 amount122 E.extend (QBE.Base QBE.Long) False shiftAmount >>= binaryOp op value123 _ -> Nothing124shiftOp _ _ _ = Nothing -- Shift amount must always be a Word.125126binaryBoolOp :: (SMT.SExpr -> SMT.SExpr -> SMT.SExpr) -> BitVector -> BitVector -> Maybe BitVector127binaryBoolOp op lhs rhs = do128 bv <- binaryOp op lhs rhs129 return $ BitVector (SMT.ite (toSExpr bv) trueValue falseValue)130 where131 -- TODO: Declare these as constants.132 trueValue :: SMT.SExpr133 trueValue = toSExpr $ E.fromLit (QBE.Base QBE.Long) 1134135 falseValue :: SMT.SExpr136 falseValue = toSExpr $ E.fromLit (QBE.Base QBE.Long) 0137138instance E.ValueRepr BitVector where139 fromLit ty n =140 let size = QBE.extTypeBitSize ty141 mask = (1 `shiftL` size) - 1142 in BitVector $ SMT.bvLit (fromIntegral size) $ fromIntegral (n .&. mask)143144 fromFloat = error "symbolic floats currently unsupported"145 fromDouble = error "symbolic doubles currently unsupported"146147 -- XXX: This only works for constants values, but this is fine since we implement148 -- concolic execution and can obtain the address from the concrete value part.149 toWord64 (BitVector value) =150 case SMT.sexprToVal value of151 SMT.Bits _ n -> fromIntegral n152 _ -> error "unrechable"153154 getType v = case bitSize v of155 08 -> QBE.Byte156 16 -> QBE.HalfWord157 32 -> QBE.Base QBE.Word158 64 -> QBE.Base QBE.Long159 _ -> error "unreachable"160161 floatToInt = error "symbolic float conversion not supported"162 intToFloat = error "symbolic float conversion not supported"163 extendFloat = error "symbolic float extension not supported"164 truncFloat = error "symbolic float trunction not supported"165166 extend extTy isSigned val@(BitVector s)167 | QBE.extTypeBitSize extTy <= bitSize val = Nothing168 | otherwise = Just $ BitVector (extFunc targetSize s)169 where170 targetSize :: Integer171 targetSize = fromIntegral $ QBE.extTypeBitSize extTy - bitSize val172173 extFunc :: Integer -> SMT.SExpr -> SMT.SExpr174 extFunc = if isSigned then SMT.signExtend else SMT.zeroExtend175176 extract extTy val@(BitVector s)177 | QBE.extTypeBitSize extTy > bitSize val = Nothing178 | otherwise = Just $ BitVector (SMT.extract s 0 $ QBE.extTypeBitSize extTy)179180 add = binaryOp SMT.bvAdd181 sub = binaryOp SMT.bvSub182 mul = binaryOp SMT.bvMul183 div = binaryOp SMT.bvSDiv184 or = binaryOp SMT.bvOr185 xor = binaryOp SMT.bvXOr186 and = binaryOp SMT.bvAnd187 urem = binaryOp SMT.bvURem188 srem = binaryOp SMT.bvSRem189 udiv = binaryOp SMT.bvUDiv190191 neg (BitVector v) = Just $ BitVector (SMT.bvNeg v)192193 sar = shiftOp SMT.bvAShr194 shr = shiftOp SMT.bvLShr195 shl = shiftOp SMT.bvShl196197 eq = binaryBoolOp SMT.eq198 ne = binaryBoolOp (\lhs rhs -> SMT.not $ SMT.eq lhs rhs)199 sle = binaryBoolOp SMT.bvSLeq200 slt = binaryBoolOp SMT.bvSLt201 sge = binaryBoolOp (flip SMT.bvSLeq)202 sgt = binaryBoolOp (flip SMT.bvSLt)203 ule = binaryBoolOp SMT.bvULeq204 ult = binaryBoolOp SMT.bvULt205 uge = binaryBoolOp (\lhs rhs -> SMT.or (SMT.bvULt rhs lhs) (SMT.eq lhs rhs))206 ugt = binaryBoolOp (flip SMT.bvULt)207208 ord = error "symbolic ordered comparison not supported"209 unord = error "symbolic unordered comparison not supported"