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 a6263makeConcolic :: String -> QBE.ExtType -> StateT Env IO (CE.Concolic DE.RegVal)64makeConcolic name ty = do65 st <- gets envStore66 let (ns, cv) = ST.getConcolic st name ty67 modify (\e -> e {envStore = ns})68 pure cv6970modifyTracer :: (MonadState Env m) => (T.ExecTrace -> T.ExecTrace) -> m ()71modifyTracer f =72 modify (\s@Env {envTracer = t} -> s {envTracer = f t})7374makeSymbolicArray ::75 QBE.GlobalIdent ->76 [CE.Concolic DE.RegVal] ->77 StateT Env IO (Maybe (CE.Concolic DE.RegVal))78makeSymbolicArray _ [arrayPtr, numElem, elemSize, namePtr] = do79 name <- E.toString <$> (toAddress namePtr >>= readNullArray)80 vlty <- case E.toWord64 elemSize of81 1 -> pure QBE.Byte82 2 -> pure QBE.HalfWord83 4 -> pure (QBE.Base QBE.Word)84 8 -> pure (QBE.Base QBE.Long)85 _ -> throwM TypingError8687 values <-88 mapM89 (\n -> makeConcolic (name ++ show n) vlty)90 [1 .. E.toWord64 numElem]9192 arrayAddr <- toAddress arrayPtr93 liftState (DS.storeValues arrayAddr values) >> pure Nothing94makeSymbolicArray ident _ = throwM $ FuncArgsMismatch ident9596findSimFunc :: QBE.GlobalIdent -> Maybe ([CE.Concolic DE.RegVal] -> (StateT Env IO) (Maybe (CE.Concolic DE.RegVal)))97findSimFunc i@(QBE.GlobalIdent "quebex_make_symbolic") = Just (makeSymbolicArray i)98findSimFunc ident = lookupSimFunc ident99100instance Simulator (StateT Env IO) (CE.Concolic DE.RegVal) where101 isTrue value = do102 let condResult = E.toWord64 (CE.concrete value) /= 0103 case CE.symbolic value of104 Nothing -> pure condResult105 Just sexpr -> do106 -- Track the taken branch in the tracer.107 let branch = T.newBranch sexpr108 modifyTracer (\t -> T.appendBranch t condResult branch)109110 pure condResult111112 -- Implements address concretization as a memory model.113 toAddress CE.Concolic {CE.concrete = cv, CE.symbolic = svMaybe} =114 case svMaybe of115 Just sv ->116 case sv `E.eq` SE.fromReg cv of117 Just c -> do118 modifyTracer (`T.appendCons` c)119 pure $ E.toWord64 cv120 Nothing -> throwM TypingError121 Nothing -> pure $ E.toWord64 cv122123 findFunc ident = do124 funcs <- gets (DS.envFuncs . envBase)125 pure $ case Map.lookup ident funcs of126 Just x -> Just $ SFuncDef x127 Nothing -> SSimFunc <$> findSimFunc ident128 findFuncByAddr addr = do129 fptrs <- gets (DS.envFuncAddrs . envBase)130 case Map.lookup addr fptrs of131 Just fn -> findFunc fn132 Nothing -> pure Nothing133134 lookupSymbol = liftState . lookupSymbol135 activeFrame = liftState activeFrame136 pushStackFrame = liftState . pushStackFrame137 popStackFrame = liftState popStackFrame138 getSP = liftState getSP139 setSP = liftState . setSP140141 writeMemory a t v = liftState (writeMemory a t v)142 readMemory t a = liftState (readMemory t a)143144------------------------------------------------------------------------145146run :: Env -> StateT Env IO a -> IO (T.ExecTrace, ST.Store)147run env state = fst <$> runStateT go env148 where149 go = do150 _ <- state151 t <- gets envTracer152 s <- gets envStore153 pure (t, s)