From 6121a9b4523d86ce788045dd997bfe2db534c5e5 Mon Sep 17 00:00:00 2001
From: Julien Signoles <julien.signoles@cea.fr>
Date: Tue, 19 Feb 2019 10:44:02 +0100
Subject: [PATCH] new option -e-acsl-functions

---
 src/plugins/e-acsl/dup_functions.ml   |  16 ++--
 src/plugins/e-acsl/env.ml             |  24 +++---
 src/plugins/e-acsl/functions.ml       |  14 ++++
 src/plugins/e-acsl/functions.mli      |   4 +
 src/plugins/e-acsl/mmodel_analysis.ml |  64 +++++++++-------
 src/plugins/e-acsl/options.ml         |   9 +++
 src/plugins/e-acsl/options.mli        |   2 +
 src/plugins/e-acsl/visit.ml           | 101 +++++++++++++++++---------
 8 files changed, 152 insertions(+), 82 deletions(-)

diff --git a/src/plugins/e-acsl/dup_functions.ml b/src/plugins/e-acsl/dup_functions.ml
index a4a7c550087..fb7067d7a67 100644
--- a/src/plugins/e-acsl/dup_functions.ml
+++ b/src/plugins/e-acsl/dup_functions.ml
@@ -263,13 +263,15 @@ class dup_functions_visitor prj = object (self)
   method !vglob_aux = function
   | GVarDecl(vi, loc) | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc)
       when self#is_unvariadic_function vi
