1-- SPDX-FileCopyrightText: 2024 University of Bremen2-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>3--4-- SPDX-License-Identifier: MIT AND GPL-3.0-only56module Language.QBE.Backend.DFS7 ( PathSel,8 newPathSel,9 trackTrace,10 findUnexplored,11 )12where1314import Control.Applicative ((<|>))15import Language.QBE.Backend.ExecTree (BTree (..), ExecTree, addTrace, mkTree)16import Language.QBE.Backend.Model qualified as Model17import Language.QBE.Backend.Tracer (Branch (..), ExecTrace, solveTrace)18import SimpleBV qualified as SMT1920-- The 'PathSel' encapsulates data for the Dynamic Symbolic Execution (DSE)21-- algorithm. Specifically for path selection and incremental solving.22data PathSel23 = PathSel24 ExecTree -- The current execution tree for the DSE algorithm25 ExecTrace -- The last solved trace, for incremental solving.2627-- Create a new empty 'PathSel' object without anything traced yet.28newPathSel :: PathSel29newPathSel = PathSel (mkTree []) []3031-- Track a new 'ExecTrace' in the 'PathSel'.32trackTrace :: PathSel -> ExecTrace -> PathSel33trackTrace (PathSel tree t) trace =34 PathSel (addTrace tree trace) t3536-- Find an assignment that causes exploration of a new execution path through37-- the tested software. This function updates the metadata in the execution38-- tree and thus returns a new execution tree, even if no satisfiable39-- assignment was found.40findUnexplored :: SMT.Solver -> [SMT.SExpr] -> PathSel -> IO (Maybe Model.Model, PathSel)41findUnexplored solver inputVars tracer@(PathSel tree oldTrace) = do42 case negateBranch tree of43 Nothing -> pure (Nothing, tracer)44 Just nt -> do45 let nextTracer = PathSel (addTrace tree nt) nt46 res <- solveTrace solver inputVars oldTrace nt47 case res of48 Nothing -> findUnexplored solver inputVars nextTracer49 Just m -> pure (Just m, nextTracer)50 where51 -- Negate an unnegated branch in the execution tree and return an52 -- 'ExecTrace' which leads to an unexplored execution path. If no53 -- such path exists, then 'Nothing' is returned. If such a path54 -- exists a concrete variable assignment for it can be calculated55 -- using 'solveTrace'.56 --57 -- The branch node metadata in the resulting 'ExecTree' is updated58 -- to reflect that negation of the selected branch node was attempted.59 -- If further branches are to be negated, the resulting trace should60 -- be added to the 'ExecTree' using 'addTrace' to update the metadata61 -- in the tree as well.62 negateBranch :: ExecTree -> Maybe ExecTrace63 negateBranch Leaf = Nothing64 negateBranch (Node (Branch wasNeg ast) Nothing _)65 | wasNeg = Nothing66 | otherwise = Just [(True, Branch True ast)]67 negateBranch (Node (Branch wasNeg ast) _ Nothing)68 | wasNeg = Nothing69 | otherwise = Just [(False, Branch True ast)]70 negateBranch (Node br (Just ifTrue) (Just ifFalse)) =71 do72 (++) [(True, br)] <$> negateBranch ifTrue73 <|> (++) [(False, br)] <$> negateBranch ifFalse