A software analysis framework built around the QBE intermediate language
git clone https://git.8pit.net/quebex.git
1-- SPDX-FileCopyrightText: 2024 University of Bremen 2-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net> 3-- 4-- SPDX-License-Identifier: MIT AND GPL-3.0-only 5 6module Language.QBE.Backend.ExecTree 7 ( BTree (..), 8 ExecTree, 9 mkTree,10 addTrace,11 )12where1314import Language.QBE.Simulator.Symbolic.Tracer (Branch, ExecTrace, fromBranch)1516-- A binary tree.17data BTree a = Node a (Maybe (BTree a)) (Maybe (BTree a)) | Leaf18 deriving (Show, Eq)1920-- Execution tree for the exeucted software, represented as follows:21--22-- a23-- True / \ False24-- b …25-- / \26-- N L27--28-- where the edges indicate what happens if branch a is true/false.29-- The left edge covers the true path while the right edge covers the30-- false path.31--32-- The Nothing (N) value indicates that a path has not been explored.33-- In the example above the path `[(True, a), (True, b)]` has not been34-- explored. A Leaf (L) node is used to indicate that a path has been35-- explored but we haven't discored additional branches yet. In the36-- example above the deepest path is hence `[(True a), (False, b)]`.37type ExecTree = BTree Branch3839-- Returns 'True' if we can continue exploring on this branch node.40-- This is the case if the node is either a 'Leaf' or 'Nothing'.41canCont :: Maybe (BTree a) -> Bool42canCont Nothing = True43canCont (Just Leaf) = True44canCont _ = False4546-- Create a new execution tree from a trace.47mkTree :: ExecTrace -> ExecTree48mkTree [] = Leaf49mkTree [(wasTrue, br)]50 | wasTrue = Node br (Just Leaf) Nothing51 | otherwise = Node br Nothing (Just Leaf)52mkTree ((True, br) : xs) = Node br (Just $ mkTree xs) Nothing53mkTree ((False, br) : xs) = Node br Nothing (Just $ mkTree xs)5455-- Add a trace to an existing execution tree. The control flow56-- in the trace must match the existing tree. If it diverges,57-- an error is raised.58--59-- This function prefers the branch nodes from the trace in the60-- resulting 'ExecTree', thus allowing updating their metadata via61-- this function.62--63-- Assertion: The branch encode in the Node and the branch encoded in64-- the trace should also be equal, regarding the encoded condition.65addTrace :: ExecTree -> ExecTrace -> ExecTree66addTrace tree [] = tree67-- The trace takes the True branch and we have taken that previously.68-- ↳ Recursively decent on that branch and look at remaining trace.69addTrace (Node br' (Just tb) fb) ((True, br) : xs) =70 Node (fromBranch br' br) (Just $ addTrace tb xs) fb71-- The trace takes the False branch and we have taken that previously.72-- ↳ Recursively decent on that branch and look at remaining trace.73addTrace (Node br' tb (Just fb)) ((False, br) : xs) =74 Node (fromBranch br' br) tb (Just $ addTrace fb xs)75-- If the trace takes the True/False branch and we have not taken that76-- yet (i.e. canCont is True) we insert the trace at that position.77addTrace (Node br' tb fb) ((wasTrue, br) : xs)78 | canCont tb && wasTrue = Node (fromBranch br' br) (Just $ mkTree xs) fb79 | canCont fb && not wasTrue = Node (fromBranch br' br) tb (Just $ mkTree xs)80 | otherwise = error "unreachable"81-- If we encounter a leaf, this part hasn't been explored yet.82-- That is, we can just insert the trace "as is" at this point.83addTrace Leaf trace = mkTree trace