diff --git a/src/plugins/e-acsl/Makefile.in b/src/plugins/e-acsl/Makefile.in
index 9321f0f5a8d863065f3ed19c1ae5b9f884a67f5e..3217ed21a1f00a3ef9700316b40609514416f95e 100644
--- a/src/plugins/e-acsl/Makefile.in
+++ b/src/plugins/e-acsl/Makefile.in
@@ -58,8 +58,7 @@ SRC_ANALYSES:=$(addprefix src/analyses/, $(SRC_ANALYSES))
 # project initializer
 SRC_PROJECT_INITIALIZER:= \
 	keep_status \
-	prepare_ast \
-	dup_functions
+	prepare_ast
 SRC_PROJECT_INITIALIZER:=\
   $(addprefix src/project_initializer/, $(SRC_PROJECT_INITIALIZER))
 
diff --git a/src/plugins/e-acsl/src/main.ml b/src/plugins/e-acsl/src/main.ml
index f8053f3dc74882f6be00928cd1561831d84fa855..c7546057321970e52c9e8e5c0bfa557c0964e0d5 100644
--- a/src/plugins/e-acsl/src/main.ml
+++ b/src/plugins/e-acsl/src/main.ml
@@ -144,7 +144,6 @@ let generate_code =
               copied_prj
               (fun () ->
                  Prepare_ast.prepare ();
-                 Dup_functions.dup ();
                  Injector.inject ();
                  (* remove the RTE's results computed from E-ACSL: they are
                       partial and associated with the wrong kernel function (the
diff --git a/src/plugins/e-acsl/src/options.ml b/src/plugins/e-acsl/src/options.ml
index 22a51c9286df2825631ab59de445461a2bbe3338..ae196e9bc0748be6ac5115454a851af8a406e3b6 100644
--- a/src/plugins/e-acsl/src/options.ml
+++ b/src/plugins/e-acsl/src/options.ml
@@ -166,7 +166,7 @@ let parameter_states =
 let must_visit () = Run.get () || Check.get ()
 
 let dkey_analysis = register_category "analysis"
-let dkey_dup = register_category "duplication"
+let dkey_prepare = register_category "preparation"
 let dkey_translation = register_category "translation"
 let dkey_typing = register_category "typing"
 
diff --git a/src/plugins/e-acsl/src/options.mli b/src/plugins/e-acsl/src/options.mli
index cdc235ce85e6f7eeeb29834b7fdc90a68e2de292..cecf66c0d31025e0d4b40104d03e49db80f057f1 100644
--- a/src/plugins/e-acsl/src/options.mli
+++ b/src/plugins/e-acsl/src/options.mli
@@ -42,7 +42,7 @@ val parameter_states: State.t list
 val must_visit: unit -> bool
 
 val dkey_analysis: category
-val dkey_dup: category
+val dkey_prepare: category
 val dkey_translation: category
 val dkey_typing: category
 
diff --git a/src/plugins/e-acsl/src/project_initializer/dup_functions.ml b/src/plugins/e-acsl/src/project_initializer/dup_functions.ml
deleted file mode 100644
index c8e395c43fbc6f5c8138d1f2c9d8e35c8a0cb23a..0000000000000000000000000000000000000000
--- a/src/plugins/e-acsl/src/project_initializer/dup_functions.ml
+++ /dev/null
@@ -1,438 +0,0 @@
-(**************************************************************************)
-(*                                                                        *)
-(*  This file is part of the Frama-C's E-ACSL plug-in.                    *)
-(*                                                                        *)
-(*  Copyright (C) 2012-2019                                               *)
-(*    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
-
-let dkey = Options.dkey_dup
-
-(* ********************************************************************** *)
-(* Environment *)
-(* ********************************************************************** *)
-
-let fct_tbl: unit Kernel_function.Hashtbl.t = Kernel_function.Hashtbl.create 7
-
-let actions = Queue.create ()
-
-module Global: sig
-  val add_logic_info: logic_info -> unit
-  val mem_logic_info: logic_info -> bool
-  val reset: unit -> unit
-end = struct
-
-  let tbl = Cil_datatype.Logic_info.Hashtbl.create 7
-  let add_logic_info x = Cil_datatype.Logic_info.Hashtbl.add tbl x ()
-  let mem_logic_info x = Cil_datatype.Logic_info.Hashtbl.mem tbl x
-  let reset () = Cil_datatype.Logic_info.Hashtbl.clear tbl
-
-end
-
-let reset () =
-  Kernel_function.Hashtbl.clear fct_tbl;
-  Global.reset ();
-  Queue.clear actions
-
-(* ********************************************************************** *)
-(* Duplicating functions *)
-(* ********************************************************************** *)
-
-let dup_funspec tbl bhv spec =
-  (*  Options.feedback "DUP SPEC %a" Cil.d_funspec spec;*)
-  let o = object
-    inherit Cil.genericCilVisitor bhv
-
-    val already_visited = Cil_datatype.Logic_var.Hashtbl.create 7
-
-    method !vlogic_info_use li =
-      if Global.mem_logic_info li then
-        Cil.ChangeDoChildrenPost
-          ({ li with l_var_info = li.l_var_info } (* force a copy *),
-           Visitor_behavior.Get.logic_info bhv)
-      else
-        Cil.JustCopy
-
-    method !vterm_offset _ =
-      Cil.DoChildrenPost
-        (function (* no way to directly visit fieldinfo and model_info uses *)
-          | TField(fi, off) ->
-            TField(Visitor_behavior.Get.fieldinfo bhv fi, off)
-          | TModel(mi, off) ->
-            TModel(Visitor_behavior.Get.model_info bhv mi, off)
-          | off ->
-            off)
-
-    method !vlogic_var_use orig_lvi =
-      match orig_lvi.lv_origin with
-      | None ->
-        Cil.JustCopy
-      | Some vi ->
-        try
-          let new_lvi =
-            Cil_datatype.Logic_var.Hashtbl.find already_visited orig_lvi
-          in
-          Cil.ChangeTo new_lvi
-        with Not_found ->
-          Cil.ChangeDoChildrenPost
-            ({ orig_lvi with lv_id = orig_lvi.lv_id } (* force a copy *),
-             fun lvi ->
-               (* using [Visitor_behavior.Get.logic_var bhv lvi] is correct only
-                  because the lv_id used to compare the lvi does not change
-                  between the original one and this copy *)
-               try
-                 let new_vi = Cil_datatype.Varinfo.Hashtbl.find tbl vi in
-                 Cil_datatype.Logic_var.Hashtbl.add
-                   already_visited orig_lvi lvi;
-                 lvi.lv_id <- new_vi.vid;
-                 lvi.lv_name <- new_vi.vname;
-                 lvi.lv_origin <- Some new_vi;
-                 new_vi.vlogic_var_assoc <- Some lvi;
-                 lvi
-               with Not_found ->
-                 assert vi.vglob;
-                 Visitor_behavior.Get.logic_var bhv lvi)
-
-    method !videntified_term _ =
-      Cil.DoChildrenPost Logic_const.refresh_identified_term
-
-    method !videntified_predicate _ =
-      Cil.DoChildrenPost Logic_const.refresh_predicate
-  end in
-  Cil.visitCilFunspec o spec
-
-let dup_fundec loc spec bhv sound_verdict_vi kf vi new_vi =
-  new_vi.vdefined <- true;
-  let formals = Kernel_function.get_formals kf in
-  let mk_formal vi =
-    let name =
-      if vi.vname = "" then
-        (* unnamed formal parameter: must generate a fresh name since a fundec
-           cannot have unnamed formals (see bts #2303). *)
-        Varname.get
-          ~scope:Varname.Function
-          (Functions.RTL.mk_gen_name "unamed_formal")
-      else
-        vi.vname
-    in
-    Cil.copyVarinfo vi name
-  in
-  let new_formals = List.map mk_formal formals in
-  let res =
-    let ty = Kernel_function.get_return_type kf in
-    if Cil.isVoidType ty then None
-    else Some (Cil.makeVarinfo false false ~referenced:true "__retres" ty)
-  in
-  let return =
-    Cil.mkStmt ~valid_sid:true
-      (Return(Extlib.opt_map (Cil.evar ~loc) res, loc))
-  in
-  let stmts =
-    let l =
-      [ Cil.mkStmtOneInstr ~valid_sid:true
-          (Call(Extlib.opt_map Cil.var res,
-                Cil.evar ~loc vi,
-                List.map (Cil.evar ~loc) new_formals,
-                loc));
-        return ]
-    in
-    if Functions.instrument kf then
-      l
-    else
-      (* set the 'unsound_verdict' variable to 'false' whenever required *)
-      let unsound =
-        Cil.mkStmtOneInstr ~valid_sid:true
-          (Set((Var sound_verdict_vi, NoOffset), Cil.zero ~loc, loc))
-      in
-      unsound :: l
-  in
-  let locals = match res with None -> [] | Some r -> [ r ] in
-  let body = Cil.mkBlock stmts in
-  body.blocals <- locals;
-  let tbl = Cil_datatype.Varinfo.Hashtbl.create 7 in
-  List.iter2 (Cil_datatype.Varinfo.Hashtbl.add tbl) formals new_formals;
-  let new_spec = dup_funspec tbl bhv spec in
-  let fundec =
-    { svar = new_vi;
-      sformals = new_formals;
-      slocals = locals;
-      smaxid = List.length new_formals;
-      sbody = body;
-      smaxstmtid = None;
-      sallstmts = [];
-      sspec = new_spec }
-  in
-  (* compute the CFG of the new [fundec] *)
-  Cfg.clearCFGinfo ~clear_id:false fundec;
-  Cfg.cfgFun fundec;
-  fundec
-
-let dup_global loc actions spec bhv sound_verdict_vi kf vi new_vi =
-  let name = vi.vname in
-  Options.feedback ~dkey ~level:2 "entering in function %s" name;
-  let fundec = dup_fundec loc spec bhv sound_verdict_vi kf vi new_vi  in
-  let fct = Definition(fundec, loc) in
-  let new_spec = fundec.sspec in
-  let new_kf = { fundec = fct; spec = new_spec } in
-  Queue.add (fun () ->
-      Kernel_function.Hashtbl.add fct_tbl new_kf ();
-      Globals.Functions.register new_kf;
-      Globals.Functions.replace_by_definition new_spec fundec loc;
-      Annotations.register_funspec new_kf;
-      if new_vi.vname = "main" then begin
-        (* recompute the info about the old main since its name has changed;
-           see the corresponding part in the main visitor *)
-        Globals.Functions.remove vi;
-        Globals.Functions.register kf
-      end)
-    actions;
-  Options.feedback ~dkey ~level:2 "function %s" name;
-  (* remove the specs attached to the previous kf iff it is a definition:
-     it is necessary to keep stable the number of annotations in order to get
-     [Keep_status] working fine. *)
-  let kf = Visitor_behavior.Get.kernel_function bhv kf in
-  if Kernel_function.is_definition kf then begin
-    Queue.add
-      (fun () ->
-         let bhvs =
-           Annotations.fold_behaviors (fun e b acc -> (e, b) :: acc) kf []
-         in
-         List.iter
-           (fun (e, b) -> Annotations.remove_behavior ~force:true e kf b)
-           bhvs;
-         Annotations.iter_decreases
-           (fun e _ -> Annotations.remove_decreases e kf)
-           kf;
-         Annotations.iter_terminates
-           (fun e _ -> Annotations.remove_terminates e kf)
-           kf;
-         Annotations.iter_complete
-           (fun e l -> Annotations.remove_complete e kf l)
-           kf;
-         Annotations.iter_disjoint
-           (fun e l -> Annotations.remove_disjoint e kf l)
-           kf)
-      actions
-  end;
-  GFun(fundec, loc), GFunDecl(new_spec, new_vi, loc)
-
-(* ********************************************************************** *)
-(* Visitor *)
-(* ********************************************************************** *)
-
-type position = Before_gmp | Gmpz | After_gmp | Memory_model | Code
-
-class dup_functions_visitor = object (self)
-  inherit Visitor.frama_c_inplace
-
-  val unduplicable_functions =
-    let white_list =
-      [ "__builtin_va_arg";
-        "__builtin_va_end";
-        "__builtin_va_start";
-        "__builtin_va_copy" ]
-    in
-    List.fold_left
-      (fun acc s -> Datatype.String.Set.add s acc)
-      Datatype.String.Set.empty
-      white_list
-
-  val fct_tbl = Cil_datatype.Varinfo.Hashtbl.create 7
-  val mutable before_memory_model = Before_gmp
-  val mutable new_definitions: global list = []
-  (* new definitions of the annotated functions which will contain the
-     translation of the E-ACSL contract *)
-
-  val mutable sound_verdict_vi =
-    let name = Functions.RTL.mk_api_name "sound_verdict" in
-    let vi = Cil.makeGlobalVar name Cil.intType in
-    vi.vstorage <- Extern;
-    vi.vreferenced <- true;
-    vi
-
-  method private before_memory_model = match before_memory_model with
-    | Before_gmp | Gmpz | After_gmp -> true
-    | Memory_model | Code -> false
-
-  method private next () =
-    match before_memory_model with
-    | Before_gmp -> ()
-    | Gmpz -> before_memory_model <- After_gmp
-    | After_gmp -> ()
-    | Memory_model -> before_memory_model <- Code
-    | Code -> ()
-
-  method !vlogic_info_decl li =
-    Global.add_logic_info li;
-    Cil.SkipChildren
-
-  method !vvrbl vi =
-    try
-      let new_vi = Cil_datatype.Varinfo.Hashtbl.find fct_tbl vi in
-      Cil.ChangeTo new_vi
-    with Not_found ->
-      Cil.SkipChildren
-
-  method private is_variadic_function vi =
-    match Cil.unrollType vi.vtype with
-    | TFun(_, _, variadic, _) -> variadic
-    | _ -> true
-
-  method !vglob_aux = function
-    | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc)
-      when (* duplicate a function iff: *)
-        not (Cil_datatype.Varinfo.Hashtbl.mem fct_tbl vi)
-        (* it is not already duplicated *)
-        && not (Datatype.String.Set.mem vi.vname unduplicable_functions)
-        (* it is duplicable *)
-        && not (self#is_variadic_function vi)
-        (* it is not a variadic function *)
-        && not (Misc.is_library_loc loc) (* it is not in the E-ACSL's RTL *)
-        && not (Misc.is_fc_or_compiler_builtin vi) (* it is not a built-in *)
-        &&
-        (let kf =
-           try Globals.Functions.get vi with Not_found -> assert false
-         in
-         not (Functions.instrument kf)
-         (* either explicitely listed as to be not instrumented *)
-         ||
-         (* or: *)
-         (not (Cil.is_empty_funspec
-                 (Annotations.funspec ~populate:false
-                    (Extlib.the self#current_kf)))
-          (* it has a function contract *)
-          && Functions.check kf
-          (* its annotations must be monitored *)))
-      ->
-      self#next ();
-      let name = Functions.RTL.mk_gen_name vi.vname in
-      let new_vi = Cil.makeGlobalVar name vi.vtype in
-      Cil_datatype.Varinfo.Hashtbl.add fct_tbl vi new_vi;
-      Cil.DoChildrenPost
-        (fun l -> match l with
-           | [ GVarDecl(vi, _) | GFunDecl(_, vi, _) | GFun({ svar = vi }, _)
-               as g ]
-             ->
-             (match g with
-              | GFunDecl _ ->
-                let kf = Extlib.the self#current_kf in
-                if not (Kernel_function.is_definition kf)
-                && vi.vname <> "malloc" && vi.vname <> "free"
-                then
-                  Options.warning "@[annotating undefined function `%a':@ \
-                                   the generated program may miss memory \
-                                   instrumentation@ if there are \
-                                   memory-related annotations.@]"
-                    Printer.pp_varinfo vi
-              | GFun _ -> ()
-              | _ -> assert false);
-             let tmp = vi.vname in
-             if tmp = "main" then begin
-               (* the new function becomes the new main: *)
-               (* 1. swap the name of both functions *)
-               vi.vname <- new_vi.vname;
-               new_vi.vname <- tmp;
-               (* 2. force recomputation of the entry point if necessary *)
-               if Kernel.MainFunction.get () = tmp then begin
-                 let selection =
-                   State_selection.with_dependencies Kernel.MainFunction.self
-                 in
-                 Project.clear ~selection ()
-               end
-               (* 3. recompute what is necessary in [Globals.Functions]:
-                  done in [dup_global] *)
-             end;
-             let kf =
-               try
-                 Globals.Functions.get vi
-               with Not_found ->
-                 Options.fatal
-                   "unknown function `%s' while trying to duplicate it"
-                   vi.vname
-             in
-             let spec = Annotations.funspec ~populate:false kf in
-             let new_g, new_decl =
-               dup_global
-                 loc
-                 self#get_filling_actions
-                 spec
-                 self#behavior
-                 sound_verdict_vi
-                 kf
-                 vi
-                 new_vi
-             in
-             (* postpone the introduction of the new function definition to the
-                end *)
-             new_definitions <- new_g :: new_definitions;
-             (* put the declaration before the original function in order to
-                solve issue with recursive functions *)
-             [ new_decl; g ]
-           | _ -> assert false)
-    | GVarDecl(_, loc) | GFunDecl(_, _, loc) | GFun(_, loc)
-      when Misc.is_library_loc loc ->
-      (match before_memory_model with
-       | Before_gmp -> before_memory_model <- Gmpz
-       | Gmpz | Memory_model -> ()
-       | After_gmp -> before_memory_model <- Memory_model
-       | Code -> () (* still processing the GMP and memory model headers,
-                       but reading some libc code *));
-      Cil.DoChildren
-    | GVarDecl(vi, _) | GFunDecl(_, vi, _) | GFun({ svar = vi }, _)
-      when Misc.is_fc_or_compiler_builtin vi ->
-      self#next ();
-      Cil.DoChildren
-    | _ ->
-      self#next ();
-      Cil.DoChildren
-
-  method !vfile f =
-    Cil.DoChildrenPost
-      (fun _ ->
-         match new_definitions with
-         | [] -> f
-         | _ :: _ ->
-           (* add the generated definitions of libc at the end of [l]. This way,
-              we are sure that they have access to all of it (in particular, the
-              memory model, GMP and the soundness variable). Also add the
-              [__e_acsl_sound_verdict] variable at the beginning *)
-           let new_globals =
-             GVarDecl(sound_verdict_vi, Cil_datatype.Location.unknown)
-             :: f.globals
-             @ new_definitions
-           in
-           f.globals <- new_globals;
-           f)
-
-  initializer
-    reset ()
-
-end
-
-let dup () =
-  Options.feedback ~level:2 "duplicating annotated functions";
-  Visitor.visitFramacFile (new dup_functions_visitor) (Ast.get ());
-  Queue.iter (fun f -> f ()) actions;
-  Ast.mark_as_grown ()
-
-(*
-Local Variables:
-compile-command: "make -C ../../../../.."
-End:
-*)
diff --git a/src/plugins/e-acsl/src/project_initializer/dup_functions.mli b/src/plugins/e-acsl/src/project_initializer/dup_functions.mli
deleted file mode 100644
index 2d8dfd48d782fd2ff6f4592487099279928fe554..0000000000000000000000000000000000000000
--- a/src/plugins/e-acsl/src/project_initializer/dup_functions.mli
+++ /dev/null
@@ -1,29 +0,0 @@
-(**************************************************************************)
-(*                                                                        *)
-(*  This file is part of the Frama-C's E-ACSL plug-in.                    *)
-(*                                                                        *)
-(*  Copyright (C) 2012-2019                                               *)
-(*    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).            *)
-(*                                                                        *)
-(**************************************************************************)
-
-val dup: unit -> unit
-
-(*
-Local Variables:
-compile-command: "make"
-End:
-*)
diff --git a/src/plugins/e-acsl/src/project_initializer/prepare_ast.ml b/src/plugins/e-acsl/src/project_initializer/prepare_ast.ml
index d015c335f8d801fc8a70bf8d108e3ff5ecb2eae5..cf20d89e609c207bc514010d6301a8963b83f973 100644
--- a/src/plugins/e-acsl/src/project_initializer/prepare_ast.ml
+++ b/src/plugins/e-acsl/src/project_initializer/prepare_ast.ml
@@ -22,6 +22,222 @@
 
 open Cil_types
 
+let dkey = Options.dkey_prepare
+
+(* ********************************************************************** *)
+(* Environment *)
+(* ********************************************************************** *)
+
+let fct_tbl: unit Kernel_function.Hashtbl.t = Kernel_function.Hashtbl.create 7
+
+let actions = Queue.create ()
+
+(* global table for ensuring that logic info are not shared between a function
+   definition and its duplicate *)
+module Global: sig
+  val add_logic_info: logic_info -> unit
+  val mem_logic_info: logic_info -> bool
+  val reset: unit -> unit
+end = struct
+
+  let tbl = Cil_datatype.Logic_info.Hashtbl.create 7
+  let add_logic_info x = Cil_datatype.Logic_info.Hashtbl.add tbl x ()
+  let mem_logic_info x = Cil_datatype.Logic_info.Hashtbl.mem tbl x
+  let reset () = Cil_datatype.Logic_info.Hashtbl.clear tbl
+
+end
+
+let reset () =
+  Kernel_function.Hashtbl.clear fct_tbl;
+  Global.reset ();
+  Queue.clear actions
+
+(* ********************************************************************** *)
+(* Duplicating functions *)
+(* ********************************************************************** *)
+
+let dup_funspec tbl bhv spec =
+  (*  Options.feedback "DUP SPEC %a" Cil.d_funspec spec;*)
+  let o = object
+    inherit Cil.genericCilVisitor bhv
+
+    val already_visited = Cil_datatype.Logic_var.Hashtbl.create 7
+
+    method !vlogic_info_use li =
+      if Global.mem_logic_info li then
+        Cil.ChangeDoChildrenPost
+          ({ li with l_var_info = li.l_var_info } (* force a copy *),
+           Visitor_behavior.Get.logic_info bhv)
+      else
+        Cil.JustCopy
+
+    method !vterm_offset _ =
+      Cil.DoChildrenPost
+        (function (* no way to directly visit fieldinfo and model_info uses *)
+          | TField(fi, off) ->
+            TField(Visitor_behavior.Get.fieldinfo bhv fi, off)
+          | TModel(mi, off) ->
+            TModel(Visitor_behavior.Get.model_info bhv mi, off)
+          | off ->
+            off)
+
+    method !vlogic_var_use orig_lvi =
+      match orig_lvi.lv_origin with
+      | None ->
+        Cil.JustCopy
+      | Some vi ->
+        try
+          let new_lvi =
+            Cil_datatype.Logic_var.Hashtbl.find already_visited orig_lvi
+          in
+          Cil.ChangeTo new_lvi
+        with Not_found ->
+          Cil.ChangeDoChildrenPost
+            ({ orig_lvi with lv_id = orig_lvi.lv_id } (* force a copy *),
+             fun lvi ->
+               (* using [Visitor_behavior.Get.logic_var bhv lvi] is correct only
+                  because the lv_id used to compare the lvi does not change
+                  between the original one and this copy *)
+               try
+                 let new_vi = Cil_datatype.Varinfo.Hashtbl.find tbl vi in
+                 Cil_datatype.Logic_var.Hashtbl.add
+                   already_visited orig_lvi lvi;
+                 lvi.lv_id <- new_vi.vid;
+                 lvi.lv_name <- new_vi.vname;
+                 lvi.lv_origin <- Some new_vi;
+                 new_vi.vlogic_var_assoc <- Some lvi;
+                 lvi
+               with Not_found ->
+                 assert vi.vglob;
+                 Visitor_behavior.Get.logic_var bhv lvi)
+
+    method !videntified_term _ =
+      Cil.DoChildrenPost Logic_const.refresh_identified_term
+
+    method !videntified_predicate _ =
+      Cil.DoChildrenPost Logic_const.refresh_predicate
+  end in
+  Cil.visitCilFunspec o spec
+
+let dup_fundec loc spec bhv sound_verdict_vi kf vi new_vi =
+  new_vi.vdefined <- true;
+  let formals = Kernel_function.get_formals kf in
+  let mk_formal vi =
+    let name =
+      if vi.vname = "" then
+        (* unnamed formal parameter: must generate a fresh name since a fundec
+           cannot have unnamed formals (see bts #2303). *)
+        Varname.get
+          ~scope:Varname.Function
+          (Functions.RTL.mk_gen_name "unamed_formal")
+      else
+        vi.vname
+    in
+    Cil.copyVarinfo vi name
+  in
+  let new_formals = List.map mk_formal formals in
+  let res =
+    let ty = Kernel_function.get_return_type kf in
+    if Cil.isVoidType ty then None
+    else Some (Cil.makeVarinfo false false ~referenced:true "__retres" ty)
+  in
+  let return =
+    Cil.mkStmt ~valid_sid:true
+      (Return(Extlib.opt_map (Cil.evar ~loc) res, loc))
+  in
+  let stmts =
+    let l =
+      [ Cil.mkStmtOneInstr ~valid_sid:true
+          (Call(Extlib.opt_map Cil.var res,
+                Cil.evar ~loc vi,
+                List.map (Cil.evar ~loc) new_formals,
+                loc));
+        return ]
+    in
+    if Functions.instrument kf then
+      l
+    else
+      (* set the 'unsound_verdict' variable to 'false' whenever required *)
+      let unsound =
+        Cil.mkStmtOneInstr ~valid_sid:true
+          (Set((Var sound_verdict_vi, NoOffset), Cil.zero ~loc, loc))
+      in
+      unsound :: l
+  in
+  let locals = match res with None -> [] | Some r -> [ r ] in
+  let body = Cil.mkBlock stmts in
+  body.blocals <- locals;
+  let tbl = Cil_datatype.Varinfo.Hashtbl.create 7 in
+  List.iter2 (Cil_datatype.Varinfo.Hashtbl.add tbl) formals new_formals;
+  let new_spec = dup_funspec tbl bhv spec in
+  let fundec =
+    { svar = new_vi;
+      sformals = new_formals;
+      slocals = locals;
+      smaxid = List.length new_formals;
+      sbody = body;
+      smaxstmtid = None;
+      sallstmts = [];
+      sspec = new_spec }
+  in
+  (* compute the CFG of the new [fundec] *)
+  Cfg.clearCFGinfo ~clear_id:false fundec;
+  Cfg.cfgFun fundec;
+  fundec
+
+let dup_global loc actions spec bhv sound_verdict_vi kf vi new_vi =
+  let name = vi.vname in
+  Options.feedback ~dkey ~level:2 "entering in function %s" name;
+  let fundec = dup_fundec loc spec bhv sound_verdict_vi kf vi new_vi  in
+  let fct = Definition(fundec, loc) in
+  let new_spec = fundec.sspec in
+  let new_kf = { fundec = fct; spec = new_spec } in
+  Queue.add (fun () ->
+      Kernel_function.Hashtbl.add fct_tbl new_kf ();
+      Globals.Functions.register new_kf;
+      Globals.Functions.replace_by_definition new_spec fundec loc;
+      Annotations.register_funspec new_kf;
+      if new_vi.vname = "main" then begin
+        (* recompute the info about the old main since its name has changed;
+           see the corresponding part in the main visitor *)
+        Globals.Functions.remove vi;
+        Globals.Functions.register kf
+      end)
+    actions;
+  Options.feedback ~dkey ~level:2 "function %s" name;
+  (* remove the specs attached to the previous kf iff it is a definition:
+     it is necessary to keep stable the number of annotations in order to get
+     [Keep_status] working fine. *)
+  let kf = Visitor_behavior.Get.kernel_function bhv kf in
+  if Kernel_function.is_definition kf then begin
+    Queue.add
+      (fun () ->
+         let bhvs =
+           Annotations.fold_behaviors (fun e b acc -> (e, b) :: acc) kf []
+         in
+         List.iter
+           (fun (e, b) -> Annotations.remove_behavior ~force:true e kf b)
+           bhvs;
+         Annotations.iter_decreases
+           (fun e _ -> Annotations.remove_decreases e kf)
+           kf;
+         Annotations.iter_terminates
+           (fun e _ -> Annotations.remove_terminates e kf)
+           kf;
+         Annotations.iter_complete
+           (fun e l -> Annotations.remove_complete e kf l)
+           kf;
+         Annotations.iter_disjoint
+           (fun e l -> Annotations.remove_disjoint e kf l)
+           kf)
+      actions
+  end;
+  GFun(fundec, loc), GFunDecl(new_spec, new_vi, loc)
+
+(* ********************************************************************** *)
+(* Alignment *)
+(* ********************************************************************** *)
+
 exception Alignment_error of string
 let align_error s = raise (Alignment_error s)
 
@@ -64,39 +280,69 @@ let sufficiently_aligned attrs algn =
 let require_alignment typ attrs algn =
   Cil.bitsSizeOf typ < algn*8 && not (sufficiently_aligned attrs algn)
 
+(* ********************************************************************** *)
+(* Visitor *)
+(* ********************************************************************** *)
+
+type position = Before_gmp | Gmpz | After_gmp | Memory_model | Code
+
 class prepare_visitor = object (self)
   inherit Visitor.frama_c_inplace
 
   val mutable has_new_stmt_in_fundec = false
-  val mutable has_new_stmt = false
+
+  (* ---------------------------------------------------------------------- *)
+  (* visitor's local variable *)
+  (* ---------------------------------------------------------------------- *)
+
   val terms = Misc.Id_term.Hashtbl.create 7
+  (* table for ensuring absence of term sharing *)
 
-  method !vterm _t =
-    Cil.DoChildrenPost
-      (fun t ->
-         if Misc.Id_term.Hashtbl.mem terms t then
-           (* remove term sharing for soundness of E-ACSL typing
-              (see typing.ml) *)
-           { t with term_node = t.term_node }
-         else
-           t)
+  val unduplicable_functions =
+    let white_list =
+      [ "__builtin_va_arg";
+        "__builtin_va_end";
+        "__builtin_va_start";
+        "__builtin_va_copy" ]
+    in
+    List.fold_left
+      (fun acc s -> Datatype.String.Set.add s acc)
+      Datatype.String.Set.empty
+      white_list
 
-  (* Add align attributes to local variables (required by temporal analysis) *)
-  method !vblock _ =
-    if Options.Temporal_validity.get () then
-      Cil.DoChildrenPost
-        (fun blk ->
-           List.iter
-             (fun vi ->
-                (* 4 bytes alignment is required to allow sufficient space for
-                   storage of 32-bit timestamps in a 1:1 shadow. *)
-                if require_alignment vi.vtype vi.vattr 4 then begin
-                  vi.vattr <- Attr("aligned", [ AInt Integer.four ]) :: vi.vattr
-                end)
-             blk.blocals;
-           blk)
-    else
-      Cil.DoChildren
+  val fct_tbl = Cil_datatype.Varinfo.Hashtbl.create 7
+  val mutable before_memory_model = Before_gmp
+  val mutable new_definitions: global list = []
+  (* new definitions of the annotated functions which will contain the
+     translation of the E-ACSL contract *)
+
+  val mutable sound_verdict_vi =
+    let name = Functions.RTL.mk_api_name "sound_verdict" in
+    let vi = Cil.makeGlobalVar name Cil.intType in
+    vi.vstorage <- Extern;
+    vi.vreferenced <- true;
+    vi
+
+  (* ---------------------------------------------------------------------- *)
+  (* visitor's private methods *)
+  (* ---------------------------------------------------------------------- *)
+
+  method private is_variadic_function vi =
+    match Cil.unrollType vi.vtype with
+    | TFun(_, _, variadic, _) -> variadic
+    | _ -> true
+
+  method private before_memory_model = match before_memory_model with
+    | Before_gmp | Gmpz | After_gmp -> true
+    | Memory_model | Code -> false
+
+  method private next () =
+    match before_memory_model with
+    | Before_gmp -> ()
+    | Gmpz -> before_memory_model <- After_gmp
+    | After_gmp -> ()
+    | Memory_model -> before_memory_model <- Code
+    | Code -> ()
 
   (* IMPORTANT: for keeping property statuses, we must preserve the ordering of
      translation, see function [Translate.translate_pre_spec] and
@@ -195,6 +441,51 @@ class prepare_visitor = object (self)
     | APragma _
     | AExtended _ -> ()
 
+  (* ---------------------------------------------------------------------- *)
+  (* visitor's method overloading *)
+  (* ---------------------------------------------------------------------- *)
+
+  method !vlogic_info_decl li =
+    Global.add_logic_info li;
+    Cil.SkipChildren
+
+  method !vvrbl vi =
+    try
+      let new_vi = Cil_datatype.Varinfo.Hashtbl.find fct_tbl vi in
+      (* replace functions at callsite by its duplicated version *)
+      Cil.ChangeTo new_vi
+    with Not_found ->
+      Cil.SkipChildren
+
+  method !vterm _t =
+    Cil.DoChildrenPost
+      (fun t ->
+         if Misc.Id_term.Hashtbl.mem terms t then
+           (* remove term sharing for soundness of E-ACSL typing
+              (see typing.ml) *)
+           { t with term_node = t.term_node }
+         else begin
+           Misc.Id_term.Hashtbl.add terms t ();
+           t
+         end)
+
+  (* Add align attributes to local variables (required by temporal analysis) *)
+  method !vblock _ =
+    if Options.Temporal_validity.get () then
+      Cil.DoChildrenPost
+        (fun blk ->
+           List.iter
+             (fun vi ->
+                (* 4 bytes alignment is required to allow sufficient space for
+                   storage of 32-bit timestamps in a 1:1 shadow. *)
+                if require_alignment vi.vtype vi.vattr 4 then begin
+                  vi.vattr <- Attr("aligned", [ AInt Integer.four ]) :: vi.vattr
+                end)
+             blk.blocals;
+           blk)
+    else
+      Cil.DoChildren
+
   method !vstmt_aux stmt =
     Annotations.iter_code_annot
       (fun _ a -> self#push_pre_code_annot a)
@@ -221,7 +512,6 @@ class prepare_visitor = object (self)
     Cil.DoChildrenPost
       (fun _ ->
          if has_new_stmt_in_fundec then begin
-           has_new_stmt <- true;
            has_new_stmt_in_fundec <- false;
            (* recompute the CFG *)
            Cfg.clearCFGinfo ~clear_id:false fundec;
@@ -229,38 +519,152 @@ class prepare_visitor = object (self)
          end;
          fundec)
 
-  method private is_unvariadic_function vi =
-    match Cil.unrollType vi.vtype with
-    | TFun(_, _, variadic, _) -> not variadic
-    | _ -> false
-
   method !vglob_aux = function
+    | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc)
+      when (* duplicate a function iff: *)
+        not (Cil_datatype.Varinfo.Hashtbl.mem fct_tbl vi)
+        (* it is not already duplicated *)
+        && not (Datatype.String.Set.mem vi.vname unduplicable_functions)
+        (* it is duplicable *)
+        && not (self#is_variadic_function vi)
+        (* it is not a variadic function *)
+        && not (Misc.is_library_loc loc) (* it is not in the E-ACSL's RTL *)
+        && not (Misc.is_fc_or_compiler_builtin vi) (* it is not a built-in *)
+        &&
+        (let kf =
+           try Globals.Functions.get vi with Not_found -> assert false
+         in
+         not (Functions.instrument kf)
+         (* either explicitely listed as to be not instrumented *)
+         ||
+         (* or: *)
+         (not (Cil.is_empty_funspec
+                 (Annotations.funspec ~populate:false
+                    (Extlib.the self#current_kf)))
+          (* it has a function contract *)
+          && Functions.check kf
+          (* its annotations must be monitored *)))
+      ->
+      self#next ();
+      let name = Functions.RTL.mk_gen_name vi.vname in
+      let new_vi = Cil.makeGlobalVar name vi.vtype in
+      Cil_datatype.Varinfo.Hashtbl.add fct_tbl vi new_vi;
+      Cil.DoChildrenPost
+        (fun l -> match l with
+           | [ GFunDecl(_, vi, _) | GFun({ svar = vi }, _) as g ]
+             ->
+             let kf = Extlib.the self#current_kf in
+             (match g with
+              | GFunDecl _ ->
+                if not (Kernel_function.is_definition kf)
+                && vi.vname <> "malloc" && vi.vname <> "free"
+                then
+                  Options.warning "@[annotating undefined function `%a':@ \
+                                   the generated program may miss memory \
+                                   instrumentation@ if there are \
+                                   memory-related annotations.@]"
+                    Printer.pp_varinfo vi
+              | GFun _ -> ()
+              | _ -> assert false);
+             let spec = Annotations.funspec ~populate:false kf in
+             self#push_pre_spec spec;
+             self#push_post_spec spec;
+             let tmp = vi.vname in
+             if tmp = "main" then begin
+               (* the new function becomes the new main: *)
+               (* 1. swap the name of both functions *)
+               vi.vname <- new_vi.vname;
+               new_vi.vname <- tmp;
+               (* 2. force recomputation of the entry point if necessary *)
+               if Kernel.MainFunction.get () = tmp then begin
+                 let selection =
+                   State_selection.with_dependencies Kernel.MainFunction.self
+                 in
+                 Project.clear ~selection ()
+               end
+               (* 3. recompute what is necessary in [Globals.Functions]:
+                  done in [dup_global] *)
+             end;
+             let new_g, new_decl =
+               dup_global
+                 loc
+                 self#get_filling_actions
+                 spec
+                 self#behavior
+                 sound_verdict_vi
+                 kf
+                 vi
+                 new_vi
+             in
+             (* postpone the introduction of the new function definition to the
+                end *)
+             new_definitions <- new_g :: new_definitions;
+             (* put the declaration before the original function in order to
+                solve issue with recursive functions *)
+             [ new_decl; g ]
+           | _ -> assert false)
+
+    | GVarDecl(_, loc) | GFunDecl(_, _, loc) | GFun(_, loc)
+      when Misc.is_library_loc loc ->
+      (match before_memory_model with
+       | Before_gmp -> before_memory_model <- Gmpz
+       | Gmpz | Memory_model -> ()
+       | After_gmp -> before_memory_model <- Memory_model
+       | Code -> () (* still processing the GMP and memory model headers,
+                       but reading some libc code *));
+      Cil.DoChildren
+
+    | GVarDecl(vi, _) | GFunDecl(_, vi, _) | GFun({ svar = vi }, _)
+      when Misc.is_fc_or_compiler_builtin vi ->
+      self#next ();
+      Cil.DoChildren
+
     | GVarDecl(vi, loc) | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc)
-      when self#is_unvariadic_function vi
-        && not (Misc.is_library_loc loc)
-        && not (Misc.is_fc_or_compiler_builtin vi)
+      when not (self#is_variadic_function vi)
       ->
+      assert (* handle by the 2 cases above *)
+        (not (Misc.is_library_loc loc || Misc.is_fc_or_compiler_builtin vi));
       let kf = Extlib.the self#current_kf in
       let s = Annotations.funspec ~populate:false kf in
+      self#next ();
       Cil.DoChildrenPost
         (fun f ->
            self#push_pre_spec s;
            self#push_post_spec s;
            f)
+
     | _ ->
+      self#next ();
       Cil.DoChildren
 
-  method !vfile _ =
+  method !vfile f =
     Cil.DoChildrenPost
-      (fun f ->
-         if has_new_stmt then Ast.mark_as_grown ();
-         f)
+      (fun _ ->
+         match new_definitions with
+         | [] -> f
+         | _ :: _ ->
+           (* add the generated definitions of libc at the end of [l]. This way,
+              we are sure that they have access to all of it (in particular, the
+              memory model, GMP and the soundness variable). Also add the
+              [__e_acsl_sound_verdict] variable at the beginning *)
+           let new_globals =
+             GVarDecl(sound_verdict_vi, Cil_datatype.Location.unknown)
+             :: f.globals
+             @ new_definitions
+           in
+           f.globals <- new_globals;
+           f)
+
+  initializer
+    reset ()
 
 end
 
 let prepare () =
   Options.feedback ~level:2 "prepare AST for E-ACSL transformations";
-  Visitor.visitFramacFileSameGlobals (new prepare_visitor) (Ast.get ())
+  Visitor.visitFramacFile (new prepare_visitor) (Ast.get ());
+  Queue.iter (fun f -> f ()) actions;
+  Ast.mark_as_grown ()
 
 (*
 Local Variables:
diff --git a/src/plugins/e-acsl/src/project_initializer/prepare_ast.mli b/src/plugins/e-acsl/src/project_initializer/prepare_ast.mli
index b23a84d6295060acf5d0d6e10fbf0dab760d033a..f9739a57ffa76851d44680d227dc146fff7bc05c 100644
--- a/src/plugins/e-acsl/src/project_initializer/prepare_ast.mli
+++ b/src/plugins/e-acsl/src/project_initializer/prepare_ast.mli
@@ -23,11 +23,12 @@
 (** Prepare AST for E-ACSL generation.
 
     So for this module performs the following tasks:
-    - remove term sharing
-    - move declarations of variables declared in the bodies of switch
+    - generating a new definition for functions with contract;
+    - removing term sharing;
+    - moving declarations of variables declared in the bodies of switch
       statements to upper scopes;
-    - store what is necessary to translate in [Keep_status]
-    - in case of temporal validity checks, add the attribute "aligned" to
+    - storing what is necessary to translate in [Keep_status]
+    - in case of temporal validity checks, adding the attribute "aligned" to
     variables that are not sufficiently aligned. *)
 
 val prepare: unit -> unit