From bd46f102447cf2bad74eccae105624fa7176754d Mon Sep 17 00:00:00 2001
From: Basile Desloges <basile.desloges@cea.fr>
Date: Fri, 6 Mar 2020 11:15:03 +0100
Subject: [PATCH] [eacsl:codegen] Update inject_in_block to support the new
 clean and delete globals placements

Since `memory_clean` and `delete_globals` are added after the calls to
`inject_in_block`, this function must be updated to add the free locals
statements as last statements of the innermost block instead of before
the `memory_clean` statement in the innermost block. Additionnaly, this
fix harmonize the treatment between a classic function and the function
`main()`, and fix the issue frama-c/e-acsl#105.
---
 .../e-acsl/src/code_generator/injector.ml     | 40 ++++++++-----------
 .../gen_at_on-purely-logic-variables.c        | 18 ++++-----
 .../e-acsl/tests/bts/issue-eacsl-105.c        | 21 ++++++++++
 .../tests/bts/oracle_ci/gen_issue-eacsl-105.c | 39 ++++++++++++++++++
 .../bts/oracle_ci/issue-eacsl-105.res.oracle  |  2 +
 .../bts/oracle_dev/issue-eacsl-105.res.oracle |  1 +
 6 files changed, 89 insertions(+), 32 deletions(-)
 create mode 100644 src/plugins/e-acsl/tests/bts/issue-eacsl-105.c
 create mode 100644 src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-105.c
 create mode 100644 src/plugins/e-acsl/tests/bts/oracle_ci/issue-eacsl-105.res.oracle
 create mode 100644 src/plugins/e-acsl/tests/bts/oracle_dev/issue-eacsl-105.res.oracle

diff --git a/src/plugins/e-acsl/src/code_generator/injector.ml b/src/plugins/e-acsl/src/code_generator/injector.ml
index b1b2c8661b5..e0eb50c7a43 100644
--- a/src/plugins/e-acsl/src/code_generator/injector.ml
+++ b/src/plugins/e-acsl/src/code_generator/injector.ml
@@ -502,7 +502,21 @@ and inject_in_block (env: Env.t) kf blk =
   | [], _ :: _ | _ :: _, [] | _ :: _, _ :: _ ->
     (* [TODO] this piece of code could be improved *)
     (* de-allocate the memory blocks observing locals *)
-    let add_locals stmts =
+    let last_stmts ?return_stmt () =
+      let stmts =
+        match return_stmt with
+        | Some return_stmt ->
+          (* now that [free] stmts for [kf] have been inserted,
+             there is no more need to keep the corresponding entries in the
+             table managing them. *)
+          At_with_lscope.Free.remove_all kf;
+          (* The free statements are passed in the same order than the malloc
+             ones. In order to free the variable in the reverse order, the list
+             is reversed before appending the return statement. Moreover,
+             `rev_append` is tail recursive contrary to `append` *)
+          List.rev_append free_stmts [ return_stmt ]
+        | None -> []
+      in
       if Functions.instrument kf then
         List.fold_left
           (fun acc vi ->
@@ -515,28 +529,8 @@ and inject_in_block (env: Env.t) kf blk =
         stmts
     in
     (* select the precise location to inject these pieces of code *)
-    let rec insert_in_innermost_last_block blk = function
-      | { skind = Return _ } as ret :: ((potential_clean :: tl) as l) ->
-        (* keep the return (enclosed in a generated block) at the end;
-           preceded by clean if any *)
-        let init, tl =
-          if Kernel_function.is_main kf && Mmodel_analysis.use_model () then
-            free_stmts @ [ potential_clean; ret ], tl
-          else
-            free_stmts @ [ ret ], l
-        in
-        (* now that [free] stmts for [kf] have been inserted,
-           there is no more need to keep the corresponding entries in the
-           table managing them. *)
-        At_with_lscope.Free.remove_all kf;
-        blk.bstmts <-
-          List.fold_left (fun acc v -> v :: acc) (add_locals init) tl
-      | { skind = Block b } :: _ ->
-        insert_in_innermost_last_block b (List.rev b.bstmts)
-      | l ->
-        blk.bstmts <- List.fold_left (fun acc v -> v :: acc) (add_locals []) l
-    in
-    insert_in_innermost_last_block blk (List.rev blk.bstmts);
+    insert_as_last_stmts_in_innermost_block ~last_stmts kf blk ;
+    (* allocate the memory blocks observing locals *)
     if Functions.instrument kf then
       blk.bstmts <-
         List.fold_left
diff --git a/src/plugins/e-acsl/tests/arith/oracle_ci/gen_at_on-purely-logic-variables.c b/src/plugins/e-acsl/tests/arith/oracle_ci/gen_at_on-purely-logic-variables.c
index 53a78260d27..007bc6daa68 100644
--- a/src/plugins/e-acsl/tests/arith/oracle_ci/gen_at_on-purely-logic-variables.c
+++ b/src/plugins/e-acsl/tests/arith/oracle_ci/gen_at_on-purely-logic-variables.c
@@ -570,13 +570,13 @@ int main(void)
   __retres = 0;
   __e_acsl_delete_block((void *)(t));
   __e_acsl_delete_block((void *)(& n));
-  free((void *)__gen_e_acsl_at_7);
-  free((void *)__gen_e_acsl_at_6);
-  free((void *)__gen_e_acsl_at_5);
-  free((void *)__gen_e_acsl_at_4);
-  free((void *)__gen_e_acsl_at_3);
-  free((void *)__gen_e_acsl_at_2);
   free((void *)__gen_e_acsl_at);
+  free((void *)__gen_e_acsl_at_2);
+  free((void *)__gen_e_acsl_at_3);
+  free((void *)__gen_e_acsl_at_4);
+  free((void *)__gen_e_acsl_at_5);
+  free((void *)__gen_e_acsl_at_6);
+  free((void *)__gen_e_acsl_at_7);
   __e_acsl_memory_clean();
   return __retres;
 }
