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.List [SMT.Atom "_", SMT.Atom ("bv" ++ show v), SMT.Atom $ show (width expr)]115 (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-- Replace 0 concats with zero extension: (concat (_ bv0 8) buf6)236concatZeros :: SExpr -> SExpr -> SExpr237concatZeros lhs@(E (Int 0)) rhs = zeroExtend (fromIntegral $ width lhs) rhs238concatZeros lhs rhs = concat' lhs rhs239240-- Replaces continuous concat expressions with a single extract expression.241concat :: SExpr -> SExpr -> SExpr242concat243 lhs@(E (Extract loff lwidth latom@(E exprLhs)))244 rhs@(E (Extract roff rwidth (E exprRhs)))245 | exprLhs == exprRhs && (roff + rwidth) == loff = extract latom roff (lwidth + rwidth)246 | otherwise = concatZeros lhs rhs247concat lhs rhs = concatZeros lhs rhs248249extract' :: SExpr -> Int -> Int -> SExpr250extract' expr off w = SExpr w $ Extract off w expr251252-- Eliminate extract expression where the value already has the desired bits.253extractSameWidth :: SExpr -> Int -> Int -> SExpr254extractSameWidth expr off w255 | off == 0 && width expr == w = expr256 | otherwise = extract' expr off w257258-- Eliminate nested extract expression of the same width.259extractNested :: SExpr -> Int -> Int -> SExpr260extractNested expr@(E (Extract ioff iwidth _)) off width =261 if ioff == off && iwidth == width262 then expr263 else extractSameWidth expr off width264extractNested expr off width = extractSameWidth expr off width265266-- Performs direct extractions of constant immediate values.267extractConst :: SExpr -> Int -> Int -> SExpr268extractConst (E (Int value)) off w =269 SExpr w . Int $ truncTo (value `shiftR` off) w270 where271 truncTo v bits = v .&. ((1 `shiftL` bits) - 1)272extractConst expr off width = extractNested expr off width273274-- This performs constant propagation for subtyping of condition values (i.e.275-- the conversion from long to word).276extractIte :: SExpr -> Int -> Int -> SExpr277extractIte (E (Ite cond ifT@(E (Int _)) ifF@(E (Int _)))) off w =278 let ex x = extractConst x off w279 in SExpr w $ Ite cond (ex ifT) (ex ifF)280extractIte expr off width = extractConst expr off width281282extractZeros ::283 SExpr ->284 Int ->285 Int ->286 SExpr287extractZeros expr@(E (ZeroExtend extBits inner)) exOff exWidth288 | exOff >= width inner && extBits > 0 = bvLit exWidth 0 -- only extracting zeros289 | otherwise = extractIte expr exOff exWidth290extractZeros outer exOff exWidth = extractIte outer exOff exWidth291292extractExt' ::293 (Integer -> SExpr -> Expr SExpr) ->294 SExpr ->295 Integer ->296 SExpr ->297 Int ->298 Int ->299 SExpr300extractExt' cons outer extBits inner exOff exWidth301 -- If we are only extracting the non-extended bytes...302 | width inner >= exOff + exWidth = extractZeros inner exOff exWidth303 -- Consider: ((_ extract 31 0) ((_ zero_extend 56) byte))304 | exWidth < fromIntegral extBits && exOff == 0 =305 SExpr exWidth $ cons (extBits - fromIntegral exWidth) inner306 -- No folding...307 | otherwise = extractZeros outer exOff exWidth308309-- Remove ZeroExtend and SignExtend expression where we don't use310-- the extended bits because we extract below the extended size.311extractExt :: SExpr -> Int -> Int -> SExpr312extractExt expr@(E (SignExtend extBits inner)) exOff exWidth =313 extractExt' SignExtend expr extBits inner exOff exWidth314extractExt expr@(E (ZeroExtend extBits inner)) exOff exWidth =315 extractExt' ZeroExtend expr extBits inner exOff exWidth316extractExt expr off w = extractIte expr off w317318extract :: SExpr -> Int -> Int -> SExpr319extract = extractExt320321------------------------------------------------------------------------322323binOp' :: (SExpr -> SExpr -> Expr SExpr) -> SExpr -> SExpr -> SExpr324binOp' op lhs rhs = lhs {sexpr = op lhs rhs}325326binOp :: (SExpr -> SExpr -> Expr SExpr) -> SExpr -> SExpr -> SExpr327-- Consider: (bvslt ((_ zero_extend 24) byte0) ((_ zero_extend 24) byte1))328-- TODO: The following only works if 'op' does not consider sign-bits. Otherwise,329-- there is no semantic expression equivalence after this folding operation.330-- binOp op lhs@(E (ZeroExtend _ lhsInner)) rhs@(E (ZeroExtend _ rhsInner)) =331-- if width lhsInner == width rhsInner332-- then binOp op lhsInner rhsInner333-- else binOp' op lhs rhs334binOp op lhs rhs = binOp' op lhs rhs335336-- TODO: Generate these using template-haskell.337338bvNeg :: SExpr -> SExpr339bvNeg x = x {sexpr = Neg x}340341bvAdd :: SExpr -> SExpr -> SExpr342bvAdd = binOp BvAdd343344bvAShr :: SExpr -> SExpr -> SExpr345bvAShr = binOp BvAShr346347bvLShr :: SExpr -> SExpr -> SExpr348bvLShr = binOp BvLShr349350bvAnd :: SExpr -> SExpr -> SExpr351bvAnd = binOp BvAnd352353bvMul :: SExpr -> SExpr -> SExpr354bvMul = binOp BvMul355356bvOr :: SExpr -> SExpr -> SExpr357bvOr = binOp BvOr358359bvSDiv :: SExpr -> SExpr -> SExpr360bvSDiv = binOp BvSDiv361362bvSLeq :: SExpr -> SExpr -> SExpr363bvSLeq = binOp BvSLeq364365bvSLt :: SExpr -> SExpr -> SExpr366bvSLt = binOp BvSLt367368bvSRem :: SExpr -> SExpr -> SExpr369bvSRem = binOp BvSRem370371bvShl :: SExpr -> SExpr -> SExpr372bvShl = binOp BvShl373374bvSub :: SExpr -> SExpr -> SExpr375bvSub = binOp BvSub376377bvUDiv :: SExpr -> SExpr -> SExpr378bvUDiv = binOp BvUDiv379380bvULeq :: SExpr -> SExpr -> SExpr381bvULeq = binOp BvULeq382383bvULt :: SExpr -> SExpr -> SExpr384bvULt = binOp BvULt385386bvURem :: SExpr -> SExpr -> SExpr387-- Fold constant bvURem operations which are emitted a lot in our generated388-- SMT-LIB because of QBE's "shift-value modulo bitsize"-semantics.389bvURem v@(E (Int lhs)) (E (Int rhs)) =390 SExpr (width v) (Int $ lhs `rem` rhs)391bvURem lhs rhs = binOp BvURem lhs rhs392393bvXOr :: SExpr -> SExpr -> SExpr394bvXOr = binOp BvXOr