diff --git a/src/plugins/value/engine/compute_functions.ml b/src/plugins/value/engine/compute_functions.ml index 13a6e265714a9c2c28b8b8977a32699613169d52..cac999676f65a89e8938639c4120039094b8d4eb 100644 --- a/src/plugins/value/engine/compute_functions.ml +++ b/src/plugins/value/engine/compute_functions.ml @@ -144,7 +144,7 @@ module Make (Abstract: Abstractions.Eva) = struct module Computer = Partitioned_dataflow.Computer - (Abstract.Dom) (PowersetDomain) (Transfer) (Init) (Logic) (Spec) + (Abstract) (PowersetDomain) (Transfer) (Init) (Logic) (Spec) let initial_state = Init.initial_state diff --git a/src/plugins/value/engine/partition.ml b/src/plugins/value/engine/partition.ml index a1825d939edc2ceb6b605f688cb95bea744aa54d..6c5c152d4d366e960953ad8997d6afada1f9f061 100644 --- a/src/plugins/value/engine/partition.ml +++ b/src/plugins/value/engine/partition.ml @@ -20,6 +20,8 @@ (* *) (**************************************************************************) +open Bottom.Type + (* --- Split monitors --- *) type split_monitor = { @@ -147,24 +149,11 @@ type action = exception InvalidAction - (* --- Flows --- *) -module type InputDomain = -sig - type t - - exception Operation_failed - - val join : t -> t -> t - val split : monitor:split_monitor -> - t -> Cil_types.exp -> (Integer.t * t) list - val eval_exp_to_int : t -> Cil_types.exp -> int -end - -module MakeFlow (Domain : InputDomain) = +module MakeFlow (Abstract: Abstractions.Eva) = struct - type state = Domain.t + type state = Abstract.Dom.t type t = (key * state) list let empty = [] @@ -183,7 +172,7 @@ struct (* Join states with the same key *) let x' = try - Domain.join (KMap.find k p) x + Abstract.Dom.join (KMap.find k p) x with Not_found -> x in KMap.add k x' p @@ -199,6 +188,113 @@ struct let union (p1 : t) (p2 : t) : t = p1 @ p2 + (* --- Evalution and split functions -------------------------------------- *) + + (* Domains transfer functions. *) + module TF = Abstract.Dom.Transfer (Abstract.Eval.Valuation) + + exception Operation_failed + + let fail ~exp message = + let source = fst exp.Cil_types.eloc in + let warn_and_raise message = + Value_parameters.warning ~source ~once:true "%s" message; + raise Operation_failed + in + Pretty_utils.ksfprintf warn_and_raise message + + let evaluate_exp_to_ival ?valuation state exp = + (* Evaluate the expression *) + let valuation, value = + match Abstract.Eval.evaluate ?valuation ~reduction:false state exp with + | `Value (valuation, value), alarms when Alarmset.is_empty alarms -> + valuation, value + | _ -> + fail ~exp "this partitioning parameter cannot be evaluated safely on \ + all states" + in + (* Get the cvalue *) + let cvalue = match Abstract.Val.get Main_values.cvalue_key with + | Some get_cvalue -> get_cvalue value + | None -> fail ~exp "partitioning is disabled when the CValue domain is \ + not active" + in + (* Extract the ival *) + let ival = + try + Cvalue.V.project_ival cvalue + with Cvalue.V.Not_based_on_null -> + fail ~exp "this partitioning parameter must evaluate to an integer" + in + valuation, ival + + exception Split_limit of Integer.t option + + let split_by_value ~monitor state exp = + let module SplitValues = Datatype.Integer.Set in + let valuation, ival = evaluate_exp_to_ival state exp in + (* Build a state with the lvalue set to a singleton *) + let build i acc = + let value = Abstract.Val.inject_int (Cil.typeOf exp) i in + let state = + Abstract.Eval.assume ~valuation state exp value >>- fun valuation -> + (* Check the reduction *) + TF.update valuation state + in + match state with + | `Value state -> + let _,new_ival = evaluate_exp_to_ival state exp in + if not (Ival.is_singleton_int new_ival) then + fail ~exp "failing to learn perfectly from split" ; + monitor.split_values <- + SplitValues.add i monitor.split_values; + (i, state) :: acc + | `Bottom -> (* This value cannot be set in the state ; the evaluation of + expr was unprecise *) + acc + in + try + (* Check the size of the ival *) + begin match Ival.cardinal ival with + | None -> raise (Split_limit None) + | Some c as count -> + if Integer.(gt c (of_int monitor.split_limit)) then + raise (Split_limit count) + end; + (* For each integer of the ival, build a new state *) + try + let result = Ival.fold_int build ival [] in + let c = SplitValues.cardinal monitor.split_values in + if c > monitor.split_limit then + raise (Split_limit (Some (Integer.of_int c))); + result + with Abstract_interp.Error_Top -> (* The ival is float *) + raise (Split_limit None) + with + | Split_limit count -> + let pp_count fmt = + match count with + | None -> () + | Some c -> Format.fprintf fmt " (%a)" (Integer.pretty ~hexa:false) c + in + fail ~exp "split on more than %d values%t prevented ; try to improve \ + the analysis precision or look at the option -eva-split-limit \ + to increase this limit." + monitor.split_limit pp_count + + + let eval_exp_to_int state exp = + let _valuation, ival = evaluate_exp_to_ival state exp in + try + Integer.to_int (Ival.project_int ival) + with + | Ival.Not_Singleton_Int -> + fail ~exp "this partitioning parameter must evaluate to a singleton" + | Failure _ -> + fail ~exp "this partitioning parameter is too big" + + (* --- Applying partitioning actions onto flows --------------------------- *) + let split_state ~monitor ~(static : bool) (exp : Cil_types.exp) (key : key) (state : state) : (key * state) list = try @@ -212,8 +308,8 @@ struct in (k,x) in - List.map update_key (Domain.split ~monitor state exp) - with Domain.Operation_failed -> + List.map update_key (split_by_value ~monitor state exp) + with Operation_failed -> [(key,state)] let split ~monitor ~(static : bool) (p : t) (exp : Cil_types.exp) = @@ -260,10 +356,10 @@ struct | Enter_loop limit_kind -> fun k x -> let limit = try match limit_kind with - | ExpLimit exp -> Domain.eval_exp_to_int x exp + | ExpLimit exp -> eval_exp_to_int x exp | IntLimit i -> i with - | Domain.Operation_failed -> 0 + | Operation_failed -> 0 in { k with loops = (0,limit) :: k.loops } diff --git a/src/plugins/value/engine/partition.mli b/src/plugins/value/engine/partition.mli index 9d2c65e8a9e655fd7a56e1db7943648adfda005c..7c8ed9e5a812f5744096ed664b27a56ab2ff7c95 100644 --- a/src/plugins/value/engine/partition.mli +++ b/src/plugins/value/engine/partition.mli @@ -126,21 +126,9 @@ exception InvalidAction (* Flows *) -module type InputDomain = +module MakeFlow (Abstract: Abstractions.Eva) : sig - type t - - exception Operation_failed - - val join : t -> t -> t - val split : monitor:split_monitor -> - t -> Cil_types.exp -> (Integer.t * t) list - val eval_exp_to_int : t -> Cil_types.exp -> int -end - -module MakeFlow (Domain : InputDomain) : -sig - type state = Domain.t + type state = Abstract.Dom.t type t val empty : t diff --git a/src/plugins/value/engine/partitioned_dataflow.ml b/src/plugins/value/engine/partitioned_dataflow.ml index 3f03b79168e15b99e716e1d7b458d3fd0afcc24c..b22c6f12e9f4e828c71980ed072f00ee33f5af0d 100644 --- a/src/plugins/value/engine/partitioned_dataflow.ml +++ b/src/plugins/value/engine/partitioned_dataflow.ml @@ -46,21 +46,25 @@ let blocks_share_locals b1 b2 = | _, _ -> false module Make_Dataflow - (Domain : Abstract_domain.External) - (States : Powerset.S with type state = Domain.t) - (Transfer : Transfer_stmt.S with type state = Domain.t) - (Init: Initialization.S with type state := Domain.t) - (Logic : Transfer_logic.S with type state = Domain.t + (Abstract : Abstractions.Eva) + (States : Powerset.S with type state = Abstract.Dom.t) + (Transfer : Transfer_stmt.S with type state = Abstract.Dom.t) + (Init: Initialization.S with type state := Abstract.Dom.t) + (Logic : Transfer_logic.S with type state = Abstract.Dom.t and type states = States.t) - (Spec: sig val treat_statement_assigns: assigns -> Domain.t -> Domain.t end) + (Spec: sig + val treat_statement_assigns: assigns -> Abstract.Dom.t -> Abstract.Dom.t + end) (AnalysisParam : sig val kf: kernel_function val call_kinstr: kinstr - val initial_state : Domain.t + val initial_state : Abstract.Dom.t end) () = struct + module Domain = Abstract.Dom + (* --- Analysis parameters --- *) let kf = AnalysisParam.kf @@ -97,7 +101,7 @@ module Make_Dataflow (* --- Abstract values storage --- *) - module Partition = Trace_partitioning.Make (Domain) (Transfer) (AnalysisParam) + module Partition = Trace_partitioning.Make (Abstract) (Transfer) (AnalysisParam) type store = Partition.store type flow = Partition.flow @@ -770,20 +774,21 @@ end module Computer - (Domain : Abstract_domain.External) - (States : Powerset.S with type state = Domain.t) - (Transfer : Transfer_stmt.S with type state = Domain.t - and type value = Domain.value) - (Init: Initialization.S with type state := Domain.state) - (Logic : Transfer_logic.S with type state = Domain.t + (Abstract : Abstractions.Eva) + (States : Powerset.S with type state = Abstract.Dom.t) + (Transfer : Transfer_stmt.S with type state = Abstract.Dom.t) + (Init: Initialization.S with type state := Abstract.Dom.t) + (Logic : Transfer_logic.S with type state = Abstract.Dom.t and type states = States.t) - (Spec: sig val treat_statement_assigns: assigns -> Domain.t -> Domain.t end) + (Spec: sig + val treat_statement_assigns: assigns -> Abstract.Dom.t -> Abstract.Dom.t + end) = struct let compute kf call_kinstr state = let module Dataflow = Make_Dataflow - (Domain) (States) (Transfer) (Init) (Logic) (Spec) + (Abstract) (States) (Transfer) (Init) (Logic) (Spec) (struct let kf = kf let call_kinstr = call_kinstr diff --git a/src/plugins/value/engine/partitioned_dataflow.mli b/src/plugins/value/engine/partitioned_dataflow.mli index e65f83e2d4f777f2173a8969d034145da641fb28..d76886b509242d78e9177225f43be81872151646 100644 --- a/src/plugins/value/engine/partitioned_dataflow.mli +++ b/src/plugins/value/engine/partitioned_dataflow.mli @@ -27,24 +27,26 @@ open Eval val signal_abort: unit -> unit module Computer - (* Abstract domain with partitioning. *) - (Domain: Abstract_domain.External) + (* Abstractions with the evaluator. *) + (Abstract: Abstractions.Eva) (* Set of states of abstract domain. *) - (States : Powerset.S with type state = Domain.t) + (States : Powerset.S with type state = Abstract.Dom.t) (* Transfer functions for statement on the abstract domain. *) - (Transfer : Transfer_stmt.S with type state = Domain.t - and type value = Domain.value) + (Transfer : Transfer_stmt.S with type state = Abstract.Dom.t + and type value = Abstract.Val.t) (* Initialization of local variables. *) - (Init: Initialization.S with type state := Domain.state) + (Init: Initialization.S with type state := Abstract.Dom.t) (* Transfer functions for the logic on the abstract domain. *) - (Logic : Transfer_logic.S with type state = Domain.t + (Logic : Transfer_logic.S with type state = Abstract.Dom.t and type states = States.t) - (Spec: sig val treat_statement_assigns: assigns -> Domain.t -> Domain.t end) + (Spec: sig + val treat_statement_assigns: assigns -> Abstract.Dom.t -> Abstract.Dom.t + end) : sig val compute: - kernel_function -> kinstr -> Domain.t -> - Domain.t list or_bottom * Value_types.cacheable + kernel_function -> kinstr -> Abstract.Dom.t -> + Abstract.Dom.t list or_bottom * Value_types.cacheable end diff --git a/src/plugins/value/engine/state_partitioning.mli b/src/plugins/value/engine/state_partitioning.mli index f143d2671be67a837951c4ddbbe4efcccb2e1b23..fdaaec4ff3138fa7dfd776b1051485c795a1581f 100644 --- a/src/plugins/value/engine/state_partitioning.mli +++ b/src/plugins/value/engine/state_partitioning.mli @@ -138,7 +138,7 @@ end module type Domain = Partitioning.Domain module type Partitioning = functor - (Domain : Abstract_domain.External) - (Transfer : Transfer_stmt.S with type state = Domain.t) + (Abstract : Abstractions.Eva) + (Transfer : Transfer_stmt.S with type state = Abstract.Dom.t) (Kf : Kf) -> - Partition with type state = Domain.t + Partition with type state = Abstract.Dom.t diff --git a/src/plugins/value/engine/trace_partitioning.ml b/src/plugins/value/engine/trace_partitioning.ml index 2411f49ce999889fe0f011682c2fec93835da2ea..9a86cf94ddc109b0351ef72d647744a601e07282 100644 --- a/src/plugins/value/engine/trace_partitioning.ml +++ b/src/plugins/value/engine/trace_partitioning.ml @@ -27,8 +27,8 @@ open Partition module Make - (Domain : Abstract_domain.External) - (Transfer : Transfer_stmt.S with type state = Domain.t) + (Abstract: Abstractions.Eva) + (Transfer : Transfer_stmt.S with type state = Abstract.Dom.t) (Kf : Kf) = struct module Parameters = Partitioning_parameters.Make (Kf) @@ -36,22 +36,14 @@ struct open Kf open Parameters - (* Add the split function to the domain *) - module Domain = - struct - exception Operation_failed = Transfer.Operation_failed - let split = Transfer.split_by_value - let eval_exp_to_int = Transfer.eval_exp_to_int + module Domain = Abstract.Dom - include Domain - - let smash = function - | [] -> [] - | v1 :: l -> [ List.fold_left join v1 l ] - end + let smash_states = function + | [] -> [] + | v1 :: l -> [ List.fold_left Domain.join v1 l ] module Index = Partitioning.Make (Domain) - module Flow = Partition.MakeFlow (Domain) + module Flow = Partition.MakeFlow (Abstract) type state = Domain.t @@ -133,7 +125,7 @@ struct Partition.to_list s.store_partition let smashed (s : store) : state or_bottom = - Bottom.of_list (Domain.smash (expanded s)) + Bottom.of_list (smash_states (expanded s)) let contents (f : flow) : state list = Flow.to_list f.flow_states @@ -179,11 +171,11 @@ struct begin match return_exp with | Some return_exp -> let states = Transfer.split_final_states kf return_exp i states in - List.flatten (List.map Domain.smash states) + List.flatten (List.map smash_states states) | None -> - Domain.smash states + smash_states states end - | Split_strategy.NoSplit -> Domain.smash states + | Split_strategy.NoSplit -> smash_states states | Split_strategy.FullSplit -> states (* Last case not possible : already transformed into SplitEqList *) | Split_strategy.SplitAuto -> assert false diff --git a/src/plugins/value/engine/transfer_stmt.ml b/src/plugins/value/engine/transfer_stmt.ml index 1f193393075333ca12341dac6de0e13acd4ea716..c81a78f0133f0029845b9030799c81ae1e9a700e 100644 --- a/src/plugins/value/engine/transfer_stmt.ml +++ b/src/plugins/value/engine/transfer_stmt.ml @@ -40,10 +40,6 @@ module type S = sig state -> (stmt * lval list * lval list * lval list * stmt ref list) list -> unit or_bottom val enter_scope: kernel_function -> varinfo list -> state -> state - exception Operation_failed - val split_by_value: monitor:Partition.split_monitor -> - state -> exp -> (Integer.t * state) list - val eval_exp_to_int: state -> exp -> int type call_result = { states: state list or_bottom; cacheable: Value_types.cacheable; @@ -875,110 +871,6 @@ module Make (Abstract: Abstractions.Eva) = struct Domain.initialize_variable lval location ~initialized init_value state in List.fold_left initialize_volatile state vars - - - (* ------------------------------------------------------------------------ *) - (* Partitioning *) - (* ------------------------------------------------------------------------ *) - - exception Operation_failed - - let fail ~exp message = - let warn_and_raise message = - Value_parameters.warning ~source:(fst exp.eloc) ~once:true "%s" message; - raise Operation_failed - in - Pretty_utils.ksfprintf warn_and_raise message - - let evaluate_exp_to_ival ?valuation state exp = - (* Evaluate the expression *) - let valuation, value = - match Eval.evaluate ?valuation ~reduction:false state exp with - | `Value (valuation, value), alarms when Alarmset.is_empty alarms -> - valuation, value - | _ -> - fail ~exp "this partitioning parameter cannot be evaluated safely on \ - all states" - in - (* Get the cvalue *) - let cvalue = match Value.get Main_values.cvalue_key with - | Some get_cvalue -> get_cvalue value - | None -> fail ~exp "partitioning is disabled when the CValue domain is \ - not active" - in - (* Extract the ival *) - let ival = - try - Cvalue.V.project_ival cvalue - with Cvalue.V.Not_based_on_null -> - fail ~exp "this partitioning parameter must evaluate to an integer" - in - valuation, ival - - exception Split_limit of Integer.t option - - let split_by_value ~monitor state exp = - let module SplitValues = Datatype.Integer.Set in - let valuation, ival = evaluate_exp_to_ival state exp in - (* Build a state with the lvalue set to a singleton *) - let build i acc = - let value = Value.inject_int (Cil.typeOf exp) i in - let state = - Eval.assume ~valuation state exp value >>- fun valuation -> - (* Check the reduction *) - TF.update valuation state - in - match state with - | `Value state -> - let _,new_ival = evaluate_exp_to_ival state exp in - if not (Ival.is_singleton_int new_ival) then - fail ~exp "failing to learn perfectly from split" ; - monitor.Partition.split_values <- - SplitValues.add i monitor.Partition.split_values; - (i, state) :: acc - | `Bottom -> (* This value cannot be set in the state ; the evaluation of - expr was unprecise *) - acc - in - try - (* Check the size of the ival *) - begin match Ival.cardinal ival with - | None -> raise (Split_limit None) - | Some c as count -> - if Integer.(gt c (of_int monitor.Partition.split_limit)) then - raise (Split_limit count) - end; - (* For each integer of the ival, build a new state *) - try - let result = Ival.fold_int build ival [] in - let c = SplitValues.cardinal monitor.Partition.split_values in - if c > monitor.Partition.split_limit then - raise (Split_limit (Some (Integer.of_int c))); - result - with Abstract_interp.Error_Top -> (* The ival is float *) - raise (Split_limit None) - with - | Split_limit count -> - let pp_count fmt = - match count with - | None -> () - | Some c -> Format.fprintf fmt " (%a)" (Integer.pretty ~hexa:false) c - in - fail ~exp "split on more than %d values%t prevented ; try to improve \ - the analysis precision or look at the option -eva-split-limit \ - to increase this limit." - monitor.Partition.split_limit pp_count - - - let eval_exp_to_int state exp = - let _valuation, ival = evaluate_exp_to_ival state exp in - try - Integer.to_int (Ival.project_int ival) - with - | Ival.Not_Singleton_Int -> - fail ~exp "this partitioning parameter must evaluate to a singleton" - | Failure _ -> - fail ~exp "this partitioning parameter is too big" end diff --git a/src/plugins/value/engine/transfer_stmt.mli b/src/plugins/value/engine/transfer_stmt.mli index 76bb4c92be00fa8da208ef09d39701066da63cd8..a2e64699a2e1578efb3311a99281ce2f326b69a3 100644 --- a/src/plugins/value/engine/transfer_stmt.mli +++ b/src/plugins/value/engine/transfer_stmt.mli @@ -51,12 +51,6 @@ module type S = sig val enter_scope: kernel_function -> varinfo list -> state -> state - exception Operation_failed - - val split_by_value: monitor:Partition.split_monitor -> - state -> exp -> (Integer.t * state) list - val eval_exp_to_int: state -> exp -> int - type call_result = { states: state list or_bottom; cacheable: Value_types.cacheable;