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.State6 ( Env (..),7 mkEnv,8 run,9 runPath,10 makeConcolic,11 )12where1314import Control.Monad.Catch (throwM)15import Control.Monad.IO.Class (liftIO)16import Control.Monad.State (MonadState, StateT, gets, modify, runStateT)17import Data.Map qualified as Map18import Data.Word (Word8)19import Language.QBE (Program)20import Language.QBE.Backend.Store qualified as ST21import Language.QBE.Backend.Tracer qualified as T22import Language.QBE.Simulator.Concolic.Expression qualified as CE23import Language.QBE.Simulator.Default.Expression qualified as DE24import Language.QBE.Simulator.Default.Funcs (lookupSimFunc)25import Language.QBE.Simulator.Default.State qualified as DS26import Language.QBE.Simulator.Error (EvalError (FuncArgsMismatch, TypingError))27import Language.QBE.Simulator.Expression qualified as E28import Language.QBE.Simulator.Memory qualified as MEM29import Language.QBE.Simulator.State30import Language.QBE.Simulator.Symbolic.Expression qualified as SE31import Language.QBE.Types qualified as QBE32import System.Random (initStdGen, mkStdGen)3334data Env35 = Env36 { envBase :: DS.Env (CE.Concolic DE.RegVal) (CE.Concolic Word8),37 envTracer :: T.ExecTrace,38 envStore :: ST.Store39 }4041mkEnv ::42 Program ->43 MEM.Address ->44 MEM.Size ->45 Maybe Int ->46 IO Env47mkEnv prog memStart memSize maySeed = do48 initEnv <- DS.mkEnv prog memStart memSize49 randGen <-50 case maySeed of51 Just sd -> pure $ mkStdGen sd52 Nothing -> initStdGen53 pure $ Env initEnv T.newExecTrace (ST.empty randGen)5455liftState ::56 (DS.SimState (CE.Concolic DE.RegVal) (CE.Concolic Word8)) a ->57 (StateT Env IO) a58liftState toLift = do59 defEnv <- gets envBase60 (a, s) <- liftIO $ runStateT toLift defEnv61 modify (\ps -> ps {envBase = s})62 pure a6364makeConcolic :: String -> QBE.ExtType -> StateT Env IO (CE.Concolic DE.RegVal)65makeConcolic name ty = do66 st <- gets envStore67 let (ns, cv) = ST.getConcolic st name ty68 modify (\e -> e {envStore = ns})69 pure cv7071modifyTracer :: (MonadState Env m) => (T.ExecTrace -> T.ExecTrace) -> m ()72modifyTracer f =73 modify (\s@Env {envTracer = t} -> s {envTracer = f t})7475makeSymbolicArray ::76 QBE.GlobalIdent ->77 [CE.Concolic DE.RegVal] ->78 StateT Env IO (Maybe (CE.Concolic DE.RegVal))79makeSymbolicArray _ [arrayPtr, numElem, elemSize, namePtr] = do80 name <- E.toString <$> (toAddress namePtr >>= readNullArray)81 vlty <- case E.toWord64 elemSize of82 1 -> pure QBE.Byte83 2 -> pure QBE.HalfWord84 4 -> pure (QBE.Base QBE.Word)85 8 -> pure (QBE.Base QBE.Long)86 _ -> throwM TypingError8788 values <-89 mapM90 (\n -> makeConcolic (name ++ show n) vlty)91 [1 .. E.toWord64 numElem]9293 arrayAddr <- toAddress arrayPtr94 liftState (DS.storeValues arrayAddr values) >> pure Nothing95makeSymbolicArray ident _ = throwM $ FuncArgsMismatch ident9697findSimFunc :: QBE.GlobalIdent -> Maybe ([CE.Concolic DE.RegVal] -> (StateT Env IO) (Maybe (CE.Concolic DE.RegVal)))98findSimFunc i@(QBE.GlobalIdent "quebex_make_symbolic") = Just (makeSymbolicArray i)99findSimFunc ident = lookupSimFunc ident100101instance Simulator (StateT Env IO) (CE.Concolic DE.RegVal) where102 isTrue value = do103 let condResult = E.toWord64 (CE.concrete value) /= 0104 case CE.symbolic value of105 Nothing -> pure condResult106 Just sexpr -> do107 -- Track the taken branch in the tracer.108 let branch = T.newBranch sexpr109 modifyTracer (\t -> T.appendBranch t condResult branch)110111 pure condResult112113 -- Implements address concretization as a memory model.114 toAddress CE.Concolic {CE.concrete = cv, CE.symbolic = svMaybe} =115 case svMaybe of116 Just sv ->117 case sv `E.eq` SE.fromReg cv of118 Just c -> do119 modifyTracer (`T.appendCons` c)120 pure $ E.toWord64 cv121 Nothing -> throwM TypingError122 Nothing -> pure $ E.toWord64 cv123124 findFunc ident = do125 funcs <- gets (DS.envFuncs . envBase)126 pure $ case Map.lookup ident funcs of127 Just x -> Just $ SFuncDef x128 Nothing -> SSimFunc <$> findSimFunc ident129 findFuncByAddr addr = do130 fptrs <- gets (DS.envFuncAddrs . envBase)131 case Map.lookup addr fptrs of132 Just fn -> findFunc fn133 Nothing -> pure Nothing134135 lookupSymbol = liftState . lookupSymbol136 activeFrame = liftState activeFrame137 pushStackFrame = liftState . pushStackFrame138 popStackFrame = liftState popStackFrame139 getSP = liftState getSP140 setSP = liftState . setSP141142 writeMemory a t v = liftState (writeMemory a t v)143 readMemory t a = liftState (readMemory t a)144145------------------------------------------------------------------------146147runPath :: StateT Env IO a -> StateT Env IO (T.ExecTrace, ST.Store)148runPath state = do149 _ <- state150 t <- gets envTracer151 s <- gets envStore152 pure (t, s)153154run :: Env -> StateT Env IO a -> IO (T.ExecTrace, ST.Store)155run env state = fst <$> runStateT (runPath state) env