From cd760e389252a82880910f3a8a4cd8c3f6b81f7b Mon Sep 17 00:00:00 2001 From: Valentin Perrelle <valentin.perrelle@cea.fr> Date: Sat, 21 Dec 2024 00:53:35 +0100 Subject: [PATCH] [Eva] build the engine modules recursively - the useful modules definitions are moved to Engine_sig - the engine contains the modules circularly dependent on the engine - every module circularly dependent on the engine receive the Engine as a functor parameter - Analysis instanciate the Engine recursively --- src/plugins/eva/engine/analysis.ml | 39 ++++++----- src/plugins/eva/engine/analysis.mli | 19 ++---- src/plugins/eva/engine/compute_functions.ml | 68 +++++++++++--------- src/plugins/eva/engine/compute_functions.mli | 15 ++--- src/plugins/eva/engine/engine_sig.ml | 65 +++++++++++++++++++ src/plugins/eva/engine/transfer_stmt.ml | 29 ++------- src/plugins/eva/engine/transfer_stmt.mli | 16 +---- 7 files changed, 138 insertions(+), 113 deletions(-) create mode 100644 src/plugins/eva/engine/engine_sig.ml diff --git a/src/plugins/eva/engine/analysis.ml b/src/plugins/eva/engine/analysis.ml index c3b9861d3fd..c55cb183796 100644 --- a/src/plugins/eva/engine/analysis.ml +++ b/src/plugins/eva/engine/analysis.ml @@ -78,28 +78,27 @@ module type Results = sig end module type S = sig - include Abstractions.S_with_evaluation + include Engine_sig.S include Results with type state := Dom.state and type value := Val.t and type location := Loc.location end -module type Analyzer = sig - include S - val compute_from_entry_point : kernel_function -> lib_entry:bool -> unit - (* val compute_from_init_state: kernel_function -> Dom.t -> unit *) -end - - module Make (Abstract: Abstractions.S) = struct - module Abstract = struct + module rec Engine : Engine_sig.S + with module Ctx = Abstract.Ctx + and module Val = Abstract.Val + and module Loc = Abstract.Loc + and module Dom = Abstract.Dom = + struct include Abstract module Eval = Evaluation.Make (Ctx) (Val) (Loc) (Dom) + module Compute = C end + and C : Engine_sig.Compute = Compute_functions.Make (Engine) - include Abstract - include Compute_functions.Make (Abstract) + include Engine let find stmt f = if is_computed () @@ -115,15 +114,15 @@ module Make (Abstract: Abstractions.S) = struct find stmt (Dom.Store.get_stmt_state ~after :> stmt -> Dom.t or_top_bottom) let get_stmt_state_by_callstack ?selection ~after stmt = - find stmt (Abstract.Dom.Store.get_stmt_state_by_callstack ?selection ~after) + find stmt (Dom.Store.get_stmt_state_by_callstack ?selection ~after) let get_global_state () = - (Abstract.Dom.Store.get_global_state () :> Dom.t or_top_bottom) + (Dom.Store.get_global_state () :> Dom.t or_top_bottom) let get_initial_state kf = if is_computed () then if Function_calls.is_called kf - then (Abstract.Dom.Store.get_initial_state kf :> Dom.t or_top_bottom) + then (Dom.Store.get_initial_state kf :> Dom.t or_top_bottom) else `Bottom else `Top @@ -154,15 +153,15 @@ module Make (Abstract: Abstractions.S) = struct end - let default = Abstractions.Config.of_list [Cvalue_domain.registered, None] -module Default : Analyzer = Make (val Abstractions.make default) +module DefaultAbstractions = (val Abstractions.make default) +module Default : S = Make (DefaultAbstractions) (* Reference to the current configuration (built by Abstractions.configure from the parameters of Eva regarding the abstractions used in the analysis) and the current Analyzer module. *) -let ref_analyzer = ref (default, (module Default : Analyzer)) +let ref_analyzer = ref (default, (module Default : S)) (* Returns the current Analyzer module. *) let current_analyzer () = (module (val (snd !ref_analyzer)): S) @@ -176,7 +175,7 @@ let register_hook = Analyzer_Hook.extend (* Sets the current Analyzer module for a given configuration. Calls the hooks above. *) -let set_current_analyzer config (analyzer: (module Analyzer)) = +let set_current_analyzer config (analyzer: (module S)) = Analyzer_Hook.apply (module (val analyzer): S); ref_analyzer := (config, analyzer) @@ -184,7 +183,7 @@ let set_current_analyzer config (analyzer: (module Analyzer)) = and sets it as the current analyzer. *) let make_analyzer config = let analyzer = - if Abstractions.Config.(equal config default) then (module Default : Analyzer) + if Abstractions.Config.(equal config default) then (module Default : S) else let module Abstract = (val Abstractions.make config) in let module Analyzer = Make (Abstract) in @@ -217,7 +216,7 @@ let force_compute () = (* The new analyzer can be accesed through hooks *) Self.ComputationState.set Computing; let module Analyzer = (val snd !ref_analyzer) in - try Analyzer.compute_from_entry_point ~lib_entry kf + try Analyzer.Compute.compute_from_entry_point ~lib_entry kf with Self.Abort -> Self.(ComputationState.set Aborted); Self.error "The analysis has been aborted: results are incomplete." diff --git a/src/plugins/eva/engine/analysis.mli b/src/plugins/eva/engine/analysis.mli index 436a53aa7c6..b1c00bbb76c 100644 --- a/src/plugins/eva/engine/analysis.mli +++ b/src/plugins/eva/engine/analysis.mli @@ -48,24 +48,19 @@ module type Results = sig end -module Make (Abstract: Abstractions.S) : sig - - val compute_from_entry_point : kernel_function -> lib_entry:bool -> unit - val compute_from_init_state: kernel_function -> Abstract.Dom.t -> unit - - include Results with type state := Abstract.Dom.state - and type value := Abstract.Val.t - and type location := Abstract.Loc.location -end - - module type S = sig - include Abstractions.S_with_evaluation + include Engine_sig.S include Results with type state := Dom.state and type value := Val.t and type location := Loc.location end +module Make (Abstract: Abstractions.S) : S + with module Ctx = Abstract.Ctx + and module Val = Abstract.Val + and module Loc = Abstract.Loc + and module Dom = Abstract.Dom + val current_analyzer : unit -> (module S) (** The abstractions used in the latest analysis, and its results. *) diff --git a/src/plugins/eva/engine/compute_functions.ml b/src/plugins/eva/engine/compute_functions.ml index 84a84a278f1..dfd18af97e0 100644 --- a/src/plugins/eva/engine/compute_functions.ml +++ b/src/plugins/eva/engine/compute_functions.ml @@ -151,28 +151,32 @@ let register_signal_handler () = let restore_sigint = register_handler Sys.sigint interrupt in fun () -> restore_sigusr1 (); restore_sigint () -module Make (Abstract: Abstractions.S_with_evaluation) = struct +module Make (Engine: Engine_sig.S) = struct - module PowersetDomain = Powerset.Make (Abstract.Dom) + module PowersetDomain = Powerset.Make (Engine.Dom) - module Transfer = Transfer_stmt.Make (Abstract) - module Logic = Transfer_logic.Make (Abstract.Dom) (PowersetDomain) - module Spec = Transfer_specification.Make (Abstract) (PowersetDomain) (Logic) - module Init = Initialization.Make (Abstract.Dom) (Abstract.Eval) (Transfer) + module Transfer = Transfer_stmt.Make (Engine) + module Logic = Transfer_logic.Make (Engine.Dom) (PowersetDomain) + module Spec = Transfer_specification.Make (Engine) (PowersetDomain) (Logic) + module Init = Initialization.Make (Engine.Dom) (Engine.Eval) (Transfer) module Computer = Iterator.Computer - (Abstract) (PowersetDomain) (Transfer) (Init) (Logic) (Spec) + (Engine) (PowersetDomain) (Transfer) (Init) (Logic) (Spec) - include Cvalue_domain.Getters (Abstract.Dom) + include Cvalue_domain.Getters (Engine.Dom) + + type state = Engine.Dom.t + type loc = Engine.Loc.location + type value = Engine.Val.t let get_cval = - match Abstract.Val.get Main_values.CVal.key with + match Engine.Val.get Main_values.CVal.key with | None -> fun _ -> assert false | Some get -> fun value -> get value let get_ploc = - match Abstract.Loc.get Main_locations.PLoc.key with + match Engine.Loc.get Main_locations.PLoc.key with | None -> fun _ -> assert false | Some get -> fun location -> get location @@ -186,7 +190,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct (* ----- Mem Exec cache --------------------------------------------------- *) - module MemExec = Mem_exec.Make (Abstract.Val) (Abstract.Dom) + module MemExec = Mem_exec.Make (Engine.Val) (Engine.Dom) let compute_and_cache_call compute kinstr call init_state = let args = @@ -196,9 +200,9 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct | None -> let call_result = compute kinstr call init_state in let () = - if call_result.Transfer.cacheable = Eval.Cacheable + if call_result.Engine_sig.cacheable = Eval.Cacheable then - let final_states = call_result.Transfer.states in + let final_states = call_result.states in MemExec.store_computed_call call.kf init_state args final_states in call_result @@ -219,7 +223,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct "Reusing old results for call to %a" Kernel_function.pretty call.kf; apply_call_results_hooks call init_state (`Reuse i); (* call can be cached since it was cached once *) - Transfer.{ states; cacheable = Cacheable; } + Engine_sig.{ states; cacheable = Cacheable; } (* ----- Body or specification analysis ----------------------------------- *) @@ -274,7 +278,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct if Parameters.ValShowProgress.get () then Self.feedback "Done for function %a" Kernel_function.pretty call.kf; - Transfer.{ states = resulting_states; cacheable; } + Engine_sig.{ states = resulting_states; cacheable; } (* ----- Use of cvalue builtins ------------------------------------------- *) @@ -293,7 +297,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct let join_states = function | [] -> `Bottom | (_k,s) :: l -> - `Value (List.fold_left Abstract.Dom.join s (List.map snd l)) + `Value (List.fold_left Engine.Dom.join s (List.map snd l)) (* Interprets a call to [kf] at callsite [kinstr] in the state [state] by using a cvalue builtin. *) @@ -311,7 +315,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct match final_state with | `Bottom -> apply_call_results_hooks call state (`Builtin ([], None)); - Transfer.{ states; cacheable = Eval.Cacheable; } + Engine_sig.{ states; cacheable = Cacheable; } | `Value final_state -> let cvalue_call = get_cvalue_call call in let post = get_cvalue_or_top final_state in @@ -321,17 +325,17 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct in let insert cvalue_state = Partition.Key.empty, - Abstract.Dom.set Cvalue_domain.State.key cvalue_state final_state + Engine.Dom.set Cvalue_domain.State.key cvalue_state final_state in let states = List.map insert cvalue_states in - Transfer.{ states; cacheable; } + Engine_sig.{ states; cacheable; } (* Uses cvalue builtin only if the cvalue domain is available. Otherwise, only use the called function specification. *) let compute_builtin = - if Abstract.Dom.mem Cvalue_domain.State.key - && Abstract.Val.mem Main_values.CVal.key - && Abstract.Loc.mem Main_locations.PLoc.key + if Engine.Dom.mem Cvalue_domain.State.key + && Engine.Val.mem Main_values.CVal.key + && Engine.Loc.mem Main_locations.PLoc.key then compute_builtin else fun (_, _, _, spec) -> compute_using_spec_or_body (`Spec spec) @@ -340,7 +344,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct (* Interprets a [call] at callsite [kinstr] in the state [state], using a builtin, the specification or the body of the called function, according to [Function_calls.register]. *) - let compute_call kinstr call recursion state = + let compute_call' kinstr call recursion state = let recursion_depth = Option.map (fun r -> r.depth) recursion in let target = Function_calls.define_analysis_target ?recursion_depth kinstr call.kf @@ -354,7 +358,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct then compute_and_cache_call compute kinstr call state else compute kinstr call state - let () = Transfer.compute_call_ref := (fun stmt -> compute_call (Kstmt stmt)) + let compute_call stmt = compute_call' (Kstmt stmt) (* ----- Main call -------------------------------------------------------- *) @@ -362,22 +366,22 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct let restore_signals = register_signal_handler () in let compute () = let callstack = Eva_utils.init_call_stack kf in - Abstract.Dom.Store.register_initial_state callstack kf init_state; + Engine.Dom.Store.register_initial_state callstack kf init_state; let call = { kf; callstack; arguments = []; rest = []; return = None; } in - let final_result = compute_call Kglobal call None init_state in - let final_states = List.map snd (final_result.Transfer.states) in + let final_result = compute_call' Kglobal call None init_state in + let final_states = List.map snd (final_result.states) in let final_state = PowersetDomain.(final_states |> of_list |> join) in Eva_utils.clear_call_stack (); - Abstract.Dom.Store.mark_as_computed (); + Engine.Dom.Store.mark_as_computed (); Self.(ComputationState.set Computed); post_analysis (); - Abstract.Dom.post_analysis final_state; + Engine.Dom.post_analysis final_state; Summary.print_summary (); Statistics.export_as_csv (); restore_signals () in let cleanup () = - Abstract.Dom.Store.mark_as_computed (); + Engine.Dom.Store.mark_as_computed (); Self.(ComputationState.set Aborted); post_analysis_cleanup ~aborted:true in @@ -395,7 +399,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct in match initial_state with | `Bottom -> - Abstract.Dom.Store.mark_as_computed (); + Engine.Dom.Store.mark_as_computed (); Self.(ComputationState.set Aborted); Self.result "Eva not started because globals \ initialization is not computable."; @@ -406,7 +410,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct let compute_from_init_state kf init_state = pre_analysis (); let b = Parameters.ResultsAll.get () in - Abstract.Dom.Store.register_global_state b (`Value init_state); + Engine.Dom.Store.register_global_state b (`Value init_state); compute kf init_state end diff --git a/src/plugins/eva/engine/compute_functions.mli b/src/plugins/eva/engine/compute_functions.mli index a79baded6ee..3d2c02c1ce2 100644 --- a/src/plugins/eva/engine/compute_functions.mli +++ b/src/plugins/eva/engine/compute_functions.mli @@ -22,14 +22,7 @@ (** Value analysis of entire functions, using Eva engine. *) -open Cil_types - -module Make (Abstract: Abstractions.S_with_evaluation) - : sig - - (** Compute a call to the main function. *) - val compute_from_entry_point: kernel_function -> lib_entry:bool -> unit - - (** Compute a call to the main function from the given initial state. *) - val compute_from_init_state: kernel_function -> Abstract.Dom.t -> unit - end +module Make (Engine : Engine_sig.S) : Engine_sig.Compute + with type state = Engine.Dom.t + and type value = Engine.Val.t + and type loc = Engine.Loc.location diff --git a/src/plugins/eva/engine/engine_sig.ml b/src/plugins/eva/engine/engine_sig.ml new file mode 100644 index 00000000000..8597c629984 --- /dev/null +++ b/src/plugins/eva/engine/engine_sig.ml @@ -0,0 +1,65 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2024 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types +open Eval + +type 'state call_result = { + states: (Partition.key * 'state) list; + cacheable: cacheable; +} + +module type Compute = +sig + type state + type loc + type value + + (** Compute a call to the main function. *) + val compute_from_entry_point: kernel_function -> lib_entry:bool -> unit + + (** Compute a call to the main function from the given initial state. *) + val compute_from_init_state: kernel_function -> state -> unit + + (** Compute a call during an analysis *) + val compute_call: + stmt -> (loc, value) call -> recursion option -> state -> state call_result +end + + +module type S = sig + (* The four abstractions (values, locations, states and evaluation context). *) + include Abstractions.S + + (* The evaluator for these abstractions *) + module Eval : Evaluation_sig.S + with type state = Dom.t + and type context = Ctx.t + and type value = Val.t + and type loc = Loc.location + and type origin = Dom.origin + + module Compute : Compute + with type state = Dom.t + and type value = Val.t + and type loc = Loc.location +end diff --git a/src/plugins/eva/engine/transfer_stmt.ml b/src/plugins/eva/engine/transfer_stmt.ml index 373b1bc3c8a..839d17ea3a3 100644 --- a/src/plugins/eva/engine/transfer_stmt.ml +++ b/src/plugins/eva/engine/transfer_stmt.ml @@ -38,12 +38,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 - type call_result = { - states: (Partition.key * state) list; - cacheable: Eval.cacheable; - } - val compute_call_ref: - (stmt -> (loc, value) call -> recursion option -> state -> call_result) ref end (* Reference filled in by the callwise-inout callback *) @@ -111,12 +105,12 @@ let substitution_visitor table = in { Eva_ast.Rewrite.default with rewrite_varinfo } -module Make (Abstract: Abstractions.S_with_evaluation) = struct +module Make (Engine: Engine_sig.S) = struct - module Value = Abstract.Val - module Location = Abstract.Loc - module Domain = Abstract.Dom - module Eval = Abstract.Eval + module Value = Engine.Val + module Location = Engine.Loc + module Domain = Engine.Dom + module Eval = Engine.Eval include Cvalue_domain.Getters (Domain) type state = Domain.t @@ -288,17 +282,6 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct (* Function Calls *) (* ------------------------------------------------------------------------ *) - type call_result = { - states: (Partition.key * state) list; - cacheable: cacheable; - } - - (* Forward reference to [Eval_funs.compute_call] *) - let compute_call_ref : - (stmt -> (loc, value) call -> recursion option -> state -> - call_result) ref - = ref (fun _ -> assert false) - (* Returns the result of a call. *) let process_call stmt call recursion valuation state = Eva_utils.push_call_stack call.kf stmt; @@ -310,7 +293,7 @@ module Make (Abstract: Abstractions.S_with_evaluation) = struct | `Value state -> let callstack = Eva_utils.current_call_stack () in Domain.Store.register_initial_state callstack call.kf state; - !compute_call_ref stmt call recursion state + Engine.Compute.compute_call stmt call recursion state | `Bottom -> { states = []; cacheable = Cacheable; } in diff --git a/src/plugins/eva/engine/transfer_stmt.mli b/src/plugins/eva/engine/transfer_stmt.mli index 92a1f591657..34ae3b01fd3 100644 --- a/src/plugins/eva/engine/transfer_stmt.mli +++ b/src/plugins/eva/engine/transfer_stmt.mli @@ -47,23 +47,9 @@ module type S = sig unit or_bottom val enter_scope: kernel_function -> varinfo list -> state -> state - - type call_result = { - states: (Partition.key * state) list; - cacheable: Eval.cacheable; - } - - val compute_call_ref: - (stmt -> (loc, value) call -> recursion option -> state -> call_result) ref end -module Make (Abstract: Abstractions.S_with_evaluation) +module Make (Abstract: Engine_sig.S) : S with type state = Abstract.Dom.t and type value = Abstract.Val.t and type loc = Abstract.Loc.location - -(* -Local Variables: -compile-command: "make -C ../../../.." -End: -*) -- GitLab