From e4671bfc915dc1856a160ff0995d27ee5d0fc1a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr> Date: Mon, 18 Jul 2022 15:12:35 +0200 Subject: [PATCH] [inout] Uses new Eva API instead of Db.Value. --- src/plugins/inout/cumulative_analysis.mli | 4 +- src/plugins/inout/derefs.ml | 6 +- src/plugins/inout/inputs.ml | 77 +++++++----------- src/plugins/inout/operational_inputs.ml | 95 ++++++++++------------- src/plugins/inout/outputs.ml | 53 ++++++------- src/plugins/inout/register.ml | 2 +- 6 files changed, 99 insertions(+), 138 deletions(-) diff --git a/src/plugins/inout/cumulative_analysis.mli b/src/plugins/inout/cumulative_analysis.mli index f98bd1b9848..a72397c8aa9 100644 --- a/src/plugins/inout/cumulative_analysis.mli +++ b/src/plugins/inout/cumulative_analysis.mli @@ -37,7 +37,7 @@ open Cil_types val fold_implicit_initializer: typ -> bool -val specialize_state_on_call: ?stmt:stmt -> kernel_function -> Db.Value.state +val specialize_state_on_call: ?stmt:stmt -> kernel_function -> Cvalue.Model.t (** If the given statement is a call to the given function, enrich the superposed memory state at this statement with the formal arguments of this function. This is usually more precise @@ -50,7 +50,7 @@ val specialize_state_on_call: ?stmt:stmt -> kernel_function -> Db.Value.state class virtual ['a] cumulative_visitor : object inherit Visitor.frama_c_inplace - method specialize_state_on_call: kernel_function -> Db.Value.state + method specialize_state_on_call: kernel_function -> Cvalue.Model.t (** If the current statement is a call to the given function, enrich the superposed memory state at this statement with the formal arguments of this function. Useful to do an analysis diff --git a/src/plugins/inout/derefs.ml b/src/plugins/inout/derefs.ml index f7436ea3a5c..bac26387241 100644 --- a/src/plugins/inout/derefs.ml +++ b/src/plugins/inout/derefs.ml @@ -40,10 +40,8 @@ class virtual do_it_ = object(self) begin match base with | Var _ -> () | Mem e -> - let state = - Db.Value.get_state (Kstmt (Option.get self#current_stmt)) - in - let r = !Db.Value.eval_expr state e in + let stmt = Option.get self#current_stmt in + let r = Eva.Results.(before stmt |> eval_exp e |> as_cvalue) in let loc = loc_bytes_to_loc_bits r in let size = Bit_utils.sizeof_lval lv in self#join diff --git a/src/plugins/inout/inputs.ml b/src/plugins/inout/inputs.ml index 19e2f96f130..c9787b60363 100644 --- a/src/plugins/inout/inputs.ml +++ b/src/plugins/inout/inputs.ml @@ -45,55 +45,43 @@ class virtual do_it_ = object(self) Cil.SkipChildren (* do not visit the additional lvals *) | _ -> super#vstmt_aux s + (* On assignment and addrof, only read the lvalue address. *) + method private read_address lv = + let request = Eva.Results.before_kinstr self#current_kinstr in + let deps = Eva.Results.address_deps lv request in + self#join deps + method! vlval lv = - let state = Db.Value.get_state self#current_kinstr in - let deps, bits_loc, _exact = - !Db.Value.lval_to_zone_with_deps_state - state ~deps:(Some Zone.bottom) ~for_writing:false lv - in + let request = Eva.Results.before_kinstr self#current_kinstr in + let deps = Eva.Results.lval_deps lv request in self#join deps; - self#join bits_loc; Cil.SkipChildren - method private do_assign lv = - let deps,_loc = - !Db.Value.lval_to_loc_with_deps (* loc ignored *) - ~deps:Zone.bottom - self#current_kinstr - lv - in - (* Format.printf "do_assign deps:%a@." - Zone.pretty deps; *) + method private do_arg_calls stmt f args = + let deps = Eva.Results.(before stmt |> expr_deps f) in self#join deps; - - method private do_arg_calls f args = - let state = Db.Value.get_state self#current_kinstr in - (if Cvalue.Model.is_top state then - self#join Zone.top - else - let deps_callees, callees = - !Db.Value.expr_to_kernel_function_state - ~deps:(Some Zone.bottom) state f - in - self#join deps_callees; - Kernel_function.Hptset.iter - (fun kf -> self#join (self#compute_kf kf)) callees; - ); - List.iter - (fun exp -> ignore (visitFramacExpr (self:>frama_c_visitor) exp)) args + let () = + match Eva.Results.(before stmt |> eval_callee f) with + | Ok callees -> + List.iter (fun kf -> self#join (self#compute_kf kf)) callees + | Error (Top | DisabledDomain) -> self#join Zone.top; + | Error Bottom -> () + in + List.iter (fun e -> ignore (visitFramacExpr (self:>frama_c_visitor) e)) args; method! vinst i = - if Db.Value.is_reachable (Db.Value.get_state self#current_kinstr) then begin + let stmt = Option.get self#current_stmt in + if Eva.Results.is_reachable stmt then begin match i with | Set (lv,exp,_) -> - self#do_assign lv; + self#read_address lv; ignore (visitFramacExpr (self:>frama_c_visitor) exp); Cil.SkipChildren | Local_init(v, AssignInit i,_) -> let rec aux lv = function | SingleInit e -> - self#do_assign lv; + self#read_address lv; ignore (visitFramacExpr (self:>frama_c_visitor) e) | CompoundInit (ct,initl) -> (* No need to consider implicit zero-initializers, for which @@ -109,15 +97,15 @@ class virtual do_it_ = object(self) Cil.SkipChildren | Call (lv_opt,exp,args,_) -> - Option.iter self#do_assign lv_opt; - self#do_arg_calls exp args; + Option.iter self#read_address lv_opt; + self#do_arg_calls stmt exp args; Cil.SkipChildren | Local_init(v, ConsInit(f, args, Plain_func), _) -> - self#do_assign (Cil.var v); - self#do_arg_calls (Cil.evar f) args; + self#read_address (Cil.var v); + self#do_arg_calls stmt (Cil.evar f) args; Cil.SkipChildren | Local_init(v, ConsInit(f, args, Constructor), _) -> - self#do_arg_calls (Cil.evar f) (Cil.mkAddrOfVi v :: args); + self#do_arg_calls stmt (Cil.evar f) (Cil.mkAddrOfVi v :: args); Cil.SkipChildren | Skip _ | Asm _ | Code_annot _ -> Cil.DoChildren end @@ -126,12 +114,7 @@ class virtual do_it_ = object(self) method! vexpr exp = match exp.enode with | AddrOf lv | StartOf lv -> - let deps,_loc = - !Db.Value.lval_to_loc_with_deps (* loc ignored *) - ~deps:Zone.bottom - self#current_kinstr lv - in - self#join deps; + self#read_address lv; Cil.SkipChildren | SizeOfE _ | AlignOfE _ | SizeOf _ | AlignOf _ -> (* we're not evaluating an expression here: there's no input. *) @@ -140,9 +123,9 @@ class virtual do_it_ = object(self) method compute_funspec kf = let state = self#specialize_state_on_call kf in - let behaviors = !Db.Value.valid_behaviors kf state in + let behaviors = Eva.Logic_inout.valid_behaviors kf state in let assigns = Ast_info.merge_assigns behaviors in - !Db.Value.assigns_inputs_to_zone state assigns + Eva.Logic_inout.assigns_inputs_to_zone state assigns method clean_kf_result (_ : kernel_function) (r: Locations.Zone.t) = r end diff --git a/src/plugins/inout/operational_inputs.ml b/src/plugins/inout/operational_inputs.ml index bbb589d3f23..4e5041fc2fd 100644 --- a/src/plugins/inout/operational_inputs.ml +++ b/src/plugins/inout/operational_inputs.ml @@ -191,7 +191,7 @@ let eval_assigns kf state assigns = } let compute_using_prototype_state state kf = - let behaviors = !Db.Value.valid_behaviors kf state in + let behaviors = Eva.Logic_inout.valid_behaviors kf state in let assigns = Ast_info.merge_assigns behaviors in eval_assigns kf state assigns @@ -230,8 +230,9 @@ module CallwiseResults = module Computer(Fenv:Dataflows.FUNCTION_ENV)(X:sig val _version: string (* Debug: Callwise or functionwise *) val _kf: kernel_function (* Debug: Function being analyzed *) - val kf_pre_state: Db.Value.state (* Memory pre-state of the function. *) - val stmt_state: stmt -> Db.Value.state (* Memory state at the given stmt *) + val kf_pre_state: Cvalue.Model.t (* Memory pre-state of the function. *) + val stmt_state: stmt -> Cvalue.Model.t (* Memory state at the given stmt *) + val stmt_request: stmt -> Eva.Results.request (* Request at the given stmt *) val at_call: stmt -> kernel_function -> Inout_type.t (* Results of the analysis for the given call. Must not contain locals or formals *) end) = struct @@ -289,8 +290,8 @@ module Computer(Fenv:Dataflows.FUNCTION_ENV)(X:sig (* Transfer function on expression. *) let transfer_exp s exp data = - let state = X.stmt_state s in - let inputs = !Db.From.find_deps_no_transitivity_state state exp in + let request = X.stmt_request s in + let inputs = Eva.Results.expr_deps exp request in let new_inputs = Zone.diff inputs data.under_outputs_d in store_non_terminating_inputs new_inputs; {data with over_inputs_d = Zone.join data.over_inputs_d new_inputs} @@ -298,12 +299,13 @@ module Computer(Fenv:Dataflows.FUNCTION_ENV)(X:sig (* Initialized const variables should be included as outputs of the function, so [for_writing] must be false for local initializations. It should be true for all other instructions. *) - let add_out ~for_writing state lv deps data = - let deps, new_outs, exact = - !Db.Value.lval_to_zone_with_deps_state state - ~deps:(Some deps) ~for_writing lv - in + let add_out ~for_writing request lv deps data = + let lv_address = Eva.Results.eval_address ~for_writing lv request in + let new_outs = Eva.Results.as_zone lv_address in + let exact = Eva.Results.is_singleton lv_address in store_non_terminating_outputs new_outs; + let lv_deps = Eva.Results.address_deps lv request in + let deps = Zone.join lv_deps deps in let new_inputs = Zone.diff deps data.under_outputs_d in store_non_terminating_inputs new_inputs; let new_sure_outs = @@ -318,34 +320,21 @@ module Computer(Fenv:Dataflows.FUNCTION_ENV)(X:sig over_outputs_d = Zone.join data.over_outputs_d new_outs } let transfer_call ~for_writing s dest f args _loc data = - let state = X.stmt_state s in - let f_inputs, called = - !Db.Value.expr_to_kernel_function_state - ~deps:(Some Zone.bottom) - state - f - in - let acc_f_arg_inputs = - (* add the inputs of [argl] to the inputs of the - function expression *) - List.fold_right - (fun arg inputs -> - let arg_inputs = !Db.From.find_deps_no_transitivity_state - state arg - in Zone.join inputs arg_inputs) - args - f_inputs - in + let request = X.stmt_request s in + (* Join the inputs of [args] and of the function expression. *) + let eval_deps acc e = Zone.join acc (Eva.Results.expr_deps e request) in + let f_args_inputs = List.fold_left eval_deps Zone.bottom (f :: args) in let data = catenate data - { over_inputs_d = acc_f_arg_inputs ; + { over_inputs_d = f_args_inputs ; under_outputs_d = Zone.bottom; over_outputs_d = Zone.bottom; } in + let called = Eva.Results.(eval_callee f request |> default []) in let for_functions = - Kernel_function.Hptset.fold - (fun kf acc -> + List.fold_left + (fun acc kf -> let res = X.at_call s kf in store_non_terminating_subcall data.over_outputs_d res; let for_function = { @@ -354,15 +343,15 @@ module Computer(Fenv:Dataflows.FUNCTION_ENV)(X:sig over_outputs_d = res.over_outputs_if_termination; } in join for_function acc) - called bottom + called in let result = catenate data for_functions in let result = (* Treatment for the possible assignment of the call result *) (match dest with | None -> result - | Some lv -> add_out ~for_writing state lv Zone.bottom result) + | Some lv -> add_out ~for_writing request lv Zone.bottom result) in result (* Propagate all zones in predicates for the given statement, only in the case @@ -395,18 +384,16 @@ module Computer(Fenv:Dataflows.FUNCTION_ENV)(X:sig let transfer_instr stmt (i: instr) (data: t) = match i with | Set (lv, exp, _) -> - let state = X.stmt_state stmt in - let e_inputs = - !Db.From.find_deps_no_transitivity_state state exp - in - add_out ~for_writing:true state lv e_inputs data + let request = X.stmt_request stmt in + let e_inputs = Eva.Results.expr_deps exp request in + add_out ~for_writing:true request lv e_inputs data | Local_init (v, AssignInit i, _) -> - let state = X.stmt_state stmt in + let request = X.stmt_request stmt in let rec aux lv i acc = match i with | SingleInit e -> - let e_inputs = !Db.From.find_deps_no_transitivity_state state e in - add_out ~for_writing:false state lv e_inputs acc + let e_inputs = Eva.Results.expr_deps e request in + add_out ~for_writing:false request lv e_inputs acc | CompoundInit(ct, initl) -> (* Avoid folding implicit zero-initializer of large arrays. *) let implicit = Cumulative_analysis.fold_implicit_initializer ct in @@ -417,7 +404,7 @@ module Computer(Fenv:Dataflows.FUNCTION_ENV)(X:sig zone of the array as outputs. It is exactly the written zone for arrays of scalar elements. Nothing is read by zero-initializers, so the inputs are empty. *) - add_out ~for_writing:false state lv Zone.bottom acc + add_out ~for_writing:false request lv Zone.bottom acc in aux (Cil.var v) i data | Call (lvaloption,funcexp,argl,loc) -> @@ -433,8 +420,8 @@ module Computer(Fenv:Dataflows.FUNCTION_ENV)(X:sig the condition. In this case, we just make sure that dead edges get bottom, instead of the input state. *) let transfer_guard stmt e t = - let state = X.stmt_state stmt in - let v_e = !Db.Value.eval_expr state e in + let request = X.stmt_request stmt in + let v_e = Eva.Results.(eval_exp e request |> as_cvalue) in let t1 = Cil.unrollType (Cil.typeOf e) in let do_then, do_else = if Cil.isIntegralType t1 || Cil.isPointerType t1 @@ -473,7 +460,7 @@ module Computer(Fenv:Dataflows.FUNCTION_ENV)(X:sig ;; let transfer_stmt s data = - if Db.Value.is_reachable (X.stmt_state s) + if Cvalue.Model.is_reachable (X.stmt_state s) then begin transfer_annotations s; transfer_stmt s data @@ -663,20 +650,15 @@ module Callwise = struct (* Returns the [kf] pre-state with respect to the single [call_stack]. *) let kf_pre_state = - match Db.Value.get_initial_state_callstack kf with - | None -> - Cvalue.Model.bottom - | Some cs -> - begin - match Value_types.Callstack.Hashtbl.find_opt cs call_stack with - | None -> Cvalue.Model.bottom - | Some state -> state - end + Eva.Results.(at_start_of kf |> in_callstack call_stack |> + get_cvalue_model) let stmt_state stmt = try Cil_datatype.Stmt.Hashtbl.find states stmt with Not_found -> Cvalue.Model.bottom + let stmt_request stmt = Eva.Results.in_cvalue_state (stmt_state stmt) + let at_call stmt kf = let _cur_kf, table = List.hd !call_inout_stack in try @@ -739,8 +721,9 @@ module FunctionWise = struct let module Computer = Computer(Fenv)(struct let _version = "functionwise" let _kf = kf - let kf_pre_state = Db.Value.get_initial_state kf - let stmt_state s = Db.Value.get_stmt_state s + let kf_pre_state = Eva.Results.(at_start_of kf |> get_cvalue_model) + let stmt_state s = Eva.Results.(before s |> get_cvalue_model) + let stmt_request s = Eva.Results.before s let at_call stmt kf = get_external_aux ~stmt kf end) in Stack.iter (fun g -> if kf == g then raise Exit) call_stack; diff --git a/src/plugins/inout/outputs.ml b/src/plugins/inout/outputs.ml index 8eb9a5f31d3..4d06b9f1a51 100644 --- a/src/plugins/inout/outputs.ml +++ b/src/plugins/inout/outputs.ml @@ -55,35 +55,32 @@ class virtual do_it_ = object(self) self#join bits_loc method! vinst i = - if Db.Value.is_reachable (Db.Value.noassert_get_state self#current_kinstr) + let stmt = Option.get self#current_stmt in + if Eva.Results.is_reachable stmt then (* noassert needed for Eval.memoize. Not really satisfactory *) begin + let assign_lval lval = + let for_writing = not (Cil.is_mutable_or_initialized lval) in + self#do_assign ~for_writing lval + in match i with | Set (lv,_,_) -> - let for_writing = not (Cil.is_mutable_or_initialized lv) in - self#do_assign ~for_writing lv - | Call (lv_opt,exp,_,_) -> - (match lv_opt with None -> () - | Some lv -> - let for_writing = - not (Cil.is_mutable_or_initialized lv) - in - self#do_assign ~for_writing lv); - let state = Db.Value.get_state self#current_kinstr in - if Cvalue.Model.is_top state then - self#join Zone.top - else - let _, callees = - !Db.Value.expr_to_kernel_function_state ~deps:None state exp in - Kernel_function.Hptset.iter - (fun kf -> - let { Inout_type.over_outputs = z } = - Operational_inputs.get_external_aux - ?stmt:self#current_stmt kf - in - self#join z - ) callees + assign_lval lv + | Call (lv_opt, exp, _, _) -> + begin + Option.iter assign_lval lv_opt; + let callees = Eva.Results.(before stmt |> eval_callee exp) in + match callees with + | Ok callees -> + let join_outputs kf = + let inout = Operational_inputs.get_external_aux ~stmt kf in + self#join inout.over_outputs + in + List.iter join_outputs callees + | Error (Top | DisabledDomain) -> self#join Zone.top + | Error Bottom -> () + end | Local_init (v, AssignInit i, _) -> let rec aux lv = function | SingleInit _ -> self#do_assign ~for_writing:false lv @@ -102,8 +99,8 @@ class virtual do_it_ = object(self) in aux (Cil.var v) i | Local_init (v, ConsInit(f, _, _),_) -> - let state = Db.Value.get_state self#current_kinstr in - if Cvalue.Model.is_top state then self#join Zone.top + if Cvalue.Model.is_top Eva.Results.(before stmt |> get_cvalue_model) + then self#join Zone.top else begin let { Inout_type.over_outputs = z } = Operational_inputs.get_external_aux ?stmt:self#current_stmt @@ -125,9 +122,9 @@ class virtual do_it_ = object(self) method compute_funspec kf = let state = self#specialize_state_on_call kf in - let behaviors = !Db.Value.valid_behaviors kf state in + let behaviors = Eva.Logic_inout.valid_behaviors kf state in let assigns = Ast_info.merge_assigns behaviors in - !Db.Value.assigns_outputs_to_zone state ~result:None assigns + Eva.Logic_inout.assigns_outputs_to_zone state ~result:None assigns end module Analysis = Cumulative_analysis.Make( diff --git a/src/plugins/inout/register.ml b/src/plugins/inout/register.ml index 3e8e1949b55..f65fd3ad50b 100644 --- a/src/plugins/inout/register.ml +++ b/src/plugins/inout/register.ml @@ -52,7 +52,7 @@ let main () = Eva.Analysis.compute (); Callgraph.Uses.iter_in_rev_order (fun kf -> - if Kernel_function.is_definition kf && !Db.Value.is_called kf + if Kernel_function.is_definition kf && Eva.Results.is_called kf then begin if forceout then Inout_parameters.result "%a" Outputs.pretty_internal kf ; -- GitLab