1-- SPDX-FileCopyrightText: 2025-2026 Sören Tempel <soeren+git@soeren-tempel.net>2--3-- SPDX-License-Identifier: GPL-3.0-only4module Language.QBE.Simulator.Explorer5 ( defSolver,6 logSolver,7 Engine (expPathVars, expPathTrace),8 newEngine,9 explorePath,10 exploreFunc,11 )12where1314import Control.Monad.IO.Class (liftIO)15import Control.Monad.State (StateT, evalStateT, get, lift, modify, put)16import Data.Map qualified as Map17import Language.QBE.Backend.DFS (PathSel, findUnexplored, newPathSel, trackTrace)18import Language.QBE.Backend.Model (Model)19import Language.QBE.Backend.Store qualified as ST20import Language.QBE.Backend.Tracer qualified as T21import Language.QBE.Simulator (execFunc)22import Language.QBE.Simulator.Concolic.State (Env (envStore), makeConcolic, runPath)23import Language.QBE.Types qualified as QBE24import SimpleBV qualified as SMT25import System.IO (Handle)2627logic :: String28logic = "QF_BV"2930defSolver :: IO SMT.Solver31defSolver = do32 -- l <- SMT.newLogger 033 s <- SMT.newSolver "bitwuzla" [] Nothing34 SMT.setLogic s logic35 return s3637logSolver :: Handle -> IO SMT.Solver38logSolver handle = do39 l <- SMT.newLoggerWithHandle handle 040 s <-41 SMT.newSolverWithConfig42 (SMT.defaultConfig "bitwuzla" [])43 { SMT.solverLogger = SMT.smtSolverLogger l44 }45 SMT.setLogic s logic46 return s4748------------------------------------------------------------------------4950data Engine51 = Engine52 { expSolver :: SMT.Solver,53 expPathSel :: PathSel,54 expEnv :: Env,55 expPathVars :: ST.Assign,56 expPathTrace :: T.ExecTrace57 }5859newEngine :: Env -> SMT.Solver -> Engine60newEngine env solver =61 Engine62 { expSolver = solver,63 expPathSel = newPathSel,64 expEnv = env,65 expPathVars = Map.empty,66 expPathTrace = []67 }6869findNext :: [SMT.SExpr] -> T.ExecTrace -> StateT Engine IO (Maybe Model)70findNext symVars eTrace = do71 engine <- get7273 let pathSel = trackTrace (expPathSel engine) eTrace74 (model, nextPathSel) <-75 liftIO $ findUnexplored (expSolver engine) symVars pathSel7677 put $ engine {expPathSel = nextPathSel}78 pure model7980-- TODO: Consider modelling changes of the PathSel (via findNext) and81-- changes of the Store (via ST.finalize and ST.setModel) as a StateT.82explorePath :: StateT Env IO a -> StateT Engine IO Bool83explorePath simState = do84 engine@(Engine {expEnv = env}) <- get85 (eTrace, nStore) <- lift $ evalStateT (runPath simState) env8687 -- Before finalizing the store, we can extract the variables we encountered88 -- during this concrete execution, as well as the concrete values used for89 -- these variables during the execution.90 let inputVars = ST.sexprs nStore91 varAssign = ST.cValues nStore92 put $ engine {expPathVars = varAssign, expPathTrace = eTrace}9394 -- Finalize the store (declare new symbolic vars in solver) and then,95 -- based on the new solver state, solve constraints to find a new input.96 store <- liftIO $ ST.finalize (expSolver engine) nStore97 model <- findNext inputVars eTrace98 case model of99 Nothing -> pure False100 Just newModel -> do101 let nEnv = env {envStore = ST.setModel store newModel}102 in modify (\e -> e {expEnv = nEnv})103 pure True104105------------------------------------------------------------------------106107exploreFunc ::108 Engine ->109 QBE.FuncDef ->110 [(String, QBE.ExtType)] ->111 IO [(ST.Assign, T.ExecTrace)]112exploreFunc engine entry params = do113 let funcState = mapM (uncurry makeConcolic) params >>= execFunc entry114 evalStateT (exploreFunc' funcState) engine115 where116 exploreFunc' st = do117 morePaths <- explorePath st118 curEngine <- get119 let ret = (expPathVars curEngine, expPathTrace curEngine)120 in if morePaths121 then (ret :) <$> exploreFunc' st122 else pure [ret]