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 makeConcolic,10 )11where1213import Control.Monad.Catch (throwM)14import Control.Monad.IO.Class (liftIO)15import Control.Monad.State (MonadState, StateT, gets, modify, runStateT)16import Data.Map qualified as Map17import Data.Word (Word8)18import Language.QBE (Program)19import Language.QBE.Backend.Store qualified as ST20import Language.QBE.Backend.Tracer qualified as T21import Language.QBE.Simulator.Concolic.Expression qualified as CE22import Language.QBE.Simulator.Default.Expression qualified as DE23import Language.QBE.Simulator.Default.Funcs (lookupSimFunc)24import Language.QBE.Simulator.Default.State qualified as DS25import Language.QBE.Simulator.Error (EvalError (FuncArgsMismatch, TypingError))26import Language.QBE.Simulator.Expression qualified as E27import Language.QBE.Simulator.Memory qualified as MEM28import Language.QBE.Simulator.State29import Language.QBE.Simulator.Symbolic.Expression qualified as SE30import Language.QBE.Types qualified as QBE31import System.Random (initStdGen, mkStdGen)3233data Env34 = Env35 { envBase :: DS.Env (CE.Concolic DE.RegVal) (CE.Concolic Word8),36 envTracer :: T.ExecTrace,37 envStore :: ST.Store38 }3940mkEnv ::41 Program ->42 MEM.Address ->43 MEM.Size ->44 Maybe Int ->45 IO Env46mkEnv prog memStart memSize maySeed = do47 initEnv <- DS.mkEnv prog memStart memSize48 randGen <-49 case maySeed of50 Just sd -> pure $ mkStdGen sd51 Nothing -> initStdGen52 pure $ Env initEnv T.newExecTrace (ST.empty randGen)5354liftState ::55 (DS.SimState (CE.Concolic DE.RegVal) (CE.Concolic Word8)) a ->56 (StateT Env IO) a57liftState toLift = do58 defEnv <- gets envBase59 (a, s) <- liftIO $ runStateT toLift defEnv60 modify (\ps -> ps {envBase = s})61 pure a6263-- TODO: Use QBE.ExtType here64makeConcolic :: 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 [0 .. 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_symbolic_array") = 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 ident129130 lookupSymbol = liftState . lookupSymbol131 activeFrame = liftState activeFrame132 pushStackFrame = liftState . pushStackFrame133 popStackFrame = liftState popStackFrame134 getSP = liftState getSP135 setSP = liftState . setSP136137 writeMemory a t v = liftState (writeMemory a t v)138 readMemory t a = liftState (readMemory t a)139140------------------------------------------------------------------------141142run :: Env -> StateT Env IO a -> IO (T.ExecTrace, ST.Store)143run env state = fst <$> runStateT go env144 where145 go = do146 _ <- state147 t <- gets envTracer148 s <- gets envStore149 pure (t, s)