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_symbolic_array") = 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 ident128129 lookupSymbol = liftState . lookupSymbol130 activeFrame = liftState activeFrame131 pushStackFrame = liftState . pushStackFrame132 popStackFrame = liftState popStackFrame133 getSP = liftState getSP134 setSP = liftState . setSP135136 writeMemory a t v = liftState (writeMemory a t v)137 readMemory t a = liftState (readMemory t a)138139------------------------------------------------------------------------140141run :: Env -> StateT Env IO a -> IO (T.ExecTrace, ST.Store)142run env state = fst <$> runStateT go env143 where144 go = do145 _ <- state146 t <- gets envTracer147 s <- gets envStore148 pure (t, s)