From 8aa4852e1bd2ff18515515244ad285b2e77a81c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr> Date: Tue, 24 Jan 2023 14:43:14 +0100 Subject: [PATCH] [Eva] Optimizes and moves function [accept_base] from Callgraph to Eva. [accept_base] is used by the Inout and From plugins, and its implementation in callgraph is very slow on programs with many callsites. --- src/plugins/callgraph/uses.ml | 32 --------------------- src/plugins/callgraph/uses.mli | 11 -------- src/plugins/eva/Eva.mli | 8 ++++++ src/plugins/eva/gui/register_gui.ml | 2 +- src/plugins/eva/legacy/logic_inout.ml | 37 +++++++++++++++++++++++++ src/plugins/eva/legacy/logic_inout.mli | 8 ++++++ src/plugins/eva/utils/eva_dynamic.ml | 5 ---- src/plugins/eva/utils/eva_dynamic.mli | 4 --- src/plugins/eva/utils/private.ml | 1 + src/plugins/eva/utils/private.mli | 1 + src/plugins/from/functionwise.ml | 7 +++-- src/plugins/inout/inputs.ml | 4 +-- src/plugins/inout/operational_inputs.ml | 2 +- src/plugins/inout/outputs.ml | 4 +-- 14 files changed, 66 insertions(+), 60 deletions(-) diff --git a/src/plugins/callgraph/uses.ml b/src/plugins/callgraph/uses.ml index a4b6931b84b..2de6a275188 100644 --- a/src/plugins/callgraph/uses.ml +++ b/src/plugins/callgraph/uses.ml @@ -101,38 +101,6 @@ let iter_on_aux iter_dir f kf = let iter_on_callers = iter_on_aux Cg.G.iter_pred let iter_on_callees = iter_on_aux Cg.G.iter_succ -let is_local_or_formal_of_caller v kf = - try - iter_on_callers - (fun caller -> - if Base.is_formal_or_local v (Kernel_function.get_definition caller) - then raise Exit) - kf; - false - with Exit -> - true - -let accept_base ~with_formals ~with_locals kf v = - let open Cil_types in - Base.is_global v - || - (match with_formals, with_locals, kf.fundec with - | false, false, _ | false, _, Declaration _ -> false - | true, false, Definition (fundec,_) -> Base.is_formal v fundec - | false, true, Definition (fundec, _) -> Base.is_local v fundec - | true, true, Definition (fundec, _) -> Base.is_formal_or_local v fundec - | true , _, Declaration (_, vd, _, _) -> Base.is_formal_of_prototype v vd) - || is_local_or_formal_of_caller v kf - -let _accept_base = - Dynamic.register - ~comment:"Returns [true] if the given base is a global, \ - or a formal or local of either [kf] or one of its callers" - ~plugin:Options.name - "accept_base" - Datatype.(func2 Kernel_function.ty Base.ty bool) - (fun kf b -> accept_base ~with_formals:true ~with_locals:true kf b) - let nb_calls () = let g = Cg.get () in (* [g] contains bidirectional edges (from caller to callee and diff --git a/src/plugins/callgraph/uses.mli b/src/plugins/callgraph/uses.mli index 4e9df9a0ad0..286d3a6c8eb 100644 --- a/src/plugins/callgraph/uses.mli +++ b/src/plugins/callgraph/uses.mli @@ -38,17 +38,6 @@ val iter_on_callees : (Kernel_function.t -> unit) -> Kernel_function.t -> unit (** Iterate over all the callees of a given function in a (reverse) depth-first way. Do nothing if the function is not in the callgraph. *) -val accept_base : - with_formals:bool -> - with_locals:bool -> - Kernel_function.t -> - Base.t -> - bool -(** [accept_base formals locals kf b] returns [true] if and only if [b] is - - a global - - a formal or local of one of the callers of [kf] - - a formal or local of [kf] and the corresponding argument is [true]. *) - val nb_calls: unit -> int (** @return the number of function calls in the whole callgraph. It is not (necessarily) equal to the number of graph edges (depending on the diff --git a/src/plugins/eva/Eva.mli b/src/plugins/eva/Eva.mli index d36e2198c59..62d89a87c61 100644 --- a/src/plugins/eva/Eva.mli +++ b/src/plugins/eva/Eva.mli @@ -708,6 +708,14 @@ module Logic_inout: sig val verify_assigns: Cil_types.kernel_function -> pre:Cvalue.Model.t -> Function_Froms.froms -> unit + + (** [accept_base ~formals ~locals kf b] returns [true] if and only if [b] is: + - a global + - a formal or local of one of the callers of [kf] + - a formal or local of [kf] and the corresponding argument is [true]. *) + val accept_base: + formals:bool -> locals:bool -> Kernel_function.t -> Base.t -> bool + end module Eva_results: sig diff --git a/src/plugins/eva/gui/register_gui.ml b/src/plugins/eva/gui/register_gui.ml index e91019d1c69..c1fe3c7bf7b 100644 --- a/src/plugins/eva/gui/register_gui.ml +++ b/src/plugins/eva/gui/register_gui.ml @@ -228,7 +228,7 @@ let gui_compute_values (main_ui:main_ui) = let cleaned_outputs kf s = let outs = Db.Outputs.kinstr (Kstmt s) in - let accept = Eva_dynamic.Callgraph.accept_base kf in + let accept = Logic_inout.accept_base ~formals:true ~locals:true kf in let filter = Locations.Zone.filter_base accept in Option.map filter outs diff --git a/src/plugins/eva/legacy/logic_inout.ml b/src/plugins/eva/legacy/logic_inout.ml index 34ed60bad55..14a5f09846d 100644 --- a/src/plugins/eva/legacy/logic_inout.ml +++ b/src/plugins/eva/legacy/logic_inout.ml @@ -290,3 +290,40 @@ let verify_assigns kf ~pre froms = in let ab = Active_behaviors.create eval_predicate funspec in check_fct_assigns kf ab ~pre_state:pre froms + +(* -------------------------------------------------------------------------- *) +(* --- Utilitary function for Inout and From plugins --- *) +(* -------------------------------------------------------------------------- *) + +let compute_all_callers kf = + let rec add_callers kf acc = + List.fold_left add_kf acc (Function_calls.callers kf) + and add_kf acc kf = + if Kernel_function.Hptset.mem kf acc + then acc + else add_callers kf (Kernel_function.Hptset.add kf acc) + in + add_callers kf (Kernel_function.Hptset.empty) + +let is_local_or_formal_of_caller callers base = + match Kernel_function.find_defining_kf (Base.to_varinfo base) with + | Some kf -> Kernel_function.Hptset.mem kf callers + | None | exception Base.Not_a_C_variable -> false + +let is_formal kf base = + match kf.fundec with + | Definition (fundec, _) -> Base.is_formal base fundec + | Declaration (_, vi, _, _) -> Base.is_formal_of_prototype base vi + +let is_local kf base = + match kf.fundec with + | Definition (fundec, _) -> Base.is_local base fundec + | Declaration _ -> false + +let accept_base ~formals ~locals kf = + let all_callers = compute_all_callers kf in + fun base -> + Base.is_global base + || (formals && is_formal kf base) + || (locals && is_local kf base) + || is_local_or_formal_of_caller all_callers base diff --git a/src/plugins/eva/legacy/logic_inout.mli b/src/plugins/eva/legacy/logic_inout.mli index d79e25f77ba..fa75127301f 100644 --- a/src/plugins/eva/legacy/logic_inout.mli +++ b/src/plugins/eva/legacy/logic_inout.mli @@ -73,4 +73,12 @@ val assigns_tlval_to_zones: val verify_assigns: Cil_types.kernel_function -> pre:Cvalue.Model.t -> Function_Froms.froms -> unit + +(** [accept_base ~formals ~locals kf b] returns [true] if and only if [b] is: + - a global + - a formal or local of one of the callers of [kf] + - a formal or local of [kf] and the corresponding argument is [true]. *) +val accept_base: + formals:bool -> locals:bool -> Kernel_function.t -> Base.t -> bool + [@@@ api_end] diff --git a/src/plugins/eva/utils/eva_dynamic.ml b/src/plugins/eva/utils/eva_dynamic.ml index 597122b7252..f3089bf4123 100644 --- a/src/plugins/eva/utils/eva_dynamic.ml +++ b/src/plugins/eva/utils/eva_dynamic.ml @@ -35,11 +35,6 @@ module Callgraph = struct else let typ = Datatype.(func (func Kernel_function.ty unit) unit) in get ~plugin "iter_in_rev_order" typ ~fallback f - - let accept_base kf v = - let fallback _ _ = true in - let typ = Datatype.(func2 Kernel_function.ty Base.ty bool) in - get ~plugin "accept_base" typ ~fallback kf v end module Scope = struct diff --git a/src/plugins/eva/utils/eva_dynamic.mli b/src/plugins/eva/utils/eva_dynamic.mli index 89b11aea252..0541972c987 100644 --- a/src/plugins/eva/utils/eva_dynamic.mli +++ b/src/plugins/eva/utils/eva_dynamic.mli @@ -27,10 +27,6 @@ module Callgraph: sig callees to callers. If callgraph is missing or if the number of callsites is too big, the order is unspecified. *) val iter_in_rev_order: (Kernel_function.t -> unit) -> unit - - (** Returns [true] if [base] is a global, or a formal or local of either [kf] - or one of its callers. If callgraph is missing, always returns true. *) - val accept_base: Kernel_function.t -> Base.t -> bool end module Scope: sig diff --git a/src/plugins/eva/utils/private.ml b/src/plugins/eva/utils/private.ml index c5a16ae1195..5040c5d25c2 100644 --- a/src/plugins/eva/utils/private.ml +++ b/src/plugins/eva/utils/private.ml @@ -36,6 +36,7 @@ module Eval_op = Eval_op module Eval_terms = Eval_terms module Eval_typ = Eval_typ module Function_calls = Function_calls +module Logic_inout = Logic_inout module Main_locations = Main_locations module Main_values = Main_values module Parameters = Parameters diff --git a/src/plugins/eva/utils/private.mli b/src/plugins/eva/utils/private.mli index d4c64ac5ad0..23024a48781 100644 --- a/src/plugins/eva/utils/private.mli +++ b/src/plugins/eva/utils/private.mli @@ -40,6 +40,7 @@ module Eval_op = Eval_op module Eval_terms = Eval_terms module Eval_typ = Eval_typ module Function_calls = Function_calls +module Logic_inout = Logic_inout module Main_locations = Main_locations module Main_values = Main_values module Parameters = Parameters diff --git a/src/plugins/from/functionwise.ml b/src/plugins/from/functionwise.ml index ee80d0cc402..2f729743dbb 100644 --- a/src/plugins/from/functionwise.ml +++ b/src/plugins/from/functionwise.ml @@ -50,14 +50,17 @@ module To_Use = struct let get_from_call kf _ = memo kf let keep_base kf = (* Eta-expansion required *) - Callgraph.Uses.accept_base ~with_formals:false ~with_locals:false kf + Eva.Logic_inout.accept_base ~formals:false ~locals:false kf let cleanup kf froms = if Function_Froms.Memory.is_bottom froms.Function_Froms.deps_table then froms else + let accept_base = + Eva.Logic_inout.accept_base ~formals:true ~locals:false kf + in let f b intervs = - if Callgraph.Uses.accept_base ~with_formals:true ~with_locals:false kf b + if accept_base b then Zone.inject b intervs else Zone.bottom in diff --git a/src/plugins/inout/inputs.ml b/src/plugins/inout/inputs.ml index c9787b60363..247f1b4f0fd 100644 --- a/src/plugins/inout/inputs.ml +++ b/src/plugins/inout/inputs.ml @@ -155,12 +155,12 @@ let get_external = Externals.memo (fun kf -> Zone.filter_base - (Callgraph.Uses.accept_base ~with_formals:false ~with_locals:false kf) + (Eva.Logic_inout.accept_base ~formals:false ~locals:false kf) (get_internal kf)) let get_with_formals kf = Zone.filter_base - (Callgraph.Uses.accept_base ~with_formals:true ~with_locals:false kf) + (Eva.Logic_inout.accept_base ~formals:true ~locals:false kf) (get_internal kf) let compute_external kf = ignore (get_external kf) diff --git a/src/plugins/inout/operational_inputs.ml b/src/plugins/inout/operational_inputs.ml index b7ce487c065..a2fc93daf04 100644 --- a/src/plugins/inout/operational_inputs.ml +++ b/src/plugins/inout/operational_inputs.ml @@ -94,7 +94,7 @@ let join_and_is_included smaller larger = let externalize_zone ~with_formals kf = Zone.filter_base - (Callgraph.Uses.accept_base ~with_formals ~with_locals:false kf) + (Eva.Logic_inout.accept_base ~formals:with_formals ~locals:false kf) (* This code evaluates an assigns, computing in particular a sound approximation of sure outputs. For an assigns [locs_out \from locs_from], the process diff --git a/src/plugins/inout/outputs.ml b/src/plugins/inout/outputs.ml index 4d06b9f1a51..6cbb7eb73b0 100644 --- a/src/plugins/inout/outputs.ml +++ b/src/plugins/inout/outputs.ml @@ -117,7 +117,7 @@ class virtual do_it_ = object(self) method clean_kf_result kf r = Zone.filter_base - (Callgraph.Uses.accept_base ~with_formals:true ~with_locals:true kf) + (Eva.Logic_inout.accept_base ~formals:true ~locals:true kf) r method compute_funspec kf = @@ -141,7 +141,7 @@ let get_internal = Analysis.kernel_function let externalize kf x = Zone.filter_base - (Callgraph.Uses.accept_base ~with_formals:false ~with_locals:false kf) + (Eva.Logic_inout.accept_base ~formals:false ~locals:false kf) x module Externals = -- GitLab