-	&& not (Misc.is_library_loc loc) 
-	&& not (Cil.is_builtin vi)
-	&& not (Cil_datatype.Varinfo.Hashtbl.mem fct_tbl vi)
-	&& not (Cil.is_empty_funspec
-		  (Annotations.funspec ~populate:false
-		     (Extlib.the self#current_kf)))
-	-> 
+        && not (Misc.is_library_loc loc)
+        && not (Cil.is_builtin vi)
+        && not (Cil_datatype.Varinfo.Hashtbl.mem fct_tbl vi)
+        && not (Cil.is_empty_funspec
+                  (Annotations.funspec ~populate:false
+                     (Extlib.the self#current_kf)))
+        && Functions.check
+          (try Globals.Functions.get vi with Not_found -> assert false)
+        ->
     self#next ();
     let name = Functions.RTL.mk_gen_name vi.vname in
     let new_vi = 
diff --git a/src/plugins/e-acsl/env.ml b/src/plugins/e-acsl/env.ml
index c23b7089391..9b7748f17f8 100644
--- a/src/plugins/e-acsl/env.ml
+++ b/src/plugins/e-acsl/env.ml
@@ -93,7 +93,7 @@ end = struct
 
 end
 
-let empty_block = 
+let empty_block =
   { new_block_vars = [];
     new_stmts = [];
     pre_stmts = [];
@@ -104,19 +104,19 @@ let empty_mpz_tbl =
   { new_exps = Term.Map.empty;
     clear_stmts = [] }
 
-let empty_local_env = 
-  { block_info = empty_block; 
+let empty_local_env =
+  { block_info = empty_block;
     mpz_tbl = empty_mpz_tbl;
     rte = true }
 
-let dummy = 
+let dummy =
   { visitor = new Visitor.generic_frama_c_visitor (Cil.inplace_visit ());
     lscope = Lscope.empty;
     lscope_reset = true;
     annotation_kind = Misc.Assertion;
     new_global_vars = [];
-    global_mpz_tbl = empty_mpz_tbl; 
-    env_stack = []; 
+    global_mpz_tbl = empty_mpz_tbl;
+    env_stack = [];
     var_mapping = Logic_var.Map.empty;
     loop_invariants = [];
     cpt = 0; }
@@ -127,15 +127,15 @@ let empty v =
     lscope_reset = true;
     annotation_kind = Misc.Assertion;
     new_global_vars = [];
-    global_mpz_tbl = empty_mpz_tbl; 
-    env_stack = []; 
+    global_mpz_tbl = empty_mpz_tbl;
+    env_stack = [];
     var_mapping = Logic_var.Map.empty;
     loop_invariants = [];
     cpt = 0 }
 
-
-let top env =
-  match env.env_stack with [] -> assert false | hd :: tl -> hd, tl
+let top env = match env.env_stack with
+  | [] -> Options.fatal "Empty environment. That is unexpected."
+  | hd :: tl -> hd, tl
 
 let has_no_new_stmt env =
   let local, _ = top env in
@@ -145,7 +145,7 @@ let has_no_new_stmt env =
 (** {2 Loop invariants} *)
 (* ************************************************************************** *)
 
-let push_loop env = 
+let push_loop env =
   { env with loop_invariants = [] :: env.loop_invariants }
 
 let add_loop_invariant env inv = match env.loop_invariants with
diff --git a/src/plugins/e-acsl/functions.ml b/src/plugins/e-acsl/functions.ml
index 22c8f1cd994..489cbbc03f4 100644
--- a/src/plugins/e-acsl/functions.ml
+++ b/src/plugins/e-acsl/functions.ml
@@ -212,3 +212,17 @@ module Libc = struct
     Cil.mkString ~loc param_str
 
 end
+
+let check kf =
+  (* [kf] is monitored iff all functions must be monitored or [kf] belongs to
+     the white list *)
+  Options.Functions.is_empty ()
+  || Options.Functions.mem kf
+  ||
+    (* also check if [kf] is a duplicate of a monitored function *)
+    let s = RTL.get_original_name kf in
+    try
+      let gen_kf = Globals.Functions.find_by_name s in
+      Options.Functions.mem gen_kf
+    with Not_found ->
+      false
diff --git a/src/plugins/e-acsl/functions.mli b/src/plugins/e-acsl/functions.mli
index 7c65ec95fd4..6798b215f89 100644
--- a/src/plugins/e-acsl/functions.mli
+++ b/src/plugins/e-acsl/functions.mli
@@ -26,6 +26,10 @@ val has_fundef: exp -> bool
 (** @return [true] if a function whose name is given via [exp] is defined and
     [false] otherwise *)
 
+val check: kernel_function -> bool
+(** @return [true] iff code must be generated for annotations of the given
+    function. *)
+
 (* ************************************************************************** *)
 (** {2 RTL} Operations on function belonging to the runtime library of E-ACSL *)
 (* ************************************************************************** *)
diff --git a/src/plugins/e-acsl/mmodel_analysis.ml b/src/plugins/e-acsl/mmodel_analysis.ml
index e5c3136060f..6bd1ce2b1d1 100644
--- a/src/plugins/e-acsl/mmodel_analysis.ml
+++ b/src/plugins/e-acsl/mmodel_analysis.ml
@@ -453,35 +453,43 @@ let register_predicate kf pred state =
       (fun state ->
         let state = Env.default_varinfos state in
         let state =
-          if (is_first || is_last) && Functions.RTL.is_generated_kf kf then
-            Annotations.fold_behaviors
-              (fun _ bhv s ->
-                let handle_annot test f s =
-                  if test then
-                    f (fun _ p s -> register_predicate kf p s) kf bhv.b_name s
-                  else
-                    s
-                in
-                let s = handle_annot is_first Annotations.fold_requires s in
-                let s = handle_annot is_first Annotations.fold_assumes s in
-                handle_annot
-                  is_last
-                  (fun f -> Annotations.fold_ensures (fun e (_, p) -> f e p)) s)
-              kf
+          if Functions.check kf then
+            let state =
+              if (is_first || is_last) && Functions.RTL.is_generated_kf kf then
+                Annotations.fold_behaviors
+                  (fun _ bhv s ->
+                    let handle_annot test f s =
+                      if test then
+                        f
+                          (fun _ p s -> register_predicate kf p s)
+                          kf
+                          bhv.b_name
+                          s
+                      else
+                        s
+                  in
+                    let s = handle_annot is_first Annotations.fold_requires s in
+                    let s = handle_annot is_first Annotations.fold_assumes s in
+                    handle_annot
+                      is_last
+                      (fun f ->
+                        Annotations.fold_ensures (fun e (_, p) -> f e p)) s)
+                  kf
+                  state
+              else
+                state
+            in
+            let state =
+              Annotations.fold_code_annot
+                (fun _ -> register_code_annot kf) stmt state
+            in
+            if stmt.ghost then
+              let rtes = Rte.stmt kf stmt in
+              List.fold_left
+                (fun state a -> register_code_annot kf a state) state rtes
+            else
               state
-          else
-            state
-        in
-        let state =
-          Annotations.fold_code_annot
-            (fun _ -> register_code_annot kf) stmt state
-        in
-        let state =
-          if stmt.ghost then
-            let rtes = Rte.stmt kf stmt in
-            List.fold_left
-              (fun state a -> register_code_annot kf a state) state rtes
-          else
+          else (* not (Options.Functions.check kf): do not monitor [kf] *)
             state
         in
         let state =
diff --git a/src/plugins/e-acsl/options.ml b/src/plugins/e-acsl/options.ml
index 93797ae0bde..c7fd1214917 100644
--- a/src/plugins/e-acsl/options.ml
+++ b/src/plugins/e-acsl/options.ml
@@ -114,6 +114,15 @@ module Builtins =
       let help = "C functions which can be used in the E-ACSL specifications"
      end)
 
+module Functions =
+  Kernel_function_set
+    (struct
+      let option_name = "-e-acsl-functions"
+      let arg_name ="f1, ..., fn"
+      let help = "only annotations in functions f1, ..., fn are checked at \
+runtime"
+     end)
+
 let () = Parameter_customize.set_group help
 module Version =
   False
diff --git a/src/plugins/e-acsl/options.mli b/src/plugins/e-acsl/options.mli
index 6e1347e43d7..628234584f1 100644
--- a/src/plugins/e-acsl/options.mli
+++ b/src/plugins/e-acsl/options.mli
@@ -34,6 +34,8 @@ module Temporal_validity: Parameter_sig.Bool
 module Validate_format_strings: Parameter_sig.Bool
 module Replace_libc_functions: Parameter_sig.Bool
 
+module Functions: Parameter_sig.Kernel_function_set
+
 val parameter_states: State.t list
 
 val must_visit: unit -> bool
diff --git a/src/plugins/e-acsl/visit.ml b/src/plugins/e-acsl/visit.ml
index 031004a66a1..c30617569d2 100644
--- a/src/plugins/e-acsl/visit.ml
+++ b/src/plugins/e-acsl/visit.ml
@@ -643,25 +643,31 @@ you must call function `%s' and `__e_acsl_memory_clean by yourself.@]"
             env
         in
         (* translate the precondition of the function *)
-        Project.on prj (Translate.translate_pre_spec kf env) !funspec
+        if Functions.check kf then
+          Project.on prj (Translate.translate_pre_spec kf env) !funspec
+        else
+          env
       else
         env
     in
 
     let env, new_annots =
-      Annotations.fold_code_annot
-        (fun _ old_a (env, new_annots) ->
-          let a =
-            (* [VP] Don't use Visitor here, as it will fill the queue in the
-               middle of the computation... *)
-            Cil.visitCilCodeAnnotation (self :> Cil.cilVisitor) old_a
-          in
-          let env =
-            Project.on prj (Translate.translate_pre_code_annotation kf env) a
-          in
-          env, a :: new_annots)
-        (Cil.get_original_stmt self#behavior stmt)
-        (env, [])
+      if Functions.check kf then
+        Annotations.fold_code_annot
+          (fun _ old_a (env, new_annots) ->
+            let a =
+              (* [VP] Don't use Visitor here, as it will fill the queue in the
+                 middle of the computation... *)
+              Cil.visitCilCodeAnnotation (self :> Cil.cilVisitor) old_a
+            in
+            let env =
+              Project.on prj (Translate.translate_pre_code_annotation kf env) a
+            in
+            env, a :: new_annots)
+          (Cil.get_original_stmt self#behavior stmt)
+          (env, [])
+      else
+        env, []
     in
 
     (* Add [__e_acsl_store_duplicate] calls for local variables which
@@ -686,18 +692,32 @@ you must call function `%s' and `__e_acsl_memory_clean by yourself.@]"
           let env = Temporal.handle_stmt stmt env in
           (* Add initialization statements and store_block statements stemming
              from Local_init *)
-            self#handle_instructions stmt env kf
+          self#handle_instructions stmt env kf
         else
           env
       in
-      let env =
-        if stmt.ghost && generate then begin
-          stmt.ghost <- false;
-          (* translate potential RTEs of ghost code *)
-          let rtes = Rte.stmt ~warn:false kf stmt in
-          Translate.translate_rte_annots Printer.pp_stmt stmt kf env rtes
-        end else
-          env
+      let new_stmt, env, must_mv =
+        if Functions.check kf then
+          let env =
+            (* handle ghost statement *)
+            if stmt.ghost then begin
+              stmt.ghost <- false;
+              (* translate potential RTEs of ghost code *)
+              let rtes = Rte.stmt ~warn:false kf stmt in
+              Translate.translate_rte_annots Printer.pp_stmt stmt kf env rtes
+            end else
+              env
+          in
+          (* handle loop invariants *)
+          let new_stmt, env, must_mv =
+            Loops.preserve_invariant prj env kf stmt
+          in
+          let orig = Cil.get_original_stmt self#behavior stmt in
+          Cil.set_orig_stmt self#behavior new_stmt orig;
+          Cil.set_stmt self#behavior orig new_stmt;
+          new_stmt, env, must_mv
+        else
+          stmt, env, false
       in
       let mk_post_env env =
         (* [fold_right] to preserve order of generation of pre_conditions *)
@@ -708,11 +728,6 @@ you must call function `%s' and `__e_acsl_memory_clean by yourself.@]"
              new_annots)
           env
       in
-      (* handle loop invariants *)
-      let new_stmt, env, must_mv = Loops.preserve_invariant prj env kf stmt in
-      let orig = Cil.get_original_stmt self#behavior stmt in
-      Cil.set_orig_stmt self#behavior new_stmt orig;
-      Cil.set_stmt self#behavior orig new_stmt;
       let new_stmt, env =
         (* Remove local variables which scopes ended via goto/break/continue. *)
         let del_vars = Exit_points.delete_vars stmt in
@@ -721,13 +736,20 @@ you must call function `%s' and `__e_acsl_memory_clean by yourself.@]"
           else env
         in
         if self#is_return kf stmt then
-          (* must generate the post_block before including [stmt] (the 'return')
-             since no code is executed after it. However, since this statement
-             is pure (Cil invariant), that is semantically correct. *)
-          let env = mk_post_env env in
-          (* also handle the postcondition of the function and clear the env *)
           let env =
-            Project.on prj (Translate.translate_post_spec kf env) !funspec
+            if Functions.check kf then
+              (* must generate the post_block before including [stmt] (the
+                 'return') since no code is executed after it. However, since
+                 this statement is pure (Cil invariant), that is semantically
+                 correct. *)
+              (* [JS 2019/2/19] TODO: what about the other ways of early exiting
+                 a block? *)
+              let env = mk_post_env env in
+              (* also handle the postcondition of the function and clear the
+                 env *)
+              Project.on prj (Translate.translate_post_spec kf env) !funspec
+            else
+              env
           in
           (* de-allocating memory previously allocating by the kf *)
           (* JS: should be done in the new project? *)
@@ -778,7 +800,15 @@ you must call function `%s' and `__e_acsl_memory_clean by yourself.@]"
                 ~global_clear:false
                 Env.After
             in
-            let env = mk_post_env (Env.push env) in
+            let env =
+              (* if [kf] is not monitored, do not translate any postcondition,
+                 but still push an empty environment consumed by
+                 [Env.pop_and_get] below. This [Env.pop_and_get] call is always
+                 required in order to generate the code not directly related to
+                 the annotations of the current stmt in anycase. *)
+              if Functions.check kf then mk_post_env (Env.push env)
+              else Env.push env
+            in
             let post_block, env =
               Env.pop_and_get
                 env
@@ -795,6 +825,7 @@ you must call function `%s' and `__e_acsl_memory_clean by yourself.@]"
             if not (Cil_datatype.Stmt.equal new_stmt res) then
               E_acsl_label.move (self :> Visitor.generic_frama_c_visitor)
                 new_stmt res;
+            let orig = Cil.get_original_stmt self#behavior stmt in
             Cil.set_stmt self#behavior orig res;
             Cil.set_orig_stmt self#behavior res orig;
             res, env
-- 
GitLab