1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>2--3-- SPDX-License-Identifier: MIT AND GPL-3.0-only4{-# LANGUAGE PatternSynonyms #-}56module SimpleBV7 ( SExpr,8 SMT.Solver,9 SMT.defaultConfig,10 SMT.newLogger,11 SMT.newLoggerWithHandle,12 SMT.newSolver,13 SMT.newSolverWithConfig,14 SMT.solverLogger,15 SMT.smtSolverLogger,16 SMT.setLogic,17 SMT.push,18 SMT.pop,19 SMT.popMany,20 SMT.check,21 SMT.Result (..),22 SMT.Value (..),23 pattern W,24 pattern Byte,25 pattern Half,26 pattern Word,27 pattern Long,28 width,29 const,30 declareBV,31 assert,32 sexprToVal,33 getValue,34 getValues,35 toSMT,36 ite,37 and,38 or,39 not,40 eq,41 bvLit,42 bvAdd,43 bvAShr,44 bvLShr,45 bvAnd,46 bvMul,47 bvNeg,48 bvOr,49 bvSDiv,50 bvSLeq,51 bvSLt,52 bvSRem,53 bvShl,54 bvSub,55 bvUDiv,56 bvULeq,57 bvULt,58 bvURem,59 bvXOr,60 concat,61 extract,62 signExtend,63 zeroExtend,64 )65where6667import Data.Bits (shiftL, shiftR, (.&.))68import SimpleSMT qualified as SMT69import Prelude hiding (and, concat, const, not, or)7071data Expr a72 = Var String73 | Int Integer74 | And a a75 | Or a a76 | Neg a77 | Not a78 | Eq a a79 | BvAdd a a80 | BvAShr a a81 | BvLShr a a82 | BvAnd a a83 | BvMul a a84 | BvOr a a85 | BvSDiv a a86 | BvSLeq a a87 | BvSLt a a88 | BvSRem a a89 | BvShl a a90 | BvSub a a91 | BvUDiv a a92 | BvULeq a a93 | BvULt a a94 | BvURem a a95 | BvXOr a a96 | Concat a a97 | Ite a a a98 | Extract Int Int a99 | SignExtend Integer a100 | ZeroExtend Integer a101 deriving (Show, Eq)102103data SExpr104 = SExpr105 { width :: Int,106 sexpr :: Expr SExpr107 }108 deriving (Show, Eq)109110toSMT :: SExpr -> SMT.SExpr111toSMT expr =112 case sexpr expr of113 (Var name) -> SMT.const name114 (Int v) -> SMT.bvHex (width expr) v115 (Or lhs rhs) -> SMT.or (toSMT lhs) (toSMT rhs)116 (Ite cond lhs rhs) -> SMT.ite (toSMT cond) (toSMT lhs) (toSMT rhs)117 (And lhs rhs) -> SMT.and (toSMT lhs) (toSMT rhs)118 (Not v) -> SMT.not (toSMT v)119 (Neg v) -> SMT.bvNeg (toSMT v)120 (SignExtend n v) -> SMT.signExtend n (toSMT v)121 (ZeroExtend n v) -> SMT.zeroExtend n (toSMT v)122 (Eq lhs rhs) -> SMT.eq (toSMT lhs) (toSMT rhs)123 (Concat lhs rhs) -> SMT.concat (toSMT lhs) (toSMT rhs)124 (Extract o w e) -> SMT.extract (toSMT e) (fromIntegral $ o + w - 1) (fromIntegral o)125 (BvAnd lhs rhs) -> SMT.bvAnd (toSMT lhs) (toSMT rhs)126 (BvAShr lhs rhs) -> SMT.bvAShr (toSMT lhs) (toSMT rhs)127 (BvLShr lhs rhs) -> SMT.bvLShr (toSMT lhs) (toSMT rhs)128 (BvAdd lhs rhs) -> SMT.bvAdd (toSMT lhs) (toSMT rhs)129 (BvMul lhs rhs) -> SMT.bvMul (toSMT lhs) (toSMT rhs)130 (BvOr lhs rhs) -> SMT.bvOr (toSMT lhs) (toSMT rhs)131 (BvSDiv lhs rhs) -> SMT.bvSDiv (toSMT lhs) (toSMT rhs)132 (BvSLeq lhs rhs) -> SMT.bvSLeq (toSMT lhs) (toSMT rhs)133 (BvSLt lhs rhs) -> SMT.bvSLt (toSMT lhs) (toSMT rhs)134 (BvSRem lhs rhs) -> SMT.bvSRem (toSMT lhs) (toSMT rhs)135 (BvShl lhs rhs) -> SMT.bvShl (toSMT lhs) (toSMT rhs)136 (BvSub lhs rhs) -> SMT.bvSub (toSMT lhs) (toSMT rhs)137 (BvUDiv lhs rhs) -> SMT.bvUDiv (toSMT lhs) (toSMT rhs)138 (BvULeq lhs rhs) -> SMT.bvULeq (toSMT lhs) (toSMT rhs)139 (BvULt lhs rhs) -> SMT.bvULt (toSMT lhs) (toSMT rhs)140 (BvURem lhs rhs) -> SMT.bvURem (toSMT lhs) (toSMT rhs)141 (BvXOr lhs rhs) -> SMT.bvXOr (toSMT lhs) (toSMT rhs)142143boolWidth :: Int144boolWidth = 1145146pattern E :: Expr SExpr -> SExpr147pattern E expr <- SExpr {sexpr = expr, width = _}148149pattern W :: Int -> SExpr150pattern W w <- SExpr {width = w}151152pattern Byte :: SExpr153pattern Byte <- SExpr {width = 8}154155pattern Half :: SExpr156pattern Half <- SExpr {width = 16}157158pattern Word :: SExpr159pattern Word <- SExpr {width = 32}160161pattern Long :: SExpr162pattern Long <- SExpr {width = 64}163164------------------------------------------------------------------------165166const :: String -> Int -> SExpr167const name width = SExpr width (Var name)168169declareBV :: SMT.Solver -> String -> Int -> IO SExpr170declareBV solver name width = do171 let bits = SMT.tBits $ fromIntegral width172 SMT.declare solver name bits >> pure (const name width)173174bvLit :: Int -> Integer -> SExpr175bvLit width value = SExpr width (Int value)176177sexprToVal :: SExpr -> SMT.Value178sexprToVal (E (Var n)) = SMT.Other $ SMT.Atom n179sexprToVal e@(E (Int i)) = SMT.Bits (width e) i180sexprToVal _ = SMT.Other $ SMT.Atom "_"181182assert :: SMT.Solver -> SExpr -> IO ()183assert solver = SMT.assert solver . toSMT184185getValue :: SMT.Solver -> SExpr -> IO SMT.Value186getValue solver = SMT.getExpr solver . toSMT187188getValues :: SMT.Solver -> [SExpr] -> IO [(String, SMT.Value)]189getValues solver exprs = do190 map go <$> SMT.getExprs solver (map toSMT exprs)191 where192 go :: (SMT.SExpr, SMT.Value) -> (String, SMT.Value)193 go (SMT.Atom name, value) = (name, value)194 go _ = error "non-atomic variable in inputVars"195196---------------------------------------------------------------------------197198ite :: SExpr -> SExpr -> SExpr -> SExpr199ite cond ifT ifF = SExpr (width ifT) (Ite cond ifT ifF)200201not :: SExpr -> SExpr202not (E (Not cond)) = cond203not expr = expr {sexpr = Not expr}204205and :: SExpr -> SExpr -> SExpr206and lhs rhs = lhs {sexpr = And lhs rhs}207208or :: SExpr -> SExpr -> SExpr209or lhs rhs = lhs {sexpr = Or lhs rhs}210211signExtend :: Integer -> SExpr -> SExpr212signExtend n expr = SExpr (width expr + fromIntegral n) $ SignExtend n expr213214zeroExtend :: Integer -> SExpr -> SExpr215zeroExtend n expr = SExpr (width expr + fromIntegral n) $ ZeroExtend n expr216217------------------------------------------------------------------------218219eq' :: SExpr -> SExpr -> SExpr220eq' lhs rhs = SExpr boolWidth $ Eq lhs rhs221222-- Eliminates ITE expressions when comparing with constants values, this is223-- useful in the QBE context to eliminate comparisons with truth values.224eq :: SExpr -> SExpr -> SExpr225eq lexpr@(E (Ite cond (E (Int ifT)) (E (Int ifF)))) rexpr@(E (Int other))226 | other == ifT = cond227 | other == ifF = not cond228 | otherwise = eq' lexpr rexpr229eq lhs rhs = eq' lhs rhs230231concat' :: SExpr -> SExpr -> SExpr232concat' lhs rhs =233 SExpr (width lhs + width rhs) $ Concat lhs rhs234235-- Replaces continuous concat expressions with a single extract expression.236concat :: SExpr -> SExpr -> SExpr237concat238 lhs@(E (Extract loff lwidth latom@(E exprLhs)))239 rhs@(E (Extract roff rwidth (E exprRhs)))240 | exprLhs == exprRhs && (roff + rwidth) == loff = extract latom roff (lwidth + rwidth)241 | otherwise = concat' lhs rhs242concat lhs rhs = concat' lhs rhs243244extract' :: SExpr -> Int -> Int -> SExpr245extract' expr off w = SExpr w $ Extract off w expr246247-- Eliminate extract expression where the value already has the desired bits.248extractSameWidth :: SExpr -> Int -> Int -> SExpr249extractSameWidth expr off w250 | off == 0 && width expr == w = expr251 | otherwise = extract' expr off w252253-- Eliminate nested extract expression of the same width.254extractNested :: SExpr -> Int -> Int -> SExpr255extractNested expr@(E (Extract ioff iwidth _)) off width =256 if ioff == off && iwidth == width257 then expr258 else extractSameWidth expr off width259extractNested expr off width = extractSameWidth expr off width260261-- Performs direct extractions of constant immediate values.262extractConst :: SExpr -> Int -> Int -> SExpr263extractConst (E (Int value)) off w =264 SExpr w . Int $ truncTo (value `shiftR` off) w265 where266 truncTo v bits = v .&. ((1 `shiftL` bits) - 1)267extractConst expr off width = extractNested expr off width268269-- This performs constant propagation for subtyping of condition values (i.e.270-- the conversion from long to word).271extractIte :: SExpr -> Int -> Int -> SExpr272extractIte (E (Ite cond ifT@(E (Int _)) ifF@(E (Int _)))) off w =273 let ex x = extractConst x off w274 in SExpr w $ Ite cond (ex ifT) (ex ifF)275extractIte expr off width = extractConst expr off width276277extractExt' ::278 (Integer -> SExpr -> Expr SExpr) ->279 SExpr ->280 Integer ->281 SExpr ->282 Int ->283 Int ->284 SExpr285extractExt' cons outer extBits inner exOff exWidth286 -- If we are only extracting the non-extended bytes...287 | width inner >= exOff + exWidth = extractIte inner exOff exWidth288 -- Consider: ((_ extract 31 0) ((_ zero_extend 56) byte))289 | exWidth < fromIntegral extBits && exOff == 0 =290 SExpr exWidth $ cons (extBits - fromIntegral exWidth) inner291 -- No folding...292 | otherwise = extractIte outer exOff exWidth293294-- Remove ZeroExtend and SignExtend expression where we don't use295-- the extended bits because we extract below the extended size.296extractExt :: SExpr -> Int -> Int -> SExpr297extractExt expr@(E (SignExtend extBits inner)) exOff exWidth =298 extractExt' SignExtend expr extBits inner exOff exWidth299extractExt expr@(E (ZeroExtend extBits inner)) exOff exWidth =300 extractExt' ZeroExtend expr extBits inner exOff exWidth301extractExt expr off w = extractIte expr off w302303extract :: SExpr -> Int -> Int -> SExpr304extract = extractExt305306------------------------------------------------------------------------307308binOp' :: (SExpr -> SExpr -> Expr SExpr) -> SExpr -> SExpr -> SExpr309binOp' op lhs rhs = lhs {sexpr = op lhs rhs}310311binOp :: (SExpr -> SExpr -> Expr SExpr) -> SExpr -> SExpr -> SExpr312-- Consider: (bvslt ((_ zero_extend 24) byte0) ((_ zero_extend 24) byte1))313-- TODO: The following only works if 'op' does not consider sign-bits. Otherwise,314-- there is no semantic expression equivalence after this folding operation.315-- binOp op lhs@(E (ZeroExtend _ lhsInner)) rhs@(E (ZeroExtend _ rhsInner)) =316-- if width lhsInner == width rhsInner317-- then binOp op lhsInner rhsInner318-- else binOp' op lhs rhs319binOp op lhs rhs = binOp' op lhs rhs320321-- TODO: Generate these using template-haskell.322323bvNeg :: SExpr -> SExpr324bvNeg x = x {sexpr = Neg x}325326bvAdd :: SExpr -> SExpr -> SExpr327bvAdd = binOp BvAdd328329bvAShr :: SExpr -> SExpr -> SExpr330bvAShr = binOp BvAShr331332bvLShr :: SExpr -> SExpr -> SExpr333bvLShr = binOp BvLShr334335bvAnd :: SExpr -> SExpr -> SExpr336bvAnd = binOp BvAnd337338bvMul :: SExpr -> SExpr -> SExpr339bvMul = binOp BvMul340341bvOr :: SExpr -> SExpr -> SExpr342bvOr = binOp BvOr343344bvSDiv :: SExpr -> SExpr -> SExpr345bvSDiv = binOp BvSDiv346347bvSLeq :: SExpr -> SExpr -> SExpr348bvSLeq = binOp BvSLeq349350bvSLt :: SExpr -> SExpr -> SExpr351bvSLt = binOp BvSLt352353bvSRem :: SExpr -> SExpr -> SExpr354bvSRem = binOp BvSRem355356bvShl :: SExpr -> SExpr -> SExpr357bvShl = binOp BvShl358359bvSub :: SExpr -> SExpr -> SExpr360bvSub = binOp BvSub361362bvUDiv :: SExpr -> SExpr -> SExpr363bvUDiv = binOp BvUDiv364365bvULeq :: SExpr -> SExpr -> SExpr366bvULeq = binOp BvULeq367368bvULt :: SExpr -> SExpr -> SExpr369bvULt = binOp BvULt370371bvURem :: SExpr -> SExpr -> SExpr372-- Fold constant bvURem operations which are emitted a lot in our generated373-- SMT-LIB because of QBE's "shift-value modulo bitsize"-semantics.374bvURem v@(E (Int lhs)) (E (Int rhs)) =375 SExpr (width v) (Int $ lhs `rem` rhs)376bvURem lhs rhs = binOp BvURem lhs rhs377378bvXOr :: SExpr -> SExpr -> SExpr379bvXOr = binOp BvXOr