1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>
2--
3-- SPDX-License-Identifier: GPL-3.0-only
4
5module Language.QBE.Simulator.Symbolic.Expression
6 ( BitVector,
7 fromByte,
8 fromReg,
9 fromSExpr,
10 toSExpr,
11 symbolic,
12 toCond,
13 )
14where
15
16import Control.Exception (assert)
17import Data.Word (Word8)
18import Language.QBE.Simulator.Default.Expression qualified as D
19import Language.QBE.Simulator.Expression qualified as E
20import Language.QBE.Simulator.Memory qualified as MEM
21import Language.QBE.Types qualified as QBE
22import SimpleSMT qualified as SMT
23
24-- This types supports more than just the 'QBE.BaseType' to ease the
25-- implementation of the 'MEM.Storable' type class. Essentially, this
26-- data type just provides a generic BitVector abstraction on top of
27-- 'SMT.SExpr'.
28data BitVector
29 = BitVector
30 { sexpr :: SMT.SExpr,
31 -- TODO: For BitVectors, we can extract the type from the SExpr.
32 -- Technically, we don't need the 'ExtType' here and can just
33 -- turn this into a newtype.
34 qtype :: QBE.ExtType
35 }
36 deriving (Show, Eq)
37
38fromByte :: Word8 -> BitVector
39fromByte byte = BitVector (SMT.bvBin 8 $ fromIntegral byte) QBE.Byte
40
41fromReg :: D.RegVal -> BitVector
42fromReg (D.VWord v) = BitVector (SMT.bvBin 32 $ fromIntegral v) (QBE.Base QBE.Word)
43fromReg (D.VLong v) = BitVector (SMT.bvBin 64 $ fromIntegral v) (QBE.Base QBE.Long)
44fromReg (D.VSingle _) = error "symbolic floats not supported"
45fromReg (D.VDouble _) = error "symbolic doubles not supported"
46
47fromSExpr :: QBE.BaseType -> SMT.SExpr -> BitVector
48fromSExpr ty sexpr = BitVector sexpr (QBE.Base ty)
49
50toSExpr :: BitVector -> SMT.SExpr
51toSExpr = sexpr
52
53symbolic :: SMT.Solver -> String -> QBE.BaseType -> IO BitVector
54symbolic solver name ty = do
55 let bits = SMT.tBits $ fromIntegral (QBE.baseTypeBitSize ty)
56 sym <- SMT.declare solver name bits
57 return $ BitVector sym (QBE.Base ty)
58
59-- In the QBE a condition (see `jnz`) is true if the Word value is not zero.
60toCond :: Bool -> BitVector -> SMT.SExpr
61toCond isTrue BitVector {sexpr = s, qtype = ty} =
62 -- Equality is only defined for Words.
63 assert (ty == QBE.Base QBE.Word) $
64 let zeroSExpr = sexpr (fromReg $ E.fromLit QBE.Word 0)
65 in toCond' s zeroSExpr
66 where
67 toCond' lhs rhs
68 | isTrue = SMT.not (SMT.eq lhs rhs) -- /= 0
69 | otherwise = SMT.eq lhs rhs -- == 0
70
71------------------------------------------------------------------------
72
73instance MEM.Storable BitVector BitVector where
74 toBytes BitVector {sexpr = s, qtype = ty} =
75 assert (size `mod` 8 == 0) $
76 map (\n -> BitVector (nthByte s n) QBE.Byte) [1 .. size `div` 8]
77 where
78 size :: Integer
79 size = fromIntegral $ QBE.extTypeBitSize ty
80
81 nthByte :: SMT.SExpr -> Integer -> SMT.SExpr
82 nthByte expr n = SMT.extract expr ((n * 8) - 1) ((n - 1) * 8)
83
84 fromBytes _ [] = Nothing
85 fromBytes ty bytes@(BitVector {sexpr = s} : xs) =
86 if length bytes /= fromIntegral (QBE.loadByteSize ty)
87 then Nothing
88 else case (ty, bytes) of
89 (QBE.LSubWord QBE.UnsignedByte, [_]) ->
90 Just (BitVector (SMT.zeroExtend 24 concated) (QBE.Base QBE.Word))
91 (QBE.LSubWord QBE.SignedByte, [_]) ->
92 Just (BitVector (SMT.signExtend 24 concated) (QBE.Base QBE.Word))
93 (QBE.LSubWord QBE.SignedHalf, [_, _]) ->
94 Just (BitVector (SMT.signExtend 16 concated) (QBE.Base QBE.Word))
95 (QBE.LSubWord QBE.UnsignedHalf, [_, _]) ->
96 Just (BitVector (SMT.zeroExtend 16 concated) (QBE.Base QBE.Word))
97 (QBE.LBase QBE.Word, [_, _, _, _]) ->
98 Just (BitVector concated (QBE.Base QBE.Word))
99 (QBE.LBase QBE.Long, [_, _, _, _, _, _, _, _]) ->
100 Just (BitVector concated (QBE.Base QBE.Long))
101 (QBE.LBase QBE.Single, [_, _, _, _]) ->
102 error "float loading not implemented"
103 (QBE.LBase QBE.Double, [_, _, _, _, _, _, _, _]) ->
104 error "double loading not implemented"
105 _ -> Nothing
106 where
107 concated :: SMT.SExpr
108 concated = foldl concatBV s xs
109
110 concatBV :: SMT.SExpr -> BitVector -> SMT.SExpr
111 concatBV acc byte =
112 assert (qtype byte == QBE.Byte) $
113 SMT.concat (sexpr byte) acc
114
115------------------------------------------------------------------------
116
117binaryOp :: (SMT.SExpr -> SMT.SExpr -> SMT.SExpr) -> BitVector -> BitVector -> Maybe BitVector
118binaryOp op lhs@(BitVector {sexpr = slhs}) rhs@(BitVector {sexpr = srhs})
119 | qtype lhs == qtype rhs = Just $ lhs {sexpr = slhs `op` srhs}
120 | otherwise = Nothing
121
122binaryBoolOp :: (SMT.SExpr -> SMT.SExpr -> SMT.SExpr) -> BitVector -> BitVector -> Maybe BitVector
123binaryBoolOp op lhs rhs = do
124 bv <- binaryOp op lhs rhs
125 -- TODO: Can we get rid of the ITE somehow?
126 return $ fromSExpr QBE.Long (SMT.ite (toSExpr bv) trueValue falseValue)
127 where
128 -- TODO: Declare these as constants.
129 trueValue :: SMT.SExpr
130 trueValue = toSExpr $ E.fromLit QBE.Long 1
131
132 falseValue :: SMT.SExpr
133 falseValue = toSExpr $ E.fromLit QBE.Long 0
134
135-- TODO: If we Change E.ValueRepr to operate in 'Exec' then we can do IO stuff here.
136instance E.ValueRepr BitVector where
137 fromLit ty n =
138 let exty = QBE.Base ty
139 size = fromIntegral $ QBE.extTypeBitSize exty
140 in BitVector (SMT.bvBin size $ fromIntegral n) exty
141
142 fromFloat = error "symbolic floats currently unsupported"
143 fromDouble = error "symbolic doubles currently unsupported"
144
145 -- XXX: This only works for constants values, but this is fine since we implement
146 -- concolic execution and can obtain the address from the concrete value part.
147 toAddress addr =
148 case SMT.sexprToVal (sexpr addr) of
149 -- TODO: Don't hardcode address type
150 SMT.Bits 64 n -> Just $ fromIntegral n
151 _ -> Nothing
152 fromAddress addr = fromReg (E.fromLit QBE.Long addr)
153
154 wordToLong (QBE.SLSubWord QBE.SignedByte) (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
155 Just $ BitVector (SMT.signExtend 56 (SMT.extract s 7 0)) (QBE.Base QBE.Long)
156 wordToLong (QBE.SLSubWord QBE.UnsignedByte) (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
157 Just $ BitVector (SMT.zeroExtend 56 (SMT.extract s 7 0)) (QBE.Base QBE.Long)
158 wordToLong (QBE.SLSubWord QBE.SignedHalf) (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
159 Just $ BitVector (SMT.signExtend 48 (SMT.extract s 15 0)) (QBE.Base QBE.Long)
160 wordToLong (QBE.SLSubWord QBE.UnsignedHalf) (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
161 Just $ BitVector (SMT.zeroExtend 48 (SMT.extract s 15 0)) (QBE.Base QBE.Long)
162 wordToLong QBE.SLSignedWord (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
163 Just $ BitVector (SMT.signExtend 32 s) (QBE.Base QBE.Long)
164 wordToLong QBE.SLUnsignedWord (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
165 Just $ BitVector (SMT.zeroExtend 32 s) (QBE.Base QBE.Long)
166 wordToLong _ _ = Nothing
167
168 subType QBE.Word v@(BitVector {qtype = QBE.Base QBE.Word}) = Just v
169 subType QBE.Word (BitVector {qtype = QBE.Base QBE.Long, sexpr = s}) =
170 Just $ BitVector (SMT.extract s 31 0) (QBE.Base QBE.Word)
171 subType QBE.Long v@(BitVector {qtype = QBE.Base QBE.Long}) = Just v
172 subType QBE.Single v@(BitVector {qtype = QBE.Base QBE.Single}) = Just v
173 subType QBE.Double v@(BitVector {qtype = QBE.Base QBE.Double}) = Just v
174 subType _ _ = Nothing
175
176 -- XXX: This only works for constants values, but this is fine (see above).
177 isZero bv =
178 case SMT.sexprToVal (sexpr bv) of
179 SMT.Bits _ v -> v == 0
180 _ -> error "unreachable"
181
182 add = binaryOp SMT.bvAdd
183 sub = binaryOp SMT.bvSub
184 mul = binaryOp SMT.bvMul
185 or = binaryOp SMT.bvOr
186 xor = binaryOp SMT.bvXOr
187 and = binaryOp SMT.bvAnd
188 urem = binaryOp SMT.bvURem
189 srem = binaryOp SMT.bvSRem
190 udiv = binaryOp SMT.bvUDiv
191
192 eq = binaryBoolOp SMT.eq
193 ne = binaryBoolOp (\lhs rhs -> SMT.not $ SMT.eq lhs rhs)
194 sle = binaryBoolOp SMT.bvSLeq
195 slt = binaryBoolOp SMT.bvSLt
196 sge = binaryBoolOp (\lhs rhs -> SMT.not $ SMT.bvSLt lhs rhs)
197 sgt = binaryBoolOp (\lhs rhs -> SMT.not $ SMT.bvSLeq lhs rhs)
198 ule = binaryBoolOp SMT.bvULeq
199 ult = binaryBoolOp SMT.bvULt
200 uge = binaryBoolOp (\lhs rhs -> SMT.not $ SMT.bvULt lhs rhs)
201 ugt = binaryBoolOp (\lhs rhs -> SMT.not $ SMT.bvULeq lhs rhs)