@@ -706,10 +706,10 @@ void __gen_e_acsl_f(int *t)
                     "\\let m = 4; \\old(*(t + m) == -4) && \\old(*(t + (m - 4))) == 9",
                     "tests/arith/at_on-purely-logic-variables.c",8);
     __e_acsl_delete_block((void *)(& t));
-    free((void *)__gen_e_acsl_at_4);
-    free((void *)__gen_e_acsl_at_3);
-    free((void *)__gen_e_acsl_at_2);
     free((void *)__gen_e_acsl_at);
+    free((void *)__gen_e_acsl_at_2);
+    free((void *)__gen_e_acsl_at_3);
+    free((void *)__gen_e_acsl_at_4);
     return;
   }
 }
diff --git a/src/plugins/e-acsl/tests/bts/issue-eacsl-105.c b/src/plugins/e-acsl/tests/bts/issue-eacsl-105.c
new file mode 100644
index 00000000000..23814838fe9
--- /dev/null
+++ b/src/plugins/e-acsl/tests/bts/issue-eacsl-105.c
@@ -0,0 +1,21 @@
+/* run.config
+   COMMENT: frama-c/e-acsl#105, test for delete block before exiting the 
+    function in the presence of early return.
+*/
+
+int f() {
+  int a = 10;
+  goto lbl_1;
+
+ lbl_2:
+  /*@ assert \valid(&a); */
+  return 0;
+
+ lbl_1:
+  goto lbl_2;
+}
+
+int main(void) {
+  f();
+  return 0;
+}
diff --git a/src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-105.c b/src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-105.c
new file mode 100644
index 00000000000..8ad1e10fa53
--- /dev/null
+++ b/src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-105.c
@@ -0,0 +1,39 @@
+/* Generated by Frama-C */
+#include "stdio.h"
+#include "stdlib.h"
+int f(void)
+{
+  int __retres;
+  int a = 10;
+  __e_acsl_store_block((void *)(& a),(size_t)4);
+  __e_acsl_full_init((void *)(& a));
+  goto lbl_1;
+  lbl_2:
+  /*@ assert \valid(&a); */
+  {
+    int __gen_e_acsl_valid;
+    __gen_e_acsl_valid = __e_acsl_valid((void *)(& a),sizeof(int),
+                                        (void *)(& a),(void *)0);
+    __e_acsl_assert(__gen_e_acsl_valid,"Assertion","f","\\valid(&a)",
+                    "tests/bts/issue-eacsl-105.c",11);
+  }
+  __retres = 0;
+  goto return_label;
+  lbl_1: goto lbl_2;
+  return_label: {
+                  __e_acsl_delete_block((void *)(& a));
+                  return __retres;
+                }
+}
+
+int main(void)
+{
+  int __retres;
+  __e_acsl_memory_init((int *)0,(char ***)0,(size_t)8);
+  f();
+  __retres = 0;
+  __e_acsl_memory_clean();
+  return __retres;
+}
+
+
diff --git a/src/plugins/e-acsl/tests/bts/oracle_ci/issue-eacsl-105.res.oracle b/src/plugins/e-acsl/tests/bts/oracle_ci/issue-eacsl-105.res.oracle
new file mode 100644
index 00000000000..efd02631129
--- /dev/null
+++ b/src/plugins/e-acsl/tests/bts/oracle_ci/issue-eacsl-105.res.oracle
@@ -0,0 +1,2 @@
+[e-acsl] beginning translation.
+[e-acsl] translation done in project "e-acsl".
diff --git a/src/plugins/e-acsl/tests/bts/oracle_dev/issue-eacsl-105.res.oracle b/src/plugins/e-acsl/tests/bts/oracle_dev/issue-eacsl-105.res.oracle
new file mode 100644
index 00000000000..382eac9f065
--- /dev/null
+++ b/src/plugins/e-acsl/tests/bts/oracle_dev/issue-eacsl-105.res.oracle
@@ -0,0 +1 @@
+[kernel] Parsing tests/bts/issue-eacsl-105.c (with preprocessing)
-- 
GitLab