From 6bc0ed2d2c9744b8be5e1d33ebb6d489cf0366fc Mon Sep 17 00:00:00 2001 From: Valentin Perrelle <valentin.perrelle@cea.fr> Date: Tue, 23 Apr 2019 19:11:45 +0200 Subject: [PATCH] [Interpreted automata] Add a dataflow computation functor --- .../analysis/interpreted_automata.ml | 88 ++++++++++++++ .../analysis/interpreted_automata.mli | 18 +++ tests/misc/interpreted_automata_dataflow.i | 23 ++++ tests/misc/interpreted_automata_dataflow.ml | 115 ++++++++++++++++++ .../interpreted_automata_dataflow.res.oracle | 5 + 5 files changed, 249 insertions(+) create mode 100644 tests/misc/interpreted_automata_dataflow.i create mode 100644 tests/misc/interpreted_automata_dataflow.ml create mode 100644 tests/misc/oracle/interpreted_automata_dataflow.res.oracle diff --git a/src/kernel_services/analysis/interpreted_automata.ml b/src/kernel_services/analysis/interpreted_automata.ml index fd73d9c1eb2..a1159e205a6 100644 --- a/src/kernel_services/analysis/interpreted_automata.ml +++ b/src/kernel_services/analysis/interpreted_automata.ml @@ -941,6 +941,9 @@ let is_wto_head kf = Compute.is_wto_head (get_wto_index_table kf) let is_back_edge kf = Compute.is_back_edge (get_wto_index_table kf) +(* ---------------------------------------------------------------------- *) +(* --- Graph with only one entry per component --- *) +(* ---------------------------------------------------------------------- *) module UnrollUnnatural = struct @@ -1090,3 +1093,88 @@ module UnrollUnnatural = struct g' end + + +(* ---------------------------------------------------------------------- *) +(* --- Dataflow computation --- *) +(* ---------------------------------------------------------------------- *) + +module type Domain = +sig + type t + + val join : t -> t -> t + val widen : t -> t -> t option (* returns None when inclusion *) + val transfer : vertex transition -> t -> t option +end + + +module Dataflow (D : Domain) = +struct + module Results = Vertex.Hashtbl + + let fixpoint kf initial_value = + let automaton = get_automaton kf in + let graph = automaton.graph in + let wto = get_wto kf in + let results = Results.create (G.nb_vertex graph) in + + (* Compute the transfer function for the given edge and add the result to + acc *) + let process_edge (v1,e,_v2) acc = + (* Retrieve origin value *) + match Results.find_opt results v1 with + | None -> acc (* No previous value *) + | Some value -> + match D.transfer e.edge_transition value with + | None -> acc + | Some new_value -> new_value :: acc + in + + (* Compute the abstract value for the given control point ; compute all + incoming transfer functions *) + let process_vertex v = + let incomming = G.fold_pred_e process_edge graph v [] + and initial = if v == automaton.entry_point then [initial_value] else [] + in + match initial @ incomming with + | [] -> (* Zero incomming values -> Bottom *) + Results.remove results v + | v1 :: vl -> + (* Join incomming values *) + let result = List.fold_left D.join v1 vl in + Results.add results v result + in + + (* widen returns whether it is necessary to continue to iterate or not *) + let widen v previous = + let current = Results.find_opt results v in + match previous, current with + | _, None -> false (* Current is bottom, let's quit *) + | None, _ -> true (* Previous was bottom *) + | Some v1, Some v2 -> + match D.widen v1 v2 with + | None -> false (* End of iteration *) + | Some value -> (* new value *) + Results.add results v value; + true + in + + let rec iterate_list l = + List.iter iterate_element l + and iterate_element = function + | Wto.Node v -> + ignore (process_vertex v) + | Wto.Component (v, w) -> + while + let previous = Results.find_opt results v in + process_vertex v; + widen v previous + do + iterate_list w; + done; + in + iterate_list wto; + results +end + diff --git a/src/kernel_services/analysis/interpreted_automata.mli b/src/kernel_services/analysis/interpreted_automata.mli index c7c1b86d873..dec17586091 100644 --- a/src/kernel_services/analysis/interpreted_automata.mli +++ b/src/kernel_services/analysis/interpreted_automata.mli @@ -244,3 +244,21 @@ module UnrollUnnatural : sig automaton -> wto -> Compute.wto_index_table -> G.t end + + +(* Dataflow computation *) + +module type Domain = +sig + type t + + val join : t -> t -> t + val widen : t -> t -> t option (* returns None when inclusion *) + val transfer : vertex transition -> t -> t option +end + +module Dataflow (D : Domain) : +sig + val fixpoint : Cil_types.kernel_function -> D.t -> D.t Vertex.Hashtbl.t +end + diff --git a/tests/misc/interpreted_automata_dataflow.i b/tests/misc/interpreted_automata_dataflow.i new file mode 100644 index 00000000000..0dbe0d14845 --- /dev/null +++ b/tests/misc/interpreted_automata_dataflow.i @@ -0,0 +1,23 @@ +/* run.config +OPT: -load-script tests/misc/interpreted_automata_dataflow.ml +*/ + +void main(int x) +{ + int y = 3; + y = y * 2; + + int z = y + 1; + int w = y + x; + int a = 1; + + for (int i = 0 ; i < 10 ; i ++) { + int b = 3; + int c = i + 1; + a = a + 1; + } + + if (x != 3) + x = 3; +} + diff --git a/tests/misc/interpreted_automata_dataflow.ml b/tests/misc/interpreted_automata_dataflow.ml new file mode 100644 index 00000000000..b23a0a3cd8d --- /dev/null +++ b/tests/misc/interpreted_automata_dataflow.ml @@ -0,0 +1,115 @@ +open Cil_types + +module Map = Cil_datatype.Varinfo.Map + +module ConstantsDomain = +struct + type t = int Map.t + + let top = Map.empty + + let pretty fmt v = + let pp_entry vi x = + Format.fprintf fmt "%s -> %d@." vi.vorig_name x + in + Map.iter pp_entry v + + let join v1 v2 = + let merge_entry _vi o1 o2 = + match o1, o2 with + | None, _ | _, None -> None + | Some x, Some x' -> if x = x' then Some x else None + in + Map.merge merge_entry v1 v2 + + let widen v1 v2 = + let same_entry vi x = + Map.find_opt vi v2 = Some x + in + if Map.for_all same_entry v1 then + None (* Inclusion *) + else + Some v2 (* No widening necessary *) + + exception Not_constant + + let rec eval v exp = + match exp.enode with + | Const (CInt64 (i,_,_)) -> + (try Integer.to_int i with _ -> raise Not_constant) + | Lval (Var vi, NoOffset) -> + (try Map.find vi v with Not_found -> raise Not_constant) + | SizeOf typ -> + Cil.bytesSizeOf typ + | UnOp (Neg, e, _) -> + -(eval v e) + | BinOp (PlusA, e1, e2, _) -> + (eval v e1) + (eval v e2) + | BinOp (MinusA, e1, e2, _) -> + (eval v e1) - (eval v e2) + | BinOp (Mult, e1, e2, _) -> + (eval v e1) * (eval v e2) + | BinOp (Div, e1, e2, _) -> + let x = eval v e2 in + if x <> 0 then (eval v e1) / x else raise Not_constant + | _ -> + raise Not_constant + + let eval_opt v exp = + try Some (eval v exp) with Not_constant -> None + + let assume v exp kind = + match exp.enode, kind with + | BinOp (Eq, e1, e2, _), Interpreted_automata.Then + | BinOp (Ne, e1, e2, _), Interpreted_automata.Else -> + begin match eval_opt v e1, eval_opt v e2 with + | None, None -> Some v + | Some x, None -> + begin match e2.enode with + | Lval (Var vi, NoOffset) -> Some (Map.add vi x v) + | _ -> Some v + end + | None, Some x -> + begin match e1.enode with + | Lval (Var vi, NoOffset) -> Some (Map.add vi x v) + | _ -> Some v + end + | Some x, Some y -> + if x = y then Some v else None + end + | _ -> Some v + + let assign v vi exp = + try + Map.add vi (eval v exp) v + with Not_constant -> + Map.remove vi v + + let transfer t v = + let open Interpreted_automata in + match t with + | Skip | Return _ | Prop _ | Enter _ | Leave _ -> Some v + | Guard (exp, kind, _) -> assume v exp kind + | Instr (Set ((Var vi, NoOffset), exp, _), _) -> Some (assign v vi exp) + | Instr (Local_init (vi, AssignInit (SingleInit exp), _), _) -> + Some (assign v vi exp) + | Instr (Local_init (_vi, AssignInit (CompoundInit _), _), _) -> Some v + | Instr ((Call _ | Local_init _ | Set _ | Asm _), _) -> Some top + | Instr ((Cil_types.Skip _ | Code_annot _), _) -> Some v +end + + +module Dataflow = Interpreted_automata.Dataflow (ConstantsDomain) + +let run () = + let main_kf, _ = Globals.entry_point () in + let results = Dataflow.fixpoint main_kf ConstantsDomain.top in + let result = Interpreted_automata.( + Vertex.Hashtbl.find results (get_automaton main_kf).return_point) + in + Kernel.result "Results at the end of function %s:@.%a" + (Kernel_function.get_name main_kf) + ConstantsDomain.pretty result + +let () = + Db.Main.extend run diff --git a/tests/misc/oracle/interpreted_automata_dataflow.res.oracle b/tests/misc/oracle/interpreted_automata_dataflow.res.oracle new file mode 100644 index 00000000000..75741cada58 --- /dev/null +++ b/tests/misc/oracle/interpreted_automata_dataflow.res.oracle @@ -0,0 +1,5 @@ +[kernel] Parsing tests/misc/interpreted_automata_dataflow.i (no preprocessing) +[kernel] Results at the end of function main: + x -> 3 + y -> 6 + z -> 7 -- GitLab