diff --git a/src/plugins/value/engine/partition.ml b/src/plugins/value/engine/partition.ml index 77a8ef9fdcd2b501b4d96d357aae05b5680cfc13..8b71d9866ca5002aec4077a1e6a8a9d13d97fa8a 100644 --- a/src/plugins/value/engine/partition.ml +++ b/src/plugins/value/engine/partition.ml @@ -20,6 +20,8 @@ (* *) (**************************************************************************) +(* --- Keys --- *) + module ExpMap = Cil_datatype.ExpStructEq.Map module IntPair = Datatype.Pair (Datatype.Int) (Datatype.Int) module LoopList = Datatype.List (IntPair) @@ -35,39 +37,19 @@ type key = { dynamic_split : Integer.t ExpMap.t; } -let zero_key : key = { - ration_stamp = None; - branches = []; - loops = []; - static_split = ExpMap.empty; - dynamic_split = ExpMap.empty; -} - -let pretty_key fmt key = - begin match key.ration_stamp with - | Some (n,_) -> Format.fprintf fmt "#%d" n - | None -> () - end; - Pretty_utils.pp_list ~pre:"[@[" ~sep:" ;@ " ~suf:"@]]" - Format.pp_print_int - fmt - key.branches; - Pretty_utils.pp_list ~pre:"(@[" ~sep:" ;@ " ~suf:"@])" - (fun fmt (i,_j) -> Format.pp_print_int fmt i) - fmt - key.loops; - Pretty_utils.pp_list ~pre:"{@[" ~sep:" ;@ " ~suf:"@]}" - (fun fmt (e,i) -> Format.fprintf fmt "%a:%a" - Cil_printer.pp_exp e - (Integer.pretty ~hexa:false) i) - fmt - (ExpMap.bindings key.static_split @ ExpMap.bindings key.dynamic_split) - - module Key = struct type t = key + (* Initial key, before any partitioning *) + let zero = { + ration_stamp = None; + branches = []; + loops = []; + static_split = ExpMap.empty; + dynamic_split = ExpMap.empty; + } + let compare k1 k2 = let (<?>) c (cmp,x,y) = if c = 0 then cmp x y else c @@ -77,12 +59,57 @@ struct <?> (ExpMap.compare Integer.compare, k1.static_split, k2.static_split) <?> (ExpMap.compare Integer.compare, k1.dynamic_split, k2.dynamic_split) <?> (BranchList.compare, k1.branches, k2.branches) + + let pretty fmt key = + begin match key.ration_stamp with + | Some (n,_) -> Format.fprintf fmt "#%d" n + | None -> () + end; + Pretty_utils.pp_list ~pre:"[@[" ~sep:" ;@ " ~suf:"@]]" + Format.pp_print_int + fmt + key.branches; + Pretty_utils.pp_list ~pre:"(@[" ~sep:" ;@ " ~suf:"@])" + (fun fmt (i,_j) -> Format.pp_print_int fmt i) + fmt + key.loops; + Pretty_utils.pp_list ~pre:"{@[" ~sep:" ;@ " ~suf:"@]}" + (fun fmt (e,i) -> Format.fprintf fmt "%a:%a" + Cil_printer.pp_exp e + (Integer.pretty ~hexa:false) i) + fmt + (ExpMap.bindings key.static_split @ ExpMap.bindings key.dynamic_split) end -module KMap = Map.Make (Key) +(* --- Partitions --- *) + +module KMap = Map.Make (Key) type 'a partition = 'a KMap.t + +let empty = KMap.empty +let find = KMap.find +let replace = KMap.add +let is_empty = KMap.is_empty +let size = KMap.cardinal +let iter = KMap.iter +let map = KMap.map +let filter = KMap.filter +let merge = KMap.merge + +let to_list (p : 'a partition) : 'a list = + KMap.fold (fun _k x l -> x :: l) p [] + +let map_filter (f : key -> 'a -> 'b option) (p : 'a partition) : 'b partition = + let opt_flatten (type a) (o : a option option) : a option = + Extlib.opt_conv None o + in + KMap.merge (fun k o _ -> opt_flatten (Extlib.opt_map (f k) o)) p KMap.empty + + +(* --- Partitioning actions --- *) + type 'a transfer_function = (key * 'a) list -> (key * 'a) list type unroll_limit = @@ -105,6 +132,8 @@ type action = exception InvalidAction +(* --- Flows --- *) + module type InputDomain = sig type t @@ -116,47 +145,6 @@ sig val eval_exp_to_int : t -> Cil_types.exp -> int end - -let empty : 'a partition = - KMap.empty - -let is_empty (p : 'a partition) : bool = - KMap.is_empty p - -let size (p : 'a partition) : int = - KMap.fold (fun _k _x n -> n + 1) p 0 - -let to_list (p : 'a partition) : 'a list = - KMap.fold (fun _k x l -> x :: l) p [] - -let find = KMap.find -let replace = KMap.add - -let merge (f : 'a option -> 'b option -> 'c option) (p1 : 'a partition) - (p2 : 'b partition) : 'c partition = - KMap.merge (fun _k o1 o2 -> f o1 o2) p1 p2 - -let iter (f : 'a -> unit) (p : 'a partition) : unit = - KMap.iter (fun _k x -> f x) p - -let iteri (f : key -> 'a -> unit) (p : 'a partition) : unit = - KMap.iter f p - - -(* Utility function on options *) -let opt_flatten (type a) (o : a option option) : a option = - Extlib.opt_conv None o - -let map_states (f : 'a -> 'a) (p : 'a partition) : 'a partition = - KMap.map f p - -let filter_keys (f : key -> bool) (p : 'a partition) : 'a partition = - KMap.filter (fun k _x -> f k) p - -let map_filter (f : key -> 'a -> 'b option) (p : 'a partition) : 'b partition = - KMap.merge (fun k o _ -> opt_flatten (Extlib.opt_map (f k) o)) p KMap.empty - - module MakeFlow (Domain : InputDomain) = struct type state = Domain.t @@ -165,7 +153,7 @@ struct let empty = [] let initial (p : 'a list) : t = - List.map (fun state -> zero_key, state) p + List.map (fun state -> Key.zero, state) p let to_list (f : t) : state list = List.map snd f @@ -314,7 +302,7 @@ struct let transfer acc (k,x) = let add = match k.ration_stamp with - (* No ration stam, just add the state to the list *) + (* No ration stamp, just add the state to the list *) | None -> fun l y -> (k,y) :: l (* There is a ration stamp, set the second part of the stamp to a unique transfer number *) | Some (s,_) -> fun l y -> diff --git a/src/plugins/value/engine/partition.mli b/src/plugins/value/engine/partition.mli index c817ca1868570625a848429fbdcec843cf0b42b2..69e115d0335c7da30a25a2f1f577fdcf3fa9b794 100644 --- a/src/plugins/value/engine/partition.mli +++ b/src/plugins/value/engine/partition.mli @@ -30,24 +30,29 @@ The key have several fields, one for each kind of partitioning. - Ration stamps: These modelize the legacy slevel. Each state is given - a ration stamp (represented by an integer) until there is no slevel left. + a ration stamp (represented by two integers) until there is no slevel + left. The first number is attributed by the store it comes from, the + second one is attributed by the last transfer. It is an option type, when there is no more ration stamp, this field is set to None; each new state will not be distinguished by this field. - - Branches: This field enumerate the last branches taken to reach this - state. The partitioning may chose how the branches are identified, but it + - Branches: This field enumerate the last junctions points passed through. + The partitioning may chose how the branches are identified, but it is a First-In-First-Out set. - Loops: This field stores the loop iterations needed to reach this state for each loop we are currently in. It is stored in reverse order - (innermost loop first) - - Static/Dynamic splits: - - Note on implementation. These partitions are implemented as map from keys - to states. We chose to have the same partition for stores, propagation and - widenings so the combination of propagation + store or propagation + - widening can be done as a map2 operation. However, this involve some tricks - to make keys be always distinguished in propagation, like giving them new - ration stamps. It may have been more natural to consider that propagations - are lists, allowing states to have the same key. + (innermost loop first) It also stores the maximum number of unrolling ; + this number varies from a state to another, as it is computed from + an expression evaluated when we enter the loop. + - Static/Dynamic splits: track the splits applied to the state as a map + from the expression of the split to the value of this expression. Since + the split creates states in which the expression evalutates to a + singleton, the values of the map are integers. + Static splits are only evaluated when the annotation is encountered + whereas dynamic splits are reevaluated regularly. + + A flow is a list of states accompanied by their key. It is used to + transfer states from one partition to another. It doesn't enforce unicity + of keys. *) type branch = int @@ -55,17 +60,32 @@ type branch = int module ExpMap = Cil_datatype.ExpStructEq.Map type key = private { - ration_stamp : (int * int) option; + ration_stamp : (int * int) option; (* store stamp / transfer stamp *) branches : branch list; - loops : (int * int) list; + loops : (int * int) list; (* current iteration / max unrolling *) static_split : Integer.t ExpMap.t; dynamic_split : Integer.t ExpMap.t; } -val pretty_key : Format.formatter -> key -> unit +type 'a partition +val empty : 'a partition +val is_empty : 'a partition -> bool +val size : 'a partition -> int +val to_list : 'a partition -> 'a list +val find : key -> 'a partition -> 'a +val replace : key -> 'a -> 'a partition -> 'a partition +val merge : (key -> 'a option -> 'b option -> 'c option) -> 'a partition -> + 'b partition -> 'c partition +val iter : (key -> 'a -> unit) -> 'a partition -> unit +val filter : (key -> 'a -> bool) -> 'a partition -> 'a partition +val map : ('a -> 'a) -> 'a partition -> 'a partition +val map_filter : (key -> 'a -> 'b option) -> 'a partition -> 'b partition + + + +(* Partitioning actions *) -type 'a partition type 'a transfer_function = (key * 'a) list -> (key * 'a) list type unroll_limit = @@ -88,24 +108,7 @@ type action = exception InvalidAction -val empty : 'a partition -val is_empty : 'a partition -> bool -val size : 'a partition -> int - -val to_list : 'a partition -> 'a list - -val find : key -> 'a partition -> 'a -val replace : key -> 'a -> 'a partition -> 'a partition - -val merge : ('a option -> 'b option -> 'c option) -> 'a partition -> - 'b partition -> 'c partition - -val iter : ('a -> unit) -> 'a partition -> unit -val iteri : (key -> 'a -> unit) -> 'a partition -> unit -val filter_keys : (key -> bool) -> 'a partition -> 'a partition -val map_states : ('a -> 'a) -> 'a partition -> 'a partition -val map_filter : (key -> 'a -> 'b option) -> 'a partition -> 'b partition - +(* Flows *) module type InputDomain = sig diff --git a/src/plugins/value/engine/partitioning_parameters.ml b/src/plugins/value/engine/partitioning_parameters.ml index 99c16a11e92966ec125457f1f0579eed66e5ba22..9111ade0ea39468a6aa486619878d0699e3ad6fb 100644 --- a/src/plugins/value/engine/partitioning_parameters.ml +++ b/src/plugins/value/engine/partitioning_parameters.ml @@ -78,28 +78,28 @@ struct let unroll stmt = let default = Partition.IntLimit min_loop_unroll in - try match get_unroll_annot stmt with - | [] -> warn_no_loop_unroll stmt; default - | [None] -> Partition.IntLimit default_loop_unroll - | [(Some t)] -> begin - (* Inlines the value of const variables in [t]. *) - let global_init vi = - try (Globals.Vars.find vi).init with Not_found -> None - in - let t = - Cil.visitCilTerm (new Logic_utils.simplify_const_lval global_init) t - in - match Logic_utils.constFoldTermToInt t with - | Some n -> Partition.IntLimit (Integer.to_int n) - | None -> Partition.ExpLimit (term_to_exp t) - end - | _ -> - warn "ignoring invalid unroll annotation"; - raise Exit - with - | Exit -> default - | Db.Properties.Interp.No_conversion -> - warn "loop unrolling parameters must be valid expressions"; + match get_unroll_annot stmt with + | [] -> warn_no_loop_unroll stmt; default + | [None] -> Partition.IntLimit default_loop_unroll + | [(Some t)] -> begin + (* Inlines the value of const variables in [t]. *) + let global_init vi = + try (Globals.Vars.find vi).init with Not_found -> None + in + let t = + Cil.visitCilTerm (new Logic_utils.simplify_const_lval global_init) t + in + match Logic_utils.constFoldTermToInt t with + | Some n -> Partition.IntLimit (Integer.to_int n) + | None -> + try + Partition.ExpLimit (term_to_exp t) + with Db.Properties.Interp.No_conversion -> + warn "loop unrolling parameters must be valid expressions"; + default + end + | _ -> + warn "ignoring invalid unroll annotation"; default let history_size = HistoryPartitioning.get () diff --git a/src/plugins/value/engine/state_partitioning.mli b/src/plugins/value/engine/state_partitioning.mli index 5f2508badf79566ea94f552e423ef7026701373d..8e330f01be9dcfc81ab515b3d77b52ce40be18b0 100644 --- a/src/plugins/value/engine/state_partitioning.mli +++ b/src/plugins/value/engine/state_partitioning.mli @@ -22,8 +22,8 @@ open Bottom.Type -type branch = Partition.branch -type loop = Cil_types.stmt +type branch = Partition.branch (* Junction branch id in the control flow *) +type loop = Cil_types.stmt (* Loop head id *) module type Kf = sig @@ -50,7 +50,6 @@ sig type tank (** An organized temporary accumulation of flows *) type widening (** Widening informations *) - (* --- Constructors --- *) val empty_store : stmt:Cil_types.stmt option -> store @@ -61,13 +60,11 @@ sig (** Build the initial tank for the entry point of a function. *) val initial_tank : state list -> tank - (* --- Pretty printing --- *) val pretty_store : Format.formatter -> store -> unit val pretty_flow : Format.formatter -> flow -> unit - (* --- Accessors --- *) val expanded : store -> state list @@ -80,12 +77,10 @@ sig val flow_size : flow -> int val tank_size : tank -> int - (* --- Reset state (for hierchical convergence) --- *) (* These functions reset the part of the state of the analysis which has been obtained after a widening. *) - val reset_store : store -> unit val reset_flow : flow -> unit val reset_tank : tank -> unit @@ -97,7 +92,6 @@ sig depend on the outer loop. *) val reset_widening_counter : widening -> unit - (* --- Partition transfer functions --- *) val enter_loop : flow -> loop -> unit @@ -105,7 +99,6 @@ sig val next_loop_iteration : flow -> loop -> unit val split_return : flow -> Cil_types.exp option -> unit - (* --- Operators --- *) (** Remove all states from the tank, leaving it empty as if it was just @@ -133,12 +126,12 @@ sig current partitioning. *) val join : (branch * flow) list -> store -> flow - (** Widen a tank. The widening object keeps track of the previous widenings to - ensure termination. The result is true when it is correct to end the - propagation here, i.e. when the current tank is only containng - states which are included into already propagated states. *) + (** Widen a flow. The widening object keeps track of the previous widenings + and previous propagated states to ensure termination. The result is true + when it is correct to end the propagation here, i.e. when the flow + object is only containng states which are included into already propagated + states. *) val widen : widening -> flow -> bool - end module type Domain = Partitioning.Domain diff --git a/src/plugins/value/engine/trace_partitioning.ml b/src/plugins/value/engine/trace_partitioning.ml index 810f61e1a7af6a44b021ca108a66e4ef440a6447..a32d411f363e59d22a26c27536ac344b84a041d4 100644 --- a/src/plugins/value/engine/trace_partitioning.ml +++ b/src/plugins/value/engine/trace_partitioning.ml @@ -124,7 +124,7 @@ struct (* Pretty printing *) let pretty_store (fmt : Format.formatter) (s : store) : unit = - Partition.iter (Domain.pretty fmt) s.store_partition + Partition.iter (fun _key state -> Domain.pretty fmt state) s.store_partition let pretty_flow (fmt : Format.formatter) (p : flow) = Flow.iter (Domain.pretty fmt) p.flow_states @@ -159,6 +159,7 @@ struct let tank_size (t : tank) : int = Partition.size t.tank_states + (* Partition transfer functions *) let loop_transfer p action = @@ -197,10 +198,10 @@ struct (* Reset state (for hierchical convergence) *) let reset_store (s : store) : unit = - let is_eternal key = + let is_eternal key _state = key.ration_stamp <> None in - s.store_partition <- Partition.filter_keys is_eternal s.store_partition + s.store_partition <- Partition.filter is_eternal s.store_partition let reset_flow (f : flow) : unit = f.flow_states <- Flow.empty @@ -215,7 +216,7 @@ struct let reset w = { w with widening_counter = max w.widening_counter (widening_period - 1) } in - w.widening_partition <- Partition.map_states reset w.widening_partition + w.widening_partition <- Partition.map reset w.widening_partition (* Operators *) @@ -226,7 +227,7 @@ struct { flow_states } let fill ~(into : tank) (f : flow) : unit = - let erase dest src = + let erase _key dest src = if Extlib.has_some src then src else dest @@ -248,7 +249,7 @@ struct match sources with | [(_,p)] -> [p.flow_states] | sources -> - (* Several branches ; partition according to the incoming branch *) + (* Several branches -> partition according to the incoming branch *) let get (b,p) = Flow.transfer_keys p.flow_states (Branch (b,history_size)) in @@ -332,7 +333,7 @@ struct previous_state = curr; widening_counter = wstate.widening_counter - 1 }; - (* Propagated state decreases, stop to propagate *) + (* Propagated state decreases, stop propagating *) if Domain.is_included curr wstate.previous_state then None (* Widening is delayed *) @@ -359,7 +360,7 @@ struct Some next end with Not_found -> - (* The key is not in the widening state; add it if slevel is not + (* The key is not in the widening state; add the state if slevel is exceeded *) if key.ration_stamp = None then update key {