diff --git a/src/kernel_services/analysis/dominators.ml b/src/kernel_services/analysis/dominators.ml index 8dc4629755e990d31dd5a5fc8e46af27c01a2568..bb04d918fa5a2e657e2d026e4d9ff9673d87cc07 100644 --- a/src/kernel_services/analysis/dominators.ml +++ b/src/kernel_services/analysis/dominators.ml @@ -20,261 +20,245 @@ (* *) (**************************************************************************) -(** Computation of dominators. - Based on "A Simple, Fast Dominance Algorithm" by K. D. Cooper et al. *) - -(* A domination tree, represented as a map from a statement to its - immediate dominator. The first statement in a function, and - statically unreachable statements (that do not have idoms), are - mapped to None. *) -module Dom_tree = State_builder.Hashtbl - (Cil_datatype.Stmt.Hashtbl) - (Datatype.Option(Cil_datatype.Stmt)) - (struct - let name = "Dominators.dom_tree" - let dependencies = [ Ast.self ] - let size = 197 - end) -;; - -(** Compute dominator information for the statements in a function *) +(* This module performs dataflow analysis using [Interpreted_automata] to + compute the domination/postdomination dependencies between statements of a + given function. *) + open Cil_types -(****************************************************************) +let dkey = Kernel.dkey_dominators -module type DIRECTION = sig - (* Number of statements in the function. *) - val nb_stmts: int +(* Datatype used to create a dot graph using analysis results. *) +module StmtTbl = Cil_datatype.Stmt.Hashtbl - (* Conversion between statements and ordered statements. *) - val to_ordered: stmt -> Ordered_stmt.ordered_stmt - val to_stmt: Ordered_stmt.ordered_stmt -> stmt +(* State type for our domain. *) +module StmtSet = Cil_datatype.Stmt.Hptset - (* Iterates on all the statements, except the roots of the - domination tree; in topological order for dominators, and reverse - topological order for the post-dominators. *) - val iter: (Ordered_stmt.ordered_stmt -> unit) -> unit +module DotGraph = Graph.Graphviz.Dot ( + struct + type t = string * (StmtSet.t StmtTbl.t) + module V = struct type t = stmt end + module E = struct + type t = (V.t * V.t) + let src = fst + let dst = snd + end - (* Entry point (for dominators) or return (for post-dominators), - that will be the root of the dominator/post-dominator tree. *) - val root_stmt: Ordered_stmt.ordered_stmt;; + let iter_vertex f (_, graph) = + StmtTbl.iter (fun stmt _ -> f stmt) graph - val is_further_from_root: - Ordered_stmt.ordered_stmt -> Ordered_stmt.ordered_stmt -> bool + let iter_edges_e f (_, graph) = + StmtTbl.iter (fun stmt -> StmtSet.iter (fun p -> f (p, stmt))) graph - (* List of all predecessors for the dominators, list of successors - for the post-dominators (for the post-dominators, it can be seen - as the predecessors in the reversed control flow graph that goes - from the sinks to the entry point). *) - val preds: Ordered_stmt.ordered_stmt -> Ordered_stmt.ordered_stmt list + let graph_attributes (title, _) = [`Label title] - val name:string -end + let default_vertex_attributes _g = [`Shape `Box; `Style `Filled] + + let vertex_name stmt = string_of_int stmt.sid + + let vertex_attributes stmt = + let txt = Format.asprintf "%a" Cil_printer.pp_stmt stmt in + [`Label txt] + + let default_edge_attributes _g = [] + + let edge_attributes _s = [] + + let get_subgraph _v = None + end) + +(* Both analyses use this domain, which propagates all encountered statements + by adding them to the state. The [join] performs an intersection which is + enough to compute domination/postdomination. *) +module Domain = struct + type t = StmtSet.t + + let join = StmtSet.inter -module Compute(D:DIRECTION) = struct - - (* Computes the smallest common dominator between two statements. *) - let nearest_common_ancestor find_domtree ord1 ord2 = - Kernel.debug ~dkey:Kernel.dkey_dominators "computing common ancestor %d %d" - (D.to_stmt ord1).sid (D.to_stmt ord2).sid; - let finger1 = ref ord1 in - let finger2 = ref ord2 in - while (!finger1 != !finger2) do ( - while ( D.is_further_from_root !finger1 !finger2) do - finger1 := (match find_domtree !finger1 with - | None -> assert false - | Some x -> x) - done; - while ( D.is_further_from_root !finger2 !finger1) do - finger2 := (match find_domtree !finger2 with - | None -> assert false - | Some x -> x) - done;) - done; - !finger1 - ;; - - (* Note: None means either unprocessed, or that the statement has no - predecessor or that all its ancestors are at None *) - (* based on "A Simple, Fast Dominance Algorithm" by K.D. Cooper et al *) - let domtree () = - let domtree = Array.make D.nb_stmts None in - - (* Initialize the dataflow: for each root, add itself to its own - set of dominators. *) - domtree.(D.root_stmt) <- Some D.root_stmt; - let changed = ref true in - while !changed do - changed := false; - D.iter (fun b -> - let ordered_preds = D.preds b in - let processed_preds = - let was_processed p = match domtree.(p) with - | None -> false - | Some(_) -> true - in - List.filter was_processed ordered_preds - in - match processed_preds with - | [] -> () (* No predecessor (e.g. unreachable stmt): leave it to None.*) - | first::rest -> - let find i = domtree.(i) in - let new_idom = - List.fold_left (nearest_common_ancestor find) first rest - in - (match domtree.(b) with - | Some(old_idom) when old_idom == new_idom -> () - | _ -> (domtree.(b) <- Some(new_idom); changed := true)) - ); - done; - (* The roots are not _immediate_ dominators of themselves, so revert - that now that the dataflow has finished. *) - domtree.(D.root_stmt) <- None; - domtree - ;; - - let display domtree = - Kernel.debug ~dkey:Kernel.dkey_dominators "Root is %d" (D.to_stmt 0).sid; - Array.iteri (fun orig dest -> match dest with - | Some(x) -> Kernel.debug ~dkey:Kernel.dkey_dominators "%s of %d is %d" - D.name (D.to_stmt orig).sid (D.to_stmt x).sid - | None -> Kernel.debug ~dkey:Kernel.dkey_dominators "no %s for %d" - D.name (D.to_stmt orig).sid) - domtree - ;; + let widen a b = + if StmtSet.subset a b then + Interpreted_automata.Fixpoint + else + Interpreted_automata.Widening (join a b) + (* Trivial transfer function: add all visited statements to the current + state. *) + let transfer v _ state = + match v.Interpreted_automata.vertex_start_of with + | None -> Some state + | Some stmt -> Some (StmtSet.add stmt state) end -let direction_dom kf = - let (stmt_to_ordered,ordered_to_stmt,_) = - Ordered_stmt.get_conversion_tables kf - in - let to_stmt = Ordered_stmt.to_stmt ordered_to_stmt in - let module Dominator = struct - let to_ordered = Ordered_stmt.to_ordered stmt_to_ordered;; - let to_stmt = to_stmt;; - let nb_stmts = Array.length ordered_to_stmt;; - let root_stmt = to_ordered (Kernel_function.find_first_stmt kf) - (* Iterate on all statements, except the entry point. *) - let iter f = - for i = 0 to nb_stmts -1 do - if i != root_stmt - then f i - done;; - let is_further_from_root p1 p2 = p1 > p2 - let preds s = List.map to_ordered (to_stmt s).Cil_types.preds - let name = "dom" - end - in - (module Dominator: DIRECTION) -;; - -(* Fill the project table with the dominators of a given function. *) -let store_dom domtree to_stmt = - Array.iteri( fun ord idom -> - let idom = Option.map to_stmt idom in - let stmt = to_stmt ord in - Kernel.debug ~dkey:Kernel.dkey_dominators "storing dom for %d: %s" - stmt.sid (match idom with None -> "self" | Some s ->string_of_int s.sid); - Dom_tree.add stmt idom - ) domtree - -let compute_dom kf = - let direction = direction_dom kf in - let module Dominator = (val direction: DIRECTION) in - let module ComputeDom = Compute(Dominator) in - let domtree = ComputeDom.domtree () in - store_dom domtree Dominator.to_stmt -;; - -(* Note: The chosen semantics for postdominator is the following one: a - post-dominates b if all the paths from b to the return statement - goes through a. - - Statements on the paths that go only into infinite loop, or to - __no_return function, do not have any post dominator (they are set - to None). - - This definition of post-dominator gives a single root to the - post-domination tree, which is required by the Cooper algorithm - above. Beware that there are alternative, incompatible, definitions - to post-domination, e.g. saying that a post dominates b if all the - paths from b to any return statement or infinite loop go through - a. *) -(* TODO: - - For each statement, associate its immediate post-dominator (if it - exists), and the list of sinks that dominates it - - - Attempt to find the post-dominator by intersection only if the - list of sinks of the points is the same. Otherwise, state that - there is no immediate post-dominator, and that the point is - dominated by the union of the lists of sinks of its successors. -*) -let _compute_pdom kf = - let (stmt_to_ordered,ordered_to_stmt,_) = - Ordered_stmt.get_conversion_tables kf - in - let module PostDominator = struct - let to_ordered = Ordered_stmt.to_ordered stmt_to_ordered;; - let to_stmt = Ordered_stmt.to_stmt ordered_to_stmt;; - let nb_stmts = Array.length ordered_to_stmt;; - let root_stmt = to_ordered (Kernel_function.find_return kf) - let iter f = - for i = nb_stmts -1 downto 0 do - if i != root_stmt - then f i - done;; - let is_further_from_root p1 p2 = p1 < p2 - let preds s = List.map to_ordered (to_stmt s).Cil_types.succs - let name = "postdom" - end - in - let module ComputePDom = Compute(PostDominator) in - let domtree = ComputePDom.domtree () in - ComputePDom.display domtree -;; - -(****************************************************************) -(* For each statement we maintain a set of statements that dominate it *) - -(* Try to find the idom, and fill the table if not already computed. *) -let get_idom s = - try Dom_tree.find s - with Not_found -> - let kf = Kernel_function.find_englobing_kf s in - let _ = (compute_dom kf) in - try Dom_tree.find s - with _ -> assert false -;; - -(** Check whether one block dominates another. This assumes that the "idom" - * field has been computed. *) -let rec dominates (s1: stmt) (s2: stmt) = - s1.sid = s2.sid || - match (get_idom s2) with - | None -> false - | Some s2idom -> dominates s1 s2idom - -let nearest_common_ancestor l = - match l with - | [] -> failwith "" - | s :: _ -> - let kf = Kernel_function.find_englobing_kf s in - let direction = direction_dom kf in - let module Dominator = (val direction: DIRECTION) in - let module ComputeDom = Compute(Dominator) in - (try ignore (Dom_tree.find s) - with Not_found -> - let domtree = ComputeDom.domtree () in - store_dom domtree Dominator.to_stmt - ); - let to_ordered = Dominator.to_ordered in - let to_stmt = Dominator.to_stmt in - let find i = Option.map to_ordered (Dom_tree.find (to_stmt i)) in - let rec aux = function - | [] -> assert false - | [s] -> to_ordered s - | s :: (_ :: _ as q) -> - ComputeDom.nearest_common_ancestor find (to_ordered s) (aux q) +(* An analysis needs a name and a starting point. *) +module type Analysis = sig + val name : string + (* May raise Kernel_function.No_Statement. *) + val get_starting_stmt : kernel_function -> stmt + include Interpreted_automata.DataflowAnalysis with type state = Domain.t +end + +(* Main module, perform the analysis, store its results and provide ways to + access them. *) +module Compute (Analysis : Analysis) = struct + + module Table = + Cil_state_builder.Stmt_hashtbl + (StmtSet) + (struct + let name = Analysis.name ^ "_table" + let dependencies = [Ast.self] + let size = 503 + end) + + let compute kf = + match Table.find_opt (Analysis.get_starting_stmt kf) with + | exception Kernel_function.No_Statement -> + Kernel.warning "No statement in function %a: %s analysis cannot be done" + Kernel_function.pretty kf Analysis.name + | Some _ -> + Kernel.feedback ~dkey "%s analysis already computed for function %a" + Analysis.name Kernel_function.pretty kf + | None -> + match kf.fundec with + | Definition (f, _) -> + Kernel.feedback ~dkey "computing %s analysis for function %a" + Analysis.name Kernel_function.pretty kf; + (* Compute the analysis, initial state is empty. *) + let result = Analysis.fixpoint kf StmtSet.empty in + (* Fill table with all statements. *) + List.iter (fun stmt -> Table.add stmt StmtSet.empty) f.sallstmts; + (* A reachable statement always (post)dominates itself, so add it here. *) + let add_in_table stmt set = Table.replace stmt (StmtSet.add stmt set) in + (* Update the table with analysis results. *) + Analysis.Result.iter_stmt add_in_table result; + Kernel.feedback ~dkey "%s analysis done for function %a" + Analysis.name Kernel_function.pretty kf + (* [Analysis.get_starting_stmt] should fatal before this point. *) + | Declaration _ -> assert false + + let find_kf stmt = + try Kernel_function.find_englobing_kf stmt + with Not_found -> + Kernel.fatal "Statement %d is not part of a function" stmt.sid + + (* Generic function to get the set of (post)dominators of [stmt]. *) + let get stmt = + match Table.find_opt stmt with + | None -> compute (find_kf stmt); Table.find stmt + | Some v -> v + + (* Generic function to get the set of strict (post)dominators of [stmt]. *) + let get_strict stmt = get stmt |> StmtSet.remove stmt + + (* Generic function to test the (post)domination of 2 statements. *) + let mem a b = get b |> StmtSet.mem a + + (* Generic function to test the strict (post)domination of 2 statements. *) + let mem_strict a b = get_strict b |> StmtSet.mem a + + (* The nearest common ancestor (resp. child) is the ancestor which is + dominated (resp. postdominated) by all common ancestors, ie. the lowest + (resp. highest) ancestor in the domination tree. *) + let nearest stmtl = + (* Get the set of strict (post)doms for each statement and intersect them to + keep the common ones. If one of them is unreachable, they do not + share a common ancestor/child. *) + let common_set = + match stmtl with + | [] -> StmtSet.empty + | stmt :: tail -> + List.fold_left + (fun acc s -> StmtSet.inter acc (get_strict s)) + (get_strict stmt) tail in - Dominator.to_stmt (aux l) + (* Try to find a statement [s] in [common_set] which is (post)dominated by + all statements of the [common_set]. *) + let is_dom_by_all a = StmtSet.for_all (fun b -> mem b a) common_set in + List.find_opt is_dom_by_all (StmtSet.elements common_set) + + let pretty fmt () = + let list = Table.to_seq () |> List.of_seq in + let pp_set fmt set = + Pretty_utils.pp_iter ~pre:"@[{" ~sep:",@," ~suf:"}@]" + StmtSet.iter (fun fmt stmt -> Format.pp_print_int fmt stmt.sid) + fmt set + in + Pretty_utils.pp_list ~pre:"@[<v>" ~sep:"@;" ~suf:"@]" ~empty:"Empty" + (fun fmt (s, set) -> Format.fprintf fmt "Stmt:%d -> %a" s.sid pp_set set) + fmt list + + (* [s_set] are [s] (post)dominators, including [s]. We don't have to represent + the relation between [s] and [s], so [get_set] removes it. And because the + (post)domination relation is transitive, if [p] is in [s_set], we can + remove [p_set] from [s_set] in order to have a clearer graph. *) + let reduce graph s = + (* Union of all (post)dominators of [s] (post)dominators [s_set]. *) + let unions p acc = StmtTbl.find graph p |> StmtSet.union acc in + let s_set = StmtTbl.find graph s in + let p_sets = StmtSet.fold unions s_set StmtSet.empty in + let res = StmtSet.diff s_set p_sets in + StmtTbl.replace graph s res + + let print_dot basename kf = + match kf.fundec with + | Definition (fct, _) -> + let graph = StmtTbl.create (List.length fct.sallstmts) in + let copy_in_graph stmt = get_strict stmt |> StmtTbl.replace graph stmt in + List.iter copy_in_graph fct.sallstmts; + List.iter (reduce graph) fct.sallstmts; + let name = Kernel_function.get_name kf in + let title = Format.sprintf "%s for function %s" Analysis.name name in + let filename = basename ^ "." ^ Kernel_function.get_name kf ^ ".dot" in + let file = open_out filename in + DotGraph.output_graph file (title, graph); + close_out file; + Kernel.result "%s: dot file generated in %s for function %a" + Analysis.name filename Kernel_function.pretty kf + | Declaration _ -> + Kernel.warning "cannot compute %s for function %a without body" + Analysis.name Kernel_function.pretty kf +end + +(* ---------------------------------------------------------------------- *) +(* --- Dominators --- *) +(* ---------------------------------------------------------------------- *) + +module ForwardAnalysis = struct + include Interpreted_automata.ForwardAnalysis (Domain) + let name = "Dominators" + let get_starting_stmt kf = Kernel_function.find_first_stmt kf +end + +module Dominators = Compute (ForwardAnalysis) + +let compute_dominators = Dominators.compute +let get_dominators = Dominators.get +let get_strict_dominators = Dominators.get_strict +let dominates = Dominators.mem +let strictly_dominates = Dominators.mem_strict +let get_idom s = Dominators.nearest [s] +let nearest_common_ancestor = Dominators.nearest +let pretty_dominators = Dominators.pretty +let print_dot_dominators = Dominators.print_dot + +(* ---------------------------------------------------------------------- *) +(* --- Postdominators --- *) +(* ---------------------------------------------------------------------- *) + +module BackwardAnalysis = struct + include Interpreted_automata.BackwardAnalysis (Domain) + let name = "PostDominators" + let get_starting_stmt kf = Kernel_function.find_return kf +end + +module PostDominators = Compute (BackwardAnalysis) + +let compute_postdominators = PostDominators.compute +let get_postdominators = PostDominators.get +let get_strict_postdominators = PostDominators.get_strict +let postdominates = PostDominators.mem +let strictly_postdominates = PostDominators.mem_strict +let get_ipostdom s = PostDominators.nearest [s] +let nearest_common_child = PostDominators.nearest +let pretty_postdominators = PostDominators.pretty +let print_dot_postdominators = PostDominators.print_dot diff --git a/src/kernel_services/analysis/dominators.mli b/src/kernel_services/analysis/dominators.mli index 55a4e6129e3c7fdf69f9d6ca006f8161b291d0d7..449394b370df919babc60c39be1013611b72146a 100644 --- a/src/kernel_services/analysis/dominators.mli +++ b/src/kernel_services/analysis/dominators.mli @@ -20,22 +20,90 @@ (* *) (**************************************************************************) -(** Computation of dominators. *) -open Cil_types;; +(** Module to perform dominators and postdominators analyses. This module was + completely redesigned and provides many new functions. -val get_idom: stmt -> stmt option -(** Immediate dominator of the statement. *) + A dominator [d] of [s] is a statement such that all paths from the entry + point of a function to [s] must go through [d]. -val dominates: stmt -> stmt -> bool -(** [dominates a b] tells whether [a] dominates [b]. *) + A postdominator [p] of [s] is a statement such that all paths from [s] to + the return point of the function must go through [p]. -val nearest_common_ancestor: stmt list -> stmt -(** Finds the statement lowest in the function that dominates - all the statements in the list passed as argument. The list must not - be empty, and must contain statements that are all in the same function. *) + By definition, a statement always (post)dominates itself (except if it is + unreachable). -(* -Local Variables: -compile-command: "make -C ../../.." -End: + An immediate (post)dominator (or i(post)dom) [d] is the unique + (post)dominator of [s] that strictly (post)dominates [s] but is + (post)dominated by all other (post)dominators of [s]. + + A common ancestor (or child) of a list of statements is a (post)dominator + that (post)dominates all the statements + + @since Frama-C+dev +*) + +open Cil_types + +val compute_dominators : kernel_function -> unit +(** Compute the Dominators analysis and register its result. *) + +val compute_postdominators : kernel_function -> unit +(** Compute the Postdominators analysis and register its result. *) + +val get_dominators : stmt -> Cil_datatype.Stmt.Hptset.t +(** Return the [set] of dominators of the given statement. + The empty set means the statement is unreachable. *) + +val get_postdominators : stmt -> Cil_datatype.Stmt.Hptset.t +(** Return the [set] of postdominators of the given statement. + The empty set means the statement is unreachable. *) + +val get_strict_dominators : stmt -> Cil_datatype.Stmt.Hptset.t +(** Same as [get_dominators] but exclude the statement itself. The empty set + means the statement is unreachable or is only dominated by itself. *) + +val get_strict_postdominators : stmt -> Cil_datatype.Stmt.Hptset.t +(** Same as [get_postdominators] but exclude the statement itself. The empty set + means the statement is unreachable or is only post-dominated by itself. *) + +val dominates : stmt -> stmt -> bool +(** [dominates a b] return [true] if [a] dominates [b]. *) + +val postdominates : stmt -> stmt -> bool +(** [postdominates a b] return [true] if [a] postdominates [b]. *) + +val strictly_dominates : stmt -> stmt -> bool +(** [strictly_dominates a b] return [true] if [a] strictly dominates [b]. *) + +val strictly_postdominates : stmt -> stmt -> bool +(** [strictly_postdominates a b] return [true] if [a] strictly postdominates + [b]. *) + +val get_idom : stmt -> stmt option +(** Return the immediate dominator of the given statement. *) + +val get_ipostdom : stmt -> stmt option +(** Return the immediate postdominator of the given statement. *) + +val nearest_common_ancestor : stmt list -> stmt option +(** Return the closest common ancestor of the given statement list. + @before Frama-C+dev previous implementation always returned a statement and + raised a failed assertion in case of unreachable statement. *) + +val nearest_common_child : stmt list -> stmt option +(** Return the closest common child of the given statement list. *) + +val pretty_dominators : Format.formatter -> unit -> unit +(** Print the result of the domination analysis. Each statement is either + dominated by a set of statements, or [Top] if unreachable. *) + +val pretty_postdominators : Format.formatter -> unit -> unit +(** Print the result of the postdomination analysis. Each statement is either + postdominated by a set of statements, or [Top] if unreachable. *) + +val print_dot_dominators : string -> kernel_function -> unit +(** Print the domination graph in a file [basename.function_name.dot]. *) + +val print_dot_postdominators : string -> kernel_function -> unit +(** Print the postdomination graph in a file [basename.function_name.dot]. *) diff --git a/src/plugins/eva/domains/taint_domain.ml b/src/plugins/eva/domains/taint_domain.ml index a6a9f9c33f1d6ac199a1f8acdf89798ff3a71d50..b08afe582914426698762bcaa24db2ea7efaf6c9 100644 --- a/src/plugins/eva/domains/taint_domain.ml +++ b/src/plugins/eva/domains/taint_domain.ml @@ -168,12 +168,9 @@ module TransferTaint = struct from the tainted assume that not go through [stmt], ie [stmt] is not a postdominator for the tainted assume. *) let filter_active_tainted_assumes stmt state = - let kf = Kernel_function.find_englobing_kf stmt in let assume_stmts = Stmt.Set.filter - (fun assume_stmt -> - not (Postdominators.is_postdominator kf - ~opening:assume_stmt ~closing:stmt)) + (fun assume_stmt -> not (Dominators.postdominates stmt assume_stmt)) state.assume_stmts in { state with assume_stmts } diff --git a/src/plugins/eva/dune b/src/plugins/eva/dune index 56e7f073e57e6a8ff904701fcdfbac34b40d3b4f..784ba60c0b4191b86ad2329d7e74c9e5f8708df8 100644 --- a/src/plugins/eva/dune +++ b/src/plugins/eva/dune @@ -28,7 +28,6 @@ (echo "EVA:" %{lib-available:frama-c-eva.core} "\n") (echo " - ppx_deriving.eq:" %{lib-available:ppx_deriving.eq} "\n") (echo " - ppx_deriving.ord:" %{lib-available:ppx_deriving.ord} "\n") - (echo " - Postdominators:" %{lib-available:frama-c-postdominators.core} "\n") (echo "Numerors:" %{lib-available:frama-c-eva.numerors.core} "\n") (echo " - MLMPFR:" %{lib-available:mlmpfr} "\n") (echo "Apron domains:" %{lib-available:frama-c-eva.apron.core} "\n") @@ -42,7 +41,7 @@ (optional) (public_name frama-c-eva.core) (flags -open Frama_c_kernel :standard -w -9) - (libraries frama-c.kernel frama-c-server.core frama-c-postdominators.core) + (libraries frama-c.kernel frama-c-server.core) (instrumentation (backend landmarks)) (instrumentation (backend bisect_ppx)) (preprocess (pps ppx_deriving.eq ppx_deriving.ord))) diff --git a/src/plugins/from/dune b/src/plugins/from/dune index 1e769f239c2119877b6ab480a5229572e8426863..a47788602948d7a3f7a68e9cee93385fa4d4997b 100644 --- a/src/plugins/from/dune +++ b/src/plugins/from/dune @@ -27,7 +27,6 @@ (echo "From:" %{lib-available:frama-c-from.core} "\n") (echo " - Callgraph:" %{lib-available:frama-c-callgraph.core} "\n") (echo " - Eva:" %{lib-available:frama-c-eva.core} "\n") - (echo " - Postdominators:" %{lib-available:frama-c-postdominators.core} "\n") ) ) ) @@ -37,7 +36,7 @@ (optional) (public_name frama-c-from.core) (flags -open Frama_c_kernel :standard -w -9) - (libraries frama-c.kernel frama-c-callgraph.core frama-c-eva.core frama-c-postdominators.core) + (libraries frama-c.kernel frama-c-callgraph.core frama-c-eva.core) (instrumentation (backend landmarks)) (instrumentation (backend bisect_ppx)) ) diff --git a/src/plugins/from/from_compute.ml b/src/plugins/from/from_compute.ml index 14c0cc2acda9f71d51759577d6ebd76f5c95cfd0..9f4f3466bde40fdb604e181c97b2f3857248d5e0 100644 --- a/src/plugins/from/from_compute.ml +++ b/src/plugins/from/from_compute.ml @@ -477,12 +477,11 @@ struct (* Eliminate additional variables originating from a control-flow branching statement closing at [s]. *) let eliminate_additional s data = - let kf = Stack.top call_stack in let map = data.additional_deps_table in let map' = ZoneStmtMap.fold (fun k _v acc_map -> - if Postdominators.is_postdominator kf ~opening:k ~closing:s + if Dominators.postdominates s k then ZoneStmtMap.remove k acc_map else acc_map ) map map diff --git a/src/plugins/gui/help_manager.ml b/src/plugins/gui/help_manager.ml index d5e9ba63a562c8a370e8128cfb8ccb994b5ad2d9..590c2bdf8a965e5abafb0b43ef65bb2602edc925 100644 --- a/src/plugins/gui/help_manager.ml +++ b/src/plugins/gui/help_manager.ml @@ -79,7 +79,7 @@ let show main_ui = let copyright (* should be automatically generated *) = "\t © CEA and INRIA for the Frama-C kernel\n\ \t © CEA for the GUI and plug-ins constant propagation, from, inout, impact, \ - metrics, occurrence pdg, postdominators, scope, security_slicing, \ + metrics, occurrence pdg, scope, security_slicing, \ semantic callgraph, slicing, sparecode, syntactic callgraph, users and value.\n\ \n\ See the particular header of each source file for details." diff --git a/src/plugins/loop_analysis/region_analysis_stmt.ml b/src/plugins/loop_analysis/region_analysis_stmt.ml index e5d4380e2d1b8ba9edc628b56f396dabdde8989e..319d8e87659017f3d61890ac710d4575ef5fb547 100644 --- a/src/plugins/loop_analysis/region_analysis_stmt.ml +++ b/src/plugins/loop_analysis/region_analysis_stmt.ml @@ -100,6 +100,7 @@ struct module DomTree = struct let dominates = Dominators.dominates + let domtree_postfix_iter f = (* Reverse the normal domtree. *) diff --git a/src/plugins/postdominators/compute.ml b/src/plugins/postdominators/compute.ml deleted file mode 100644 index 412f32507fee6f978b7e4b3feef22125a8d83ced..0000000000000000000000000000000000000000 --- a/src/plugins/postdominators/compute.ml +++ /dev/null @@ -1,228 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of Frama-C. *) -(* *) -(* Copyright (C) 2007-2024 *) -(* CEA (Commissariat à l'énergie atomique et aux énergies *) -(* alternatives) *) -(* *) -(* you can redistribute it and/or modify it under the terms of the GNU *) -(* Lesser General Public License as published by the Free Software *) -(* Foundation, version 2.1. *) -(* *) -(* It is distributed in the hope that it will be useful, *) -(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) -(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) -(* GNU Lesser General Public License for more details. *) -(* *) -(* See the GNU Lesser General Public License version 2.1 *) -(* for more details (enclosed in the file licenses/LGPLv2.1). *) -(* *) -(**************************************************************************) - -open Cil_types -open Cil_datatype - -include - Plugin.Register - (struct - let name = "dominators" - let shortname = "dominators" - let help = "Compute postdominators of statements" - end) - -module DomSet = struct - - type domset = Value of Stmt.Hptset.t | Top - - let inter a b = match a,b with - | Top,Top -> Top - | Value v, Top | Top, Value v -> Value v - | Value v, Value v' -> Value (Stmt.Hptset.inter v v') - - let add v d = match d with - | Top -> Top - | Value d -> Value (Stmt.Hptset.add v d) - - let mem v = function - | Top -> true - | Value d -> Stmt.Hptset.mem v d - - let map f = function - | Top -> Top - | Value set -> Value (f set) - - include Datatype.Make - (struct - include Datatype.Serializable_undefined - type t = domset - let name = "dominator_set" - let reprs = Top :: List.map (fun s -> Value s) Stmt.Hptset.reprs - let structural_descr = - Structural_descr.t_sum [| [| Stmt.Hptset.packed_descr |] |] - let pretty fmt = function - | Top -> Format.fprintf fmt "Top" - | Value d -> - Pretty_utils.pp_iter ~pre:"@[{" ~sep:",@," ~suf:"}@]" - Stmt.Hptset.iter - (fun fmt s -> Format.fprintf fmt "%d" s.sid) - fmt d - let equal a b = match a,b with - | Top,Top -> true - | Value _v, Top | Top, Value _v -> false - | Value v, Value v' -> Stmt.Hptset.equal v v' - let copy = map Cil_datatype.Stmt.Hptset.copy - let mem_project = Datatype.never_any_project - end) - -end - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) - -exception Top - -module type MakePostDomArg = sig - val is_accessible: stmt -> bool - (* Evaluation of an expression which is supposed to be the condition of an - 'if'. The first boolean (resp. second) represents the possibility that - the expression can be non-zero (resp. zero), ie. true (resp. false). *) - val eval_cond: stmt -> exp -> bool * bool - - val dependencies: State.t list - val name: string -end - -module MakePostDom(X: MakePostDomArg) = -struct - - module PostDom = - Cil_state_builder.Stmt_hashtbl - (DomSet) - (struct - let name = "postdominator." ^ X.name - let dependencies = Ast.self :: X.dependencies - let size = 503 - end) - - module PostComputer = struct - - let name = "postdominator" - let debug = false - - type t = DomSet.t - module StmtStartData = PostDom - - let pretty = DomSet.pretty - - let combineStmtStartData _stmt ~old new_ = - if DomSet.equal old new_ then None else Some new_ - - let combineSuccessors = DomSet.inter - - let doStmt stmt = - Async.yield (); - Postdominators_parameters.debug ~level:2 "doStmt: %d" stmt.sid; - match stmt.skind with - | Return _ -> Dataflow2.Done (DomSet.Value (Stmt.Hptset.singleton stmt)) - | _ -> Dataflow2.Post (fun data -> DomSet.add stmt data) - - - let doInstr _ _ _ = Dataflow2.Default - - (* We make special tests for 'if' statements without a 'then' or - 'else' branch. It can lead to better precision if we can evaluate - the condition of the 'if' with always the same truth value *) - let filterIf ifstmt next = match ifstmt.skind with - | If (e, { bstmts = sthen :: _ }, { bstmts = [] }, _) - when not (Stmt.equal sthen next) -> - (* [next] is the syntactic successor of the 'if', ie the - 'else' branch. If the condition is never false, then - [sthen] postdominates [next]. We must not follow the edge - from [ifstmt] to [next] *) - snd (X.eval_cond ifstmt e) - - | If (e, { bstmts = [] }, { bstmts = selse :: _ }, _) - when not (Stmt.equal selse next) -> - (* dual case *) - fst (X.eval_cond ifstmt e) - - | _ -> true - - let filterStmt pred next = - X.is_accessible pred && filterIf pred next - - - let funcExitData = DomSet.Value Stmt.Hptset.empty - - end - module PostCompute = Dataflow2.Backwards(PostComputer) - - let compute kf = - let return = - try Kernel_function.find_return kf - with Kernel_function.No_Statement -> - Postdominators_parameters.abort - "No return statement for a function with body %a" - Kernel_function.pretty kf - in - try - let _ = PostDom.find return in - Postdominators_parameters.feedback ~level:2 "computed for function %a" - Kernel_function.pretty kf - with Not_found -> - Postdominators_parameters.feedback ~level:2 "computing for function %a" - Kernel_function.pretty kf; - let f = kf.fundec in - match f with - | Definition (f,_) -> - let stmts = f.sallstmts in - List.iter (fun s -> PostDom.add s DomSet.Top) stmts; - PostCompute.compute [return]; - Postdominators_parameters.feedback ~level:2 "done for function %a" - Kernel_function.pretty kf - | Declaration _ -> () - - let get_stmt_postdominators f stmt = - let do_it () = PostDom.find stmt in - try do_it () - with Not_found -> compute f; do_it () - - (** @raise Top when the statement postdominators - * have not been computed ie neither the return statement is reachable, - * nor the statement is in a natural loop. *) - let stmt_postdominators f stmt = - match get_stmt_postdominators f stmt with - | DomSet.Value s -> - Postdominators_parameters.debug ~level:1 "Postdom for %d are %a" - stmt.sid Stmt.Hptset.pretty s; - s - | DomSet.Top -> raise Top - - let is_postdominator f ~opening ~closing = - let open_postdominators = get_stmt_postdominators f opening in - DomSet.mem closing open_postdominators - - let display () = - let disp_all fmt = - PostDom.iter - (fun k v -> Format.fprintf fmt "Stmt:%d -> @[%a@]\n" - k.sid PostComputer.pretty v) - in Postdominators_parameters.result "%t" disp_all - -end - -module PostDomArg = struct - let is_accessible _ = true - let dependencies = [] - let name = "basic" - let eval_cond _ _ = true, true -end - -include MakePostDom (PostDomArg) -let self = PostDom.self - -(* -Local Variables: -compile-command: "make -C ../../.." -End: -*) diff --git a/src/plugins/postdominators/compute.mli b/src/plugins/postdominators/compute.mli deleted file mode 100644 index 748beef0b5befe142b16cc1c5be3914cb17076a3..0000000000000000000000000000000000000000 --- a/src/plugins/postdominators/compute.mli +++ /dev/null @@ -1,41 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of Frama-C. *) -(* *) -(* Copyright (C) 2007-2024 *) -(* CEA (Commissariat à l'énergie atomique et aux énergies *) -(* alternatives) *) -(* *) -(* you can redistribute it and/or modify it under the terms of the GNU *) -(* Lesser General Public License as published by the Free Software *) -(* Foundation, version 2.1. *) -(* *) -(* It is distributed in the hope that it will be useful, *) -(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) -(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) -(* GNU Lesser General Public License for more details. *) -(* *) -(* See the GNU Lesser General Public License version 2.1 *) -(* for more details (enclosed in the file licenses/LGPLv2.1). *) -(* *) -(**************************************************************************) - -open Cil_types - -exception Top -(** Used for postdominators-related functions, when the - postdominators of a statement cannot be computed. It means that - there is no path from this statement to the function return. *) - -val compute: kernel_function -> unit - -val stmt_postdominators: - kernel_function -> stmt -> Cil_datatype.Stmt.Hptset.t -(** @raise Top (see above) *) - -val is_postdominator: - kernel_function -> opening:stmt -> closing:stmt -> bool - -val display: unit -> unit - -val self: State.t diff --git a/src/plugins/postdominators/dune b/src/plugins/postdominators/dune deleted file mode 100644 index 421fca944bc2d73026038f7d29130d72774b1638..0000000000000000000000000000000000000000 --- a/src/plugins/postdominators/dune +++ /dev/null @@ -1,42 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; This file is part of Frama-C. ;; -;; ;; -;; Copyright (C) 2007-2024 ;; -;; CEA (Commissariat à l'énergie atomique et aux énergies ;; -;; alternatives) ;; -;; ;; -;; you can redistribute it and/or modify it under the terms of the GNU ;; -;; Lesser General Public License as published by the Free Software ;; -;; Foundation, version 2.1. ;; -;; ;; -;; It is distributed in the hope that it will be useful, ;; -;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; -;; GNU Lesser General Public License for more details. ;; -;; ;; -;; See the GNU Lesser General Public License version 2.1 ;; -;; for more details (enclosed in the file licenses/LGPLv2.1). ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(rule - (alias frama-c-configure) - (deps (universe)) - (action (progn - (echo "Postdominators:" %{lib-available:frama-c-postdominators.core} "\n") - ) - ) -) - -( library - (name postdominators) - (optional) - (public_name frama-c-postdominators.core) - (flags -open Frama_c_kernel :standard -w -9) - (libraries frama-c.kernel) - (instrumentation (backend landmarks)) - (instrumentation (backend bisect_ppx)) -) - -(plugin (optional) (name postdominators) (libraries frama-c-postdominators.core) (site (frama-c plugins))) diff --git a/src/plugins/postdominators/dune-project b/src/plugins/postdominators/dune-project deleted file mode 100644 index 1e31a2576fcd8d1781fb5f0acb3d25170369928a..0000000000000000000000000000000000000000 --- a/src/plugins/postdominators/dune-project +++ /dev/null @@ -1,25 +0,0 @@ -(lang dune 3.7) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; This file is part of Frama-C. ;; -;; ;; -;; Copyright (C) 2007-2024 ;; -;; CEA (Commissariat à l'énergie atomique et aux énergies ;; -;; alternatives) ;; -;; ;; -;; you can redistribute it and/or modify it under the terms of the GNU ;; -;; Lesser General Public License as published by the Free Software ;; -;; Foundation, version 2.1. ;; -;; ;; -;; It is distributed in the hope that it will be useful, ;; -;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; -;; GNU Lesser General Public License for more details. ;; -;; ;; -;; See the GNU Lesser General Public License version 2.1 ;; -;; for more details (enclosed in the file licenses/LGPLv2.1). ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(name frama-c-postdominators) -(using dune_site 0.1) diff --git a/src/plugins/postdominators/frama-c-postdominators.opam b/src/plugins/postdominators/frama-c-postdominators.opam deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/plugins/postdominators/postdominators.ml b/src/plugins/postdominators/postdominators.ml deleted file mode 100644 index 2ebc1ad9268c9e9928d1672c77fa3a01b8a733f6..0000000000000000000000000000000000000000 --- a/src/plugins/postdominators/postdominators.ml +++ /dev/null @@ -1,42 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of Frama-C. *) -(* *) -(* Copyright (C) 2007-2024 *) -(* CEA (Commissariat à l'énergie atomique et aux énergies *) -(* alternatives) *) -(* *) -(* you can redistribute it and/or modify it under the terms of the GNU *) -(* Lesser General Public License as published by the Free Software *) -(* Foundation, version 2.1. *) -(* *) -(* It is distributed in the hope that it will be useful, *) -(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) -(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) -(* GNU Lesser General Public License for more details. *) -(* *) -(* See the GNU Lesser General Public License version 2.1 *) -(* for more details (enclosed in the file licenses/LGPLv2.1). *) -(* *) -(**************************************************************************) - -include Compute - -let print_dot basename kf = - let filename = basename ^ "." ^ Kernel_function.get_name kf ^ ".dot" in - Print.build_dot filename kf; - Postdominators_parameters.result "dot file generated in %s" filename - -let output () = - let dot_postdom = Postdominators_parameters.DotPostdomBasename.get () in - if dot_postdom <> "" then ( - Ast.compute (); - Globals.Functions.iter (fun kf -> - if Kernel_function.is_definition kf then - print_dot dot_postdom kf) - ) - -let output, _ = State_builder.apply_once "Postdominators.Compute.output" - [Compute.self] output - -let () = Boot.Main.extend output diff --git a/src/plugins/postdominators/postdominators.mli b/src/plugins/postdominators/postdominators.mli deleted file mode 100644 index a8786b4ba531903728c94deaaeb82595a9eb136a..0000000000000000000000000000000000000000 --- a/src/plugins/postdominators/postdominators.mli +++ /dev/null @@ -1,43 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of Frama-C. *) -(* *) -(* Copyright (C) 2007-2024 *) -(* CEA (Commissariat à l'énergie atomique et aux énergies *) -(* alternatives) *) -(* *) -(* you can redistribute it and/or modify it under the terms of the GNU *) -(* Lesser General Public License as published by the Free Software *) -(* Foundation, version 2.1. *) -(* *) -(* It is distributed in the hope that it will be useful, *) -(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) -(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) -(* GNU Lesser General Public License for more details. *) -(* *) -(* See the GNU Lesser General Public License version 2.1 *) -(* for more details (enclosed in the file licenses/LGPLv2.1). *) -(* *) -(**************************************************************************) - -open Cil_types - -exception Top -(** Used for postdominators-related functions, when the - postdominators of a statement cannot be computed. It means that - there is no path from this statement to the function return. *) - -val compute: kernel_function -> unit - -val stmt_postdominators: - kernel_function -> stmt -> Cil_datatype.Stmt.Hptset.t -(** @raise Top (see above) *) - -val is_postdominator: - kernel_function -> opening:stmt -> closing:stmt -> bool - -val display: unit -> unit - -val print_dot : string -> kernel_function -> unit -(** Print a representation of the postdominators in a dot file - whose name is [basename.function_name.dot]. *) diff --git a/src/plugins/postdominators/postdominators_parameters.ml b/src/plugins/postdominators/postdominators_parameters.ml deleted file mode 100644 index 86b00634e808aaa74d8a21b2d9a393674f277b16..0000000000000000000000000000000000000000 --- a/src/plugins/postdominators/postdominators_parameters.ml +++ /dev/null @@ -1,42 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of Frama-C. *) -(* *) -(* Copyright (C) 2007-2024 *) -(* CEA (Commissariat à l'énergie atomique et aux énergies *) -(* alternatives) *) -(* *) -(* you can redistribute it and/or modify it under the terms of the GNU *) -(* Lesser General Public License as published by the Free Software *) -(* Foundation, version 2.1. *) -(* *) -(* It is distributed in the hope that it will be useful, *) -(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) -(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) -(* GNU Lesser General Public License for more details. *) -(* *) -(* See the GNU Lesser General Public License version 2.1 *) -(* for more details (enclosed in the file licenses/LGPLv2.1). *) -(* *) -(**************************************************************************) - -include Plugin.Register - (struct - let name = "postdominators" - let shortname = "postdominators" - let help = "computing postdominators of statements" - end) - -module DotPostdomBasename = - Empty_string - (struct - let option_name = "-dot-postdom" - let arg_name = "f" - let help = "put the postdominators of function <f> in basename.f.dot" - end) - -(* -Local Variables: -compile-command: "make -C ../../.." -End: -*) diff --git a/src/plugins/postdominators/postdominators_parameters.mli b/src/plugins/postdominators/postdominators_parameters.mli deleted file mode 100644 index 4e54382d36affe9be855bd01bb4107ecb88dff61..0000000000000000000000000000000000000000 --- a/src/plugins/postdominators/postdominators_parameters.mli +++ /dev/null @@ -1,32 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of Frama-C. *) -(* *) -(* Copyright (C) 2007-2024 *) -(* CEA (Commissariat à l'énergie atomique et aux énergies *) -(* alternatives) *) -(* *) -(* you can redistribute it and/or modify it under the terms of the GNU *) -(* Lesser General Public License as published by the Free Software *) -(* Foundation, version 2.1. *) -(* *) -(* It is distributed in the hope that it will be useful, *) -(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) -(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) -(* GNU Lesser General Public License for more details. *) -(* *) -(* See the GNU Lesser General Public License version 2.1 *) -(* for more details (enclosed in the file licenses/LGPLv2.1). *) -(* *) -(**************************************************************************) - -include Plugin.General_services - -module DotPostdomBasename: Parameter_sig.String - - -(* -Local Variables: -compile-command: "make -C ../../.." -End: -*) diff --git a/src/plugins/postdominators/print.ml b/src/plugins/postdominators/print.ml deleted file mode 100644 index 40534a9b8b9c1b6411d257dffbab7843f0ca9cd7..0000000000000000000000000000000000000000 --- a/src/plugins/postdominators/print.ml +++ /dev/null @@ -1,148 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of Frama-C. *) -(* *) -(* Copyright (C) 2007-2024 *) -(* CEA (Commissariat à l'énergie atomique et aux énergies *) -(* alternatives) *) -(* *) -(* you can redistribute it and/or modify it under the terms of the GNU *) -(* Lesser General Public License as published by the Free Software *) -(* Foundation, version 2.1. *) -(* *) -(* It is distributed in the hope that it will be useful, *) -(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) -(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) -(* GNU Lesser General Public License for more details. *) -(* *) -(* See the GNU Lesser General Public License version 2.1 *) -(* for more details (enclosed in the file licenses/LGPLv2.1). *) -(* *) -(**************************************************************************) - -open Cil_types -open Cil_datatype - -let pretty_stmt fmt s = Cil_printer.pp_stmt fmt s - -module Printer = struct - - type t = string * (Stmt.Hptset.t option Kinstr.Hashtbl.t) - module V = struct - type t = Cil_types.stmt * bool - let pretty fmt v = pretty_stmt fmt v - end - module E = struct - type t = (V.t * V.t) - let src e = fst e - let dst e = snd e - end - - let iter_vertex f (_, graph) = - let do_s ki postdom = - let s = match ki with Kstmt s -> s | _ -> assert false in - Postdominators_parameters.debug "iter_vertex %d : %a\n" s.sid V.pretty s; - let has_postdom = match postdom with None -> false | _ -> true in - f (s, has_postdom) - in - Kinstr.Hashtbl.iter do_s graph - - let iter_edges_e f (_, graph) = - let do_s ki postdom = - let s = match ki with Kstmt s -> s | _ -> assert false in - match postdom with None -> () - | Some postdom -> - let do_edge p = f ((s, true), (p, true)) in - Stmt.Hptset.iter do_edge postdom - in - Kinstr.Hashtbl.iter do_s graph - - - let vertex_name (s, _) = string_of_int s.sid - - let graph_attributes (title, _) = [`Label title] - - let default_vertex_attributes _g = [`Style `Filled] - let default_edge_attributes _g = [] - - let vertex_attributes (s, has_postdom) = - let attrib = [] in - let txt = Format.asprintf "%a" V.pretty s in - let attrib = (`Label txt) :: attrib in - let color = if has_postdom then 0x7FFFD4 else 0xFF0000 in - let attrib = (`Shape `Box) :: attrib in - let attrib = (`Fillcolor color) :: attrib in - attrib - - let edge_attributes _s = [] - - let get_subgraph _v = None -end - -module PostdomGraph = Graph.Graphviz.Dot(Printer) - -let get_postdom kf graph s = - try - match Kinstr.Hashtbl.find graph (Kstmt s) with - | None -> Stmt.Hptset.empty - | Some l -> l - with Not_found -> - try - let postdom = Compute.stmt_postdominators kf s in - let postdom = Stmt.Hptset.remove s postdom in - Postdominators_parameters.debug "postdom for %d:%a = %a\n" - s.sid pretty_stmt s Stmt.Hptset.pretty postdom; - Kinstr.Hashtbl.add graph (Kstmt s) (Some postdom); postdom - with Compute.Top -> - Kinstr.Hashtbl.add graph (Kstmt s) None; - raise Compute.Top - -(** [s_postdom] are [s] postdominators, including [s]. - * We don't have to represent the relation between s and s. - * And because the postdom relation is transitive, if [p] is in [s_postdom], - * we can remove [p_postdom] from [s_postdom] in order to have a clearer graph. -*) -let reduce kf graph s = - let remove p s_postdom = - if Stmt.Hptset.mem p s_postdom - then - try - let p_postdom = get_postdom kf graph p in - let s_postdom = Stmt.Hptset.diff s_postdom p_postdom - in s_postdom - with Compute.Top -> assert false - (* p postdom s -> cannot be top *) - else s_postdom (* p has already been removed from s_postdom *) - in - try - let postdom = get_postdom kf graph s in - let postdom = Stmt.Hptset.fold remove postdom postdom in - Postdominators_parameters.debug "new postdom for %d:%a = %a\n" - s.sid pretty_stmt s Stmt.Hptset.pretty postdom; - Kinstr.Hashtbl.replace graph (Kstmt s) (Some postdom) - with Compute.Top -> - () - -let build_reduced_graph kf graph stmts = - List.iter (reduce kf graph) stmts - -let build_dot filename kf = - match kf.fundec with - | Definition (fct, _) -> - let stmts = fct.sallstmts in - let graph = Kinstr.Hashtbl.create (List.length stmts) in - let _ = build_reduced_graph kf graph stmts in - let name = Kernel_function.get_name kf in - let title = "Postdominators for function " ^ name in - let file = open_out filename in - PostdomGraph.output_graph file (title, graph); - close_out file - | Declaration _ -> - Kernel.error "cannot compute for a function without body %a" - Kernel_function.pretty kf - -(* -Local Variables: -compile-command: "make -C ../../.." -End: -*) diff --git a/src/plugins/postdominators/print.mli b/src/plugins/postdominators/print.mli deleted file mode 100644 index 95ea33441d9327a25a8634300dd49225e542b2dd..0000000000000000000000000000000000000000 --- a/src/plugins/postdominators/print.mli +++ /dev/null @@ -1,23 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of Frama-C. *) -(* *) -(* Copyright (C) 2007-2024 *) -(* CEA (Commissariat à l'énergie atomique et aux énergies *) -(* alternatives) *) -(* *) -(* you can redistribute it and/or modify it under the terms of the GNU *) -(* Lesser General Public License as published by the Free Software *) -(* Foundation, version 2.1. *) -(* *) -(* It is distributed in the hope that it will be useful, *) -(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) -(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) -(* GNU Lesser General Public License for more details. *) -(* *) -(* See the GNU Lesser General Public License version 2.1 *) -(* for more details (enclosed in the file licenses/LGPLv2.1). *) -(* *) -(**************************************************************************) - -val build_dot: string -> Kernel_function.t -> unit diff --git a/src/plugins/scope/datascope.ml b/src/plugins/scope/datascope.ml index a65f14d60462aaa3428c9e69e27e3fbd6405af58..9715cdf49f62fe2e22101b3b8a2bc110260a59d1 100644 --- a/src/plugins/scope/datascope.ml +++ b/src/plugins/scope/datascope.ml @@ -537,8 +537,7 @@ let get_prop_scope_at_stmt ~warn kf stmt ?(proven=CA_Map.empty) annot = end; let add ((acc_scope, acc_to_be_rm) as acc) s = match pre_state s with | State.SameVal -> - if Dominators.dominates stmt s && not (Cil_datatype.Stmt.equal stmt s) - then + if Dominators.strictly_dominates stmt s then let acc_scope = add_s s acc_scope in let acc_to_be_rm = check_stmt_annots (annot, stmt) s acc_to_be_rm in (acc_scope, acc_to_be_rm) diff --git a/tests/misc/oracle/dom_graph.f.dot b/tests/misc/oracle/dom_graph.f.dot new file mode 100644 index 0000000000000000000000000000000000000000..ca126d8e69606de83dd763955586838d1244081d --- /dev/null +++ b/tests/misc/oracle/dom_graph.f.dot @@ -0,0 +1,56 @@ +digraph G { + label="Dominators for function f"; + node [shape=box, style="filled", ]; + 1 [label="/* sid:1 */ +c = 12;", ]; + 3 [label="/* sid:3 */ +if (c) { + /* sid:4 */ + test: c = 42; + /* sid:5 */ + goto test; +} +else { + /* sid:6 */ + c = 12; + /* sid:53 */ + { + /* sid:8 */ + __retres = c; + /* sid:54 */ + goto return_label; + } +}", + ]; + 4 [label="/* sid:4 */ +test: c = 42;", ]; + 53 [label="/* sid:53 */ +{ + /* sid:8 */ + __retres = c; + /* sid:54 */ + goto return_label; +}", + ]; + 5 [label="/* sid:5 */ +goto test;", ]; + 54 [label="/* sid:54 */ +goto return_label;", ]; + 6 [label="/* sid:6 */ +c = 12;", ]; + 55 [label="/* sid:55 */ +return_label: return __retres;", ]; + 8 [label="/* sid:8 */ +__retres = c;", ]; + + + 1 -> 3; + 3 -> 4; + 6 -> 53; + 4 -> 5; + 8 -> 54; + 3 -> 6; + 54 -> 55; + 53 -> 8; + + } diff --git a/tests/misc/oracle/dom_graph.g.dot b/tests/misc/oracle/dom_graph.g.dot new file mode 100644 index 0000000000000000000000000000000000000000..8d9ff3a679f7c9177486a7097ff2c4094561cfdd --- /dev/null +++ b/tests/misc/oracle/dom_graph.g.dot @@ -0,0 +1,63 @@ +digraph G { + label="Dominators for function g"; + node [shape=box, style="filled", ]; + 16 [label="/* sid:16 */ +c = 12;", ]; + 18 [label="/* sid:18 */ +__retres = c;", ]; + 20 [label="/* sid:20 */ +__retres = c;", ]; + 57 [label="/* sid:57 */ +{ + /* sid:18 */ + __retres = c; + /* sid:58 */ + goto return_label; +}", + ]; + 58 [label="/* sid:58 */ +goto return_label;", ]; + 10 [label="/* sid:10 */ +c = 12;", ]; + 59 [label="/* sid:59 */ +return_label: return __retres;", ]; + 12 [label="/* sid:12 */ +if (c) { + /* sid:13 */ + goto test; + /* sid:14 */ + c += 1; + /* sid:15 */ + test: c = 42; +} +else { + /* sid:16 */ + c = 12; + /* sid:57 */ + { + /* sid:18 */ + __retres = c; + /* sid:58 */ + goto return_label; + } +}", + ]; + 13 [label="/* sid:13 */ +goto test;", ]; + 14 [label="/* sid:14 */ +c += 1;", ]; + 15 [label="/* sid:15 */ +test: c = 42;", ]; + + + 12 -> 16; + 57 -> 18; + 15 -> 20; + 16 -> 57; + 18 -> 58; + 12 -> 59; + 10 -> 12; + 12 -> 13; + 13 -> 15; + + } diff --git a/tests/misc/oracle/dom_graph.h.dot b/tests/misc/oracle/dom_graph.h.dot new file mode 100644 index 0000000000000000000000000000000000000000..2826acff315de2ec5020301ef387f3faa4f016dd --- /dev/null +++ b/tests/misc/oracle/dom_graph.h.dot @@ -0,0 +1,113 @@ +digraph G { + label="Dominators for function h"; + node [shape=box, style="filled", ]; + 33 [label="/* sid:33 */ +j += 1;", ]; + 34 [label="/* sid:34 */ +stop();", ]; + 36 [label="/* sid:36 */ +return x;", ]; + 22 [label="/* sid:22 */ +{ + /* sid:23 */ + int j = 0; + /* sid:24 */ + while (1) { + /* sid:26 */ + if (j < 10) { + + } + else { + /* sid:27 */ + break; + } + /* sid:28 */ + { + /* sid:30 */ + if (j % 2 == 0) { + /* sid:31 */ + x += 1; + } + else { + + } + } + /* sid:33 */ + j += 1; + } +}", + ]; + 23 [label="/* sid:23 */ +int j = 0;", ]; + 24 [label="/* sid:24 */ +while (1) { + /* sid:26 */ + if (j < 10) { + + } + else { + /* sid:27 */ + break; + } + /* sid:28 */ + { + /* sid:30 */ + if (j % 2 == 0) { + /* sid:31 */ + x += 1; + } + else { + + } + } + /* sid:33 */ + j += 1; +}", + ]; + 26 [label="/* sid:26 */ +if (j < 10) { + +} +else { + /* sid:27 */ + break; +}", + ]; + 27 [label="/* sid:27 */ +break;", ]; + 28 [label="/* sid:28 */ +{ + /* sid:30 */ + if (j % 2 == 0) { + /* sid:31 */ + x += 1; + } + else { + + } +}", + ]; + 30 [label="/* sid:30 */ +if (j % 2 == 0) { + /* sid:31 */ + x += 1; +} +else { + +}", + ]; + 31 [label="/* sid:31 */ +x += 1;", ]; + + + 30 -> 33; + 27 -> 34; + 22 -> 23; + 23 -> 24; + 24 -> 26; + 26 -> 27; + 26 -> 28; + 28 -> 30; + 30 -> 31; + + } diff --git a/tests/misc/oracle/dom_graph.i.dot b/tests/misc/oracle/dom_graph.i.dot new file mode 100644 index 0000000000000000000000000000000000000000..9a4bece54956587ce9dae0bd4f0b35284a442c10 --- /dev/null +++ b/tests/misc/oracle/dom_graph.i.dot @@ -0,0 +1,76 @@ +digraph G { + label="Dominators for function i"; + node [shape=box, style="filled", ]; + 48 [label="/* sid:48 */ +{ + /* sid:49 */ + x += 1; + /* sid:50 */ + loop: ; +}", + ]; + 49 [label="/* sid:49 */ +x += 1;", ]; + 50 [label="/* sid:50 */ +loop: ;", ]; + 38 [label="/* sid:38 */ +int x = 0;", ]; + 40 [label="/* sid:40 */ +if (nondet) { + /* sid:41 */ + goto loop; +} +else { + +}", + ]; + 41 [label="/* sid:41 */ +goto loop;", ]; + 43 [label="/* sid:43 */ +x = 1;", ]; + 44 [label="/* sid:44 */ +while (1) { + /* sid:46 */ + if (x < 10) { + + } + else { + /* sid:47 */ + break; + } + /* sid:48 */ + { + /* sid:49 */ + x += 1; + /* sid:50 */ + loop: ; + } +}", + ]; + 62 [label="/* sid:62 */ +return;", ]; + 46 [label="/* sid:46 */ +if (x < 10) { + +} +else { + /* sid:47 */ + break; +}", + ]; + 47 [label="/* sid:47 */ +break;", ]; + + + 46 -> 48; + 48 -> 49; + 40 -> 50; + 38 -> 40; + 40 -> 41; + 40 -> 43; + 40 -> 44; + 47 -> 62; + 44 -> 46; + 46 -> 47; + + } diff --git a/tests/misc/oracle/postdom_graph.f.dot b/tests/misc/oracle/postdom_graph.f.dot new file mode 100644 index 0000000000000000000000000000000000000000..07312345443505555321d09c6d7bca939a2c46b6 --- /dev/null +++ b/tests/misc/oracle/postdom_graph.f.dot @@ -0,0 +1,54 @@ +digraph G { + label="PostDominators for function f"; + node [shape=box, style="filled", ]; + 1 [label="/* sid:1 */ +c = 12;", ]; + 3 [label="/* sid:3 */ +if (c) { + /* sid:4 */ + test: c = 42; + /* sid:5 */ + goto test; +} +else { + /* sid:6 */ + c = 12; + /* sid:53 */ + { + /* sid:8 */ + __retres = c; + /* sid:54 */ + goto return_label; + } +}", + ]; + 4 [label="/* sid:4 */ +test: c = 42;", ]; + 53 [label="/* sid:53 */ +{ + /* sid:8 */ + __retres = c; + /* sid:54 */ + goto return_label; +}", + ]; + 5 [label="/* sid:5 */ +goto test;", ]; + 54 [label="/* sid:54 */ +goto return_label;", ]; + 6 [label="/* sid:6 */ +c = 12;", ]; + 55 [label="/* sid:55 */ +return_label: return __retres;", ]; + 8 [label="/* sid:8 */ +__retres = c;", ]; + + + 3 -> 1; + 6 -> 3; + 8 -> 53; + 55 -> 54; + 53 -> 6; + 54 -> 8; + + } diff --git a/tests/misc/oracle/postdom_graph.g.dot b/tests/misc/oracle/postdom_graph.g.dot new file mode 100644 index 0000000000000000000000000000000000000000..b67c6c4a95ef21b02083a75cc6de251f36b93280 --- /dev/null +++ b/tests/misc/oracle/postdom_graph.g.dot @@ -0,0 +1,64 @@ +digraph G { + label="PostDominators for function g"; + node [shape=box, style="filled", ]; + 16 [label="/* sid:16 */ +c = 12;", ]; + 18 [label="/* sid:18 */ +__retres = c;", ]; + 20 [label="/* sid:20 */ +__retres = c;", ]; + 57 [label="/* sid:57 */ +{ + /* sid:18 */ + __retres = c; + /* sid:58 */ + goto return_label; +}", + ]; + 58 [label="/* sid:58 */ +goto return_label;", ]; + 10 [label="/* sid:10 */ +c = 12;", ]; + 59 [label="/* sid:59 */ +return_label: return __retres;", ]; + 12 [label="/* sid:12 */ +if (c) { + /* sid:13 */ + goto test; + /* sid:14 */ + c += 1; + /* sid:15 */ + test: c = 42; +} +else { + /* sid:16 */ + c = 12; + /* sid:57 */ + { + /* sid:18 */ + __retres = c; + /* sid:58 */ + goto return_label; + } +}", + ]; + 13 [label="/* sid:13 */ +goto test;", ]; + 14 [label="/* sid:14 */ +c += 1;", ]; + 15 [label="/* sid:15 */ +test: c = 42;", ]; + + + 57 -> 16; + 58 -> 18; + 59 -> 20; + 18 -> 57; + 59 -> 58; + 12 -> 10; + 59 -> 12; + 15 -> 13; + 15 -> 14; + 20 -> 15; + + } diff --git a/tests/misc/oracle/postdom_graph.h.dot b/tests/misc/oracle/postdom_graph.h.dot new file mode 100644 index 0000000000000000000000000000000000000000..0335859b42222119a397f5b135f16a7000b958d4 --- /dev/null +++ b/tests/misc/oracle/postdom_graph.h.dot @@ -0,0 +1,104 @@ +digraph G { + label="PostDominators for function h"; + node [shape=box, style="filled", ]; + 33 [label="/* sid:33 */ +j += 1;", ]; + 34 [label="/* sid:34 */ +stop();", ]; + 36 [label="/* sid:36 */ +return x;", ]; + 22 [label="/* sid:22 */ +{ + /* sid:23 */ + int j = 0; + /* sid:24 */ + while (1) { + /* sid:26 */ + if (j < 10) { + + } + else { + /* sid:27 */ + break; + } + /* sid:28 */ + { + /* sid:30 */ + if (j % 2 == 0) { + /* sid:31 */ + x += 1; + } + else { + + } + } + /* sid:33 */ + j += 1; + } +}", + ]; + 23 [label="/* sid:23 */ +int j = 0;", ]; + 24 [label="/* sid:24 */ +while (1) { + /* sid:26 */ + if (j < 10) { + + } + else { + /* sid:27 */ + break; + } + /* sid:28 */ + { + /* sid:30 */ + if (j % 2 == 0) { + /* sid:31 */ + x += 1; + } + else { + + } + } + /* sid:33 */ + j += 1; +}", + ]; + 26 [label="/* sid:26 */ +if (j < 10) { + +} +else { + /* sid:27 */ + break; +}", + ]; + 27 [label="/* sid:27 */ +break;", ]; + 28 [label="/* sid:28 */ +{ + /* sid:30 */ + if (j % 2 == 0) { + /* sid:31 */ + x += 1; + } + else { + + } +}", + ]; + 30 [label="/* sid:30 */ +if (j % 2 == 0) { + /* sid:31 */ + x += 1; +} +else { + +}", + ]; + 31 [label="/* sid:31 */ +x += 1;", ]; + + + + } diff --git a/tests/misc/oracle/postdom_graph.i.dot b/tests/misc/oracle/postdom_graph.i.dot new file mode 100644 index 0000000000000000000000000000000000000000..64c9cb923c32876d2623b6c6f46fb35effa107a6 --- /dev/null +++ b/tests/misc/oracle/postdom_graph.i.dot @@ -0,0 +1,76 @@ +digraph G { + label="PostDominators for function i"; + node [shape=box, style="filled", ]; + 48 [label="/* sid:48 */ +{ + /* sid:49 */ + x += 1; + /* sid:50 */ + loop: ; +}", + ]; + 49 [label="/* sid:49 */ +x += 1;", ]; + 50 [label="/* sid:50 */ +loop: ;", ]; + 38 [label="/* sid:38 */ +int x = 0;", ]; + 40 [label="/* sid:40 */ +if (nondet) { + /* sid:41 */ + goto loop; +} +else { + +}", + ]; + 41 [label="/* sid:41 */ +goto loop;", ]; + 43 [label="/* sid:43 */ +x = 1;", ]; + 44 [label="/* sid:44 */ +while (1) { + /* sid:46 */ + if (x < 10) { + + } + else { + /* sid:47 */ + break; + } + /* sid:48 */ + { + /* sid:49 */ + x += 1; + /* sid:50 */ + loop: ; + } +}", + ]; + 62 [label="/* sid:62 */ +return;", ]; + 46 [label="/* sid:46 */ +if (x < 10) { + +} +else { + /* sid:47 */ + break; +}", + ]; + 47 [label="/* sid:47 */ +break;", ]; + + + 49 -> 48; + 50 -> 49; + 44 -> 50; + 40 -> 38; + 44 -> 40; + 50 -> 41; + 44 -> 43; + 46 -> 44; + 47 -> 46; + 62 -> 47; + + } diff --git a/tests/misc/oracle/test_dominators.res.oracle b/tests/misc/oracle/test_dominators.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..76bebfc2cd6a1a86e8b1efd075dcf962409ffdde --- /dev/null +++ b/tests/misc/oracle/test_dominators.res.oracle @@ -0,0 +1,257 @@ +[kernel] Parsing test_dominators.c (with preprocessing) + +Computing for function f: +{ + int __retres; + /* sid:1 */ + c = 12; + /* sid:3 */ + if (c) { + /* sid:4 */ + test: c = 42; + /* sid:5 */ + goto test; + } + else { + /* sid:6 */ + c = 12; + /* sid:53 */ + { + /* sid:8 */ + __retres = c; + /* sid:54 */ + goto return_label; + } + } + /* sid:55 */ + return_label: return __retres; +} +[kernel] Dominators: dot file generated in dom_graph.f.dot for function f +[kernel] PostDominators: dot file generated in postdom_graph.f.dot for function f +Immediate dominators of f (sid, idom, ipostdom): + (1, none, 3), (3, 1, 6), (4, 3, none), (5, 4, none), (6, 3, 53), + (53, 6, 8), (8, 53, 54), (54, 8, 55), (55, 54, none) + +Computing for function g: +{ + int __retres; + /* sid:10 */ + c = 12; + /* sid:12 */ + if (c) { + /* sid:13 */ + goto test; + /* sid:14 */ + c += 1; + /* sid:15 */ + test: c = 42; + } + else { + /* sid:16 */ + c = 12; + /* sid:57 */ + { + /* sid:18 */ + __retres = c; + /* sid:58 */ + goto return_label; + } + } + /* sid:20 */ + __retres = c; + /* sid:59 */ + return_label: return __retres; +} +[kernel] Dominators: dot file generated in dom_graph.g.dot for function g +[kernel] PostDominators: dot file generated in postdom_graph.g.dot for function g +Immediate dominators of g (sid, idom, ipostdom): + (10, none, 12), (12, 10, 59), (13, 12, 15), (14, none, 15), (15, 13, 20), + (16, 12, 57), (57, 16, 18), (18, 57, 58), (58, 18, 59), (20, 15, 59), + (59, 12, none) + +Computing for function h: +{ + /* sid:22 */ + { + /* sid:23 */ + int j = 0; + /* sid:24 */ + while (1) { + /* sid:26 */ + if (j < 10) { + + } + else { + /* sid:27 */ + break; + } + /* sid:28 */ + { + /* sid:30 */ + if (j % 2 == 0) { + /* sid:31 */ + x += 1; + } + else { + + } + } + /* sid:33 */ + j += 1; + } + } + /* sid:34 */ + stop(); + /* sid:36 */ + return x; +} +[kernel] Dominators: dot file generated in dom_graph.h.dot for function h +[kernel] PostDominators: dot file generated in postdom_graph.h.dot for function h +Immediate dominators of h (sid, idom, ipostdom): + (22, none, none), (23, 22, none), (24, 23, none), (26, 24, none), + (27, 26, none), (28, 26, none), (30, 28, none), (31, 30, none), + (33, 30, none), (34, 27, none), (36, none, none) + +Computing for function i: +{ + /* sid:38 */ + int x = 0; + /* sid:40 */ + if (nondet) { + /* sid:41 */ + goto loop; + } + else { + + } + /* sid:43 */ + x = 1; + /* sid:44 */ + while (1) { + /* sid:46 */ + if (x < 10) { + + } + else { + /* sid:47 */ + break; + } + /* sid:48 */ + { + /* sid:49 */ + x += 1; + /* sid:50 */ + loop: ; + } + } + /* sid:62 */ + return; +} +[kernel] Dominators: dot file generated in dom_graph.i.dot for function i +[kernel] PostDominators: dot file generated in postdom_graph.i.dot for function i +Immediate dominators of i (sid, idom, ipostdom): + (38, none, 40), (40, 38, 44), (41, 40, 50), (43, 40, 44), (44, 40, 46), + (46, 44, 47), (47, 46, 62), (48, 46, 49), (49, 48, 50), (50, 40, 44), + (62, 47, none) + +Dominators analysis: + Stmt:1 -> {1} + Stmt:3 -> {1,3} + Stmt:4 -> {1,3,4} + Stmt:5 -> {1,3,4,5} + Stmt:6 -> {1,3,6} + Stmt:8 -> {1,3,6,8,53} + Stmt:10 -> {10} + Stmt:12 -> {10,12} + Stmt:13 -> {10,12,13} + Stmt:14 -> {} + Stmt:15 -> {10,12,13,15} + Stmt:16 -> {10,12,16} + Stmt:18 -> {10,12,16,18,57} + Stmt:20 -> {10,12,13,15,20} + Stmt:22 -> {22} + Stmt:23 -> {22,23} + Stmt:24 -> {22,23,24} + Stmt:26 -> {22,23,24,26} + Stmt:27 -> {22,23,24,26,27} + Stmt:28 -> {22,23,24,26,28} + Stmt:30 -> {22,23,24,26,28,30} + Stmt:31 -> {22,23,24,26,28,30,31} + Stmt:33 -> {22,23,24,26,28,30,33} + Stmt:34 -> {22,23,24,26,27,34} + Stmt:36 -> {} + Stmt:38 -> {38} + Stmt:40 -> {38,40} + Stmt:41 -> {38,40,41} + Stmt:43 -> {38,40,43} + Stmt:44 -> {38,40,44} + Stmt:46 -> {38,40,44,46} + Stmt:47 -> {38,40,44,46,47} + Stmt:48 -> {38,40,44,46,48} + Stmt:49 -> {38,40,44,46,48,49} + Stmt:50 -> {38,40,50} + Stmt:53 -> {1,3,6,53} + Stmt:54 -> {1,3,6,8,53,54} + Stmt:55 -> {1,3,6,8,53,54,55} + Stmt:57 -> {10,12,16,57} + Stmt:58 -> {10,12,16,18,57,58} + Stmt:59 -> {10,12,59} + Stmt:62 -> {38,40,44,46,47,62} + +Postominators analysis: + Stmt:1 -> {1,3,6,8,53,54,55} + Stmt:3 -> {3,6,8,53,54,55} + Stmt:4 -> {} + Stmt:5 -> {} + Stmt:6 -> {6,8,53,54,55} + Stmt:8 -> {8,54,55} + Stmt:10 -> {10,12,59} + Stmt:12 -> {12,59} + Stmt:13 -> {13,15,20,59} + Stmt:14 -> {14,15,20,59} + Stmt:15 -> {15,20,59} + Stmt:16 -> {16,18,57,58,59} + Stmt:18 -> {18,58,59} + Stmt:20 -> {20,59} + Stmt:22 -> {} + Stmt:23 -> {} + Stmt:24 -> {} + Stmt:26 -> {} + Stmt:27 -> {} + Stmt:28 -> {} + Stmt:30 -> {} + Stmt:31 -> {} + Stmt:33 -> {} + Stmt:34 -> {} + Stmt:36 -> {36} + Stmt:38 -> {38,40,44,46,47,62} + Stmt:40 -> {40,44,46,47,62} + Stmt:41 -> {41,44,46,47,50,62} + Stmt:43 -> {43,44,46,47,62} + Stmt:44 -> {44,46,47,62} + Stmt:46 -> {46,47,62} + Stmt:47 -> {47,62} + Stmt:48 -> {44,46,47,48,49,50,62} + Stmt:49 -> {44,46,47,49,50,62} + Stmt:50 -> {44,46,47,50,62} + Stmt:53 -> {8,53,54,55} + Stmt:54 -> {54,55} + Stmt:55 -> {55} + Stmt:57 -> {18,57,58,59} + Stmt:58 -> {58,59} + Stmt:59 -> {59} + Stmt:62 -> {62} + +Invalidate tables, which should now be empty + +Dominators analysis: + Empty + +Postominators analysis: + Empty + +Trigger some errors/warnings : +[kernel] Failure: Statement -1 is not part of a function +[kernel] Warning: No statement in function stop: Dominators analysis cannot be done +[kernel] Warning: No statement in function stop: PostDominators analysis cannot be done +[kernel] Warning: cannot compute Dominators for function stop without body diff --git a/tests/misc/test_dominators.c b/tests/misc/test_dominators.c new file mode 100644 index 0000000000000000000000000000000000000000..a461d101dce754b2061bc83fdf2bddc1a137e38f --- /dev/null +++ b/tests/misc/test_dominators.c @@ -0,0 +1,54 @@ +/* run.config + MODULE: @PTEST_NAME@ + LOG: postdom_graph.f.dot dom_graph.f.dot + LOG: postdom_graph.g.dot dom_graph.g.dot + LOG: postdom_graph.h.dot dom_graph.h.dot + LOG: postdom_graph.i.dot dom_graph.i.dot + OPT: -kernel-msg-key printer:sid -print-as-is +*/ + +void stop(void) __attribute__ ((noreturn)) ; + +int f (int c){ + c = 12; + if (c) { + test: c = 42; + goto test; + } + else { + c = 12; + return c; + } +} + +int g (int c){ + c = 12; + if (c) { + goto test; + c++; + test: c = 42; + } + else { + c = 12; + return c; + } + return c; +} + +int h(int x){ + for (int j = 0; j < 10; j++){ + if (j % 2 == 0) x++; + } + stop(); + return x; +} + +void i (int nondet) { + int x = 0; + if (nondet) goto loop; + x = 1; + while (x < 10) { + x++; + loop: ; + } +} diff --git a/tests/misc/test_dominators.ml b/tests/misc/test_dominators.ml new file mode 100644 index 0000000000000000000000000000000000000000..eb26706d80b6a8578ae0d7e1d740e336c4250e21 --- /dev/null +++ b/tests/misc/test_dominators.ml @@ -0,0 +1,135 @@ +open Cil_types + +module StmtSet = Cil_datatype.Stmt.Hptset + +let get = function + | None -> "none" + | Some stmt -> string_of_int stmt.sid + +let pp_res = + Pretty_utils.pp_list ~pre:"(" ~sep:", " ~suf:")" Format.pp_print_string + +(** For each statement of [f], find its immediate dominator and postdominator + and print the triplets. *) +let print_immediate f = + let res = + List.map (fun s -> + let dom = Dominators.get_idom s in + let postdom = Dominators.get_ipostdom s in + [string_of_int s.sid; get dom; get postdom] + ) f.sallstmts + in + Format.printf "@[<v2>Immediate dominators of %s (sid, idom, ipostdom):@;%a@]@;" + f.svar.vname + (Pretty_utils.pp_list ~pre:"@[" ~sep:",@ " ~suf:"@]" pp_res) res + +(* Make sure that the difference between (post)dominators and strict + (post)dominators of a statement [s] is equal to the singleton [s]. *) +let test_strict f = + let test s dom strict_dom = + if StmtSet.is_empty dom + then assert (StmtSet.is_empty strict_dom) + else assert (StmtSet.diff dom strict_dom == StmtSet.singleton s) + in + List.iter (fun s -> + let dom = Dominators.get_dominators s in + let sdom = Dominators.get_strict_dominators s in + let postdom = Dominators.get_postdominators s in + let spostdom = Dominators.get_strict_postdominators s in + test s dom sdom; + test s postdom spostdom; + assert (Dominators.strictly_dominates s s = false); + assert (Dominators.strictly_postdominates s s = false) + ) f.sallstmts + +let test_nearest kf f = + (* Simple test for empty list coverage of nearest function. *) + assert (Dominators.nearest_common_ancestor [] = None); + assert (Dominators.nearest_common_child [] = None); + + (* Since first and last statement do not have an ancestor/child, + common nearest for all statements should be empty. *) + assert (Dominators.nearest_common_ancestor f.sallstmts = None); + assert (Dominators.nearest_common_child f.sallstmts = None); + + if List.length f.sallstmts > 1 then begin + + let first = Kernel_function.find_first_stmt kf in + let last = Kernel_function.find_return kf in + let all_but_first = List.filter (fun s -> s != first) f.sallstmts in + let all_but_last = List.filter (fun s -> s != last) f.sallstmts in + + (* Test that all statements (except the first one) have as common ancestor + the first statement of f, unless one of them is unreachable. *) + begin match Dominators.nearest_common_ancestor all_but_first with + | None -> (* At leats one statement is unreachable. *) () + | Some ancestor -> assert (first == ancestor) + end; + + (* Test that all statements (except the last one) have as common child + the return statement of f, unless one of them is unreachable. This test + supposes that frama-c's normalization removed all but one return statement. + *) + begin match Dominators.nearest_common_child all_but_last with + | None -> (* At leats one statement is unreachable. *) () + | Some child -> assert (last == child) + end + end + +class visitPostDom = object(self) + inherit Visitor.frama_c_inplace + + method! vfunc f = + let kf = Option.get (self#current_kf) in + Format.printf "@.@[<v>Computing for function %s:@;%a@?@;@?@]" + f.svar.vname Cil_printer.pp_block f.sbody; + + Dominators.compute_dominators kf; + Dominators.print_dot_dominators "dom_graph" kf; + + Dominators.compute_postdominators kf; + Dominators.print_dot_postdominators "postdom_graph" kf; + + print_immediate f; + test_strict f; + test_nearest kf f; + + SkipChildren +end + +let cover_errors () = + Format.printf "Trigger some errors/warnings :@."; + + (* Test when a statement is not in a function. *) + let loc = Cil_datatype.Location.unknown in + let skip = Cil_builder.Pure.(cil_stmt ~loc skip) in + try ignore (Dominators.get_postdominators skip) with Log.AbortFatal _ -> (); + + let trigger kf = + match kf.fundec with + | Definition _ -> () + | Declaration _ -> + (* Test Kernel_function.find_first_stmt on decl. *) + ignore @@ Dominators.compute_dominators kf; + (* Test Kernel_function.find_return on decl. *) + ignore @@ Dominators.compute_postdominators kf; + (* Test print_dot on decl. *) + ignore @@ Dominators.print_dot_dominators "tmp" kf + in + Globals.Functions.iter trigger + +let pretty () = + Format.printf "@.@[<v2>Dominators analysis:@;%a@]\n@." + Dominators.pretty_dominators (); + Format.printf "@[<v2>Postominators analysis:@;%a@]\n@." + Dominators.pretty_postdominators () + +let startup () = + ignore (Cil.visitCilFileSameGlobals (new visitPostDom:>Cil.cilVisitor) (Ast.get ())); + pretty (); + Ast.mark_as_changed (); + Format.printf "Invalidate tables, which should now be empty\n"; + pretty (); + cover_errors () + +let () = Boot.Main.extend startup