quebex

A software analysis framework built around the QBE intermediate language

git clone https://git.8pit.net/quebex.git

 1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>
 2--
 3-- SPDX-License-Identifier: MIT AND GPL-3.0-only
 4
 5module SMT (smtBench) where
 6
 7import Control.Monad (when)
 8import Criterion.Main
 9import Data.List (find)
10import Language.QBE (globalFuncs, parse)
11import Language.QBE.Backend.Store qualified as ST
12import Language.QBE.Backend.Tracer qualified as T
13import Language.QBE.Simulator.Concolic.State (mkEnv)
14import Language.QBE.Simulator.Explorer (explore, logSolver, newEngine)
15import Language.QBE.Simulator.Symbolic.Unwind (unwind)
16import Language.QBE.Types qualified as QBE
17import System.Exit (ExitCode (ExitSuccess))
18import System.FilePath ((</>))
19import System.IO (IOMode (WriteMode), hClose, hPutStrLn, openFile, withFile)
20import System.Process
21  ( StdStream (CreatePipe, UseHandle),
22    createProcess,
23    proc,
24    std_in,
25    std_out,
26    waitForProcess,
27  )
28
29logPath :: FilePath
30logPath = "/tmp/quebex-symex-bench.smt2"
31
32entryFunc :: QBE.GlobalIdent
33entryFunc = QBE.GlobalIdent "main"
34
35------------------------------------------------------------------------
36
37exploreQBE :: FilePath -> IO [(ST.Assign, T.ExecTrace)]
38exploreQBE filePath = do
39  content <- readFile filePath
40  prog <- case parse filePath content of
41    Right rt -> pure rt
42    Left err -> fail $ "Parsing error: " ++ show err
43
44  let funcs = globalFuncs prog
45  func <- case find (\f -> QBE.fName f == entryFunc) funcs of
46    Just x -> pure x
47    Nothing -> fail $ "Unable to find entry function: " ++ show entryFunc
48
49  withFile logPath WriteMode (explore' prog func)
50  where
51    explore' prog func handle = do
52      engine <- newEngine <$> logSolver handle
53      defEnv <- mkEnv prog 0 128 (Just 0)
54      explore engine defEnv func []
55
56getQueries :: String -> IO String
57getQueries name = do
58  _ <- exploreQBE ("bench" </> "data" </> "SMT" </> name)
59  -- XXX: Uncomment this to benchmark incremental solving instead.
60  unwind logPath
61
62solveQueries :: String -> IO ()
63solveQueries queries = do
64  devNull <- openFile "/dev/null" WriteMode
65  (Just hin, _, _, p) <-
66    createProcess
67      (proc "bitwuzla" [])
68        { std_in = CreatePipe,
69          std_out = UseHandle devNull
70        }
71
72  hPutStrLn hin queries <* hClose hin
73  ret <- waitForProcess p <* hClose devNull
74  when (ret /= ExitSuccess) $
75    error "SMT solver failed"
76
77smtBench :: Benchmark
78smtBench = do
79  bgroup
80    "SMT Complexity"
81    [ benchWithEnv "prime-numbers.qbe",
82      benchWithEnv "bubble-sort.qbe",
83      benchWithEnv "insertion-sort-uchar.qbe"
84    ]
85  where
86    benchSolver :: String -> String -> Benchmark
87    benchSolver name queries = bench name $ nfIO (solveQueries queries)
88
89    benchWithEnv :: String -> Benchmark
90    benchWithEnv name = env (getQueries name) (benchSolver name)