diff --git a/headers/header_spec.txt b/headers/header_spec.txt
index 41c9799d728213bcd1d3af67d98a09370c1b046b..e29fccb1861e052928775f85ed2c0ff0d9c17eee 100644
--- a/headers/header_spec.txt
+++ b/headers/header_spec.txt
@@ -933,6 +933,8 @@ src/plugins/occurrence/register_gui.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/builtin/basic_blocks.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/builtin/basic_blocks.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/builtin/Builtin.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/builtin/builtin_builder.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/builtin/builtin_builder.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/builtin/configure.ac: CEA_LGPL_OR_PROPRIETARY
 src/plugins/builtin/Makefile.in: CEA_LGPL_OR_PROPRIETARY
 src/plugins/builtin/memcmp.ml: CEA_LGPL_OR_PROPRIETARY
diff --git a/src/plugins/builtin/Makefile.in b/src/plugins/builtin/Makefile.in
index 702ddb08ba8a3e8e3f600c81ecec9af5adc6572d..c2c0f929c77c68ea118e7390ce3270ca12144c4f 100644
--- a/src/plugins/builtin/Makefile.in
+++ b/src/plugins/builtin/Makefile.in
@@ -37,7 +37,9 @@ PLUGIN_DIR ?= .
 PLUGIN_ENABLE := @ENABLE_BUILTIN@
 PLUGIN_NAME := Builtin
 PLUGIN_CMI := 
-PLUGIN_CMO := basic_blocks options transform memcpy memcmp memmove register
+PLUGIN_CMO := basic_blocks options builtin_builder transform \
+			memcpy memcmp memmove memset \
+			register
 PLUGIN_DISTRIBUTED := $(PLUGIN_ENABLE)
 PLUGIN_DISTRIB_EXTERNAL:= Makefile.in configure.ac configure
 #PLUGIN_NO_DEFAULT_TEST := no
diff --git a/src/plugins/builtin/builtin_builder.ml b/src/plugins/builtin/builtin_builder.ml
new file mode 100644
index 0000000000000000000000000000000000000000..12299a7886d7ea054856da8d0b1030bcbf2bc3fb
--- /dev/null
+++ b/src/plugins/builtin/builtin_builder.ml
@@ -0,0 +1,110 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-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
+open Basic_blocks
+
+module type Generator_sig = sig
+  module Hashtbl: Datatype.Hashtbl
+  type override_key = Hashtbl.key
+
+  val function_name: string
+  val well_typed_call: exp list -> bool
+  val key_from_call: exp list -> override_key
+  val retype_args: override_key -> exp list -> exp list
+  val generate_prototype: override_key -> (string * typ)
+  val generate_spec: override_key -> fundec -> location -> funspec
+  val args_for_original: override_key -> fundec -> exp list
+end
+
+module type Builtin = sig
+  module Enabled: Parameter_sig.Bool
+  type override_key
+
+  val function_name: string
+  val well_typed_call: exp list -> bool
+  val key_from_call: exp list -> override_key
+  val retype_args: override_key -> exp list -> exp list
+  val get_override: override_key -> fundec
+  val get_kfs: unit -> kernel_function list
+  val mark_as_computed:  ?project:Project.t -> unit -> unit
+end
+
+
+let build_body caller callee args_generator =
+  let open Extlib in
+  let loc  = Cil_datatype.Location.unknown in
+  let ret_var = match Cil.getReturnType caller.svar.vtype with
+    | t when Cil.isVoidType t -> None
+    | t -> Some (Cil.makeLocalVar caller "__retres" t)
+  in
+  let call =
+    let args = args_generator caller in
+    Cil.mkStmt (Instr(call_function (opt_map Cil.var ret_var) callee args))
+  in
+  let ret = Cil.mkStmt (Return ( (opt_map Cil.evar ret_var), loc)) in
+  { (Cil.mkBlock [call ; ret]) with blocals = list_of_opt ret_var }
+
+module Make_builtin (G: Generator_sig) = struct
+  include G
+  module Enabled = Options.NewBuiltin (G)
+  module Cache = State_builder.Hashtbl (G.Hashtbl) (Cil_datatype.Fundec)
+      (struct
+        let size = 5
+        let dependencies = []
+        let name = "Builtins." ^ G.function_name
+      end)
+
+  let make_fundec key =
+    let open Globals.Functions in
+    let (name, typ) = G.generate_prototype key in
+    let fd = Basic_blocks.prepare_definition name typ in
+    let orig = get_vi (find_by_name function_name) in
+    let sbody = build_body fd orig (G.args_for_original key) in
+    let fd = { fd with sbody } in
+    Cfg.cfgFun fd ;
+    fd
+
+  let build_function key =
+    let loc = Cil_datatype.Location.unknown in
+    let fd = make_fundec key in
+    let spec = Cil.empty_funspec () in
+    Globals.Functions.replace_by_definition spec fd loc ;
+    let kf = Globals.Functions.get fd.svar in
+    let spec = generate_spec key fd loc in
+    Annotations.add_behaviors Options.emitter kf spec.spec_behavior ;
+    fd
+
+  let get_override key =
+    try
+      Cache.find key
+    with Not_found ->
+      let fd = build_function key in
+      Cache.add key fd ;
+      fd
+
+  (* If you use this before finalization, you'll have problems *)
+  let get_kfs () =
+    Cache.fold (fun _ fd l -> Globals.Functions.get fd.svar :: l) []
+
+  let mark_as_computed = Cache.mark_as_computed
+end
diff --git a/src/plugins/builtin/builtin_builder.mli b/src/plugins/builtin/builtin_builder.mli
new file mode 100644
index 0000000000000000000000000000000000000000..f45a623f37010f689ff20d2b54a371b2ebb7733a
--- /dev/null
+++ b/src/plugins/builtin/builtin_builder.mli
@@ -0,0 +1,51 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-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
+
+module type Generator_sig = sig
+  module Hashtbl: Datatype.Hashtbl
+  type override_key = Hashtbl.key
+
+  val function_name: string
+  val well_typed_call: exp list -> bool
+  val key_from_call: exp list -> override_key
+  val retype_args: override_key -> exp list -> exp list
+  val generate_prototype: override_key -> (string * typ)
+  val generate_spec: override_key -> fundec -> location -> funspec
+  val args_for_original: override_key -> fundec -> exp list
+end
+
+module type Builtin = sig
+  module Enabled: Parameter_sig.Bool
+  type override_key
+
+  val function_name: string
+  val well_typed_call: exp list -> bool
+  val key_from_call: exp list -> override_key
+  val retype_args: override_key -> exp list -> exp list
+  val get_override: override_key -> fundec
+  val get_kfs: unit -> kernel_function list
+  val mark_as_computed:  ?project:Project.t -> unit -> unit
+end
+
+module Make_builtin (G: Generator_sig) : Builtin
diff --git a/src/plugins/builtin/memcmp.ml b/src/plugins/builtin/memcmp.ml
index a5b8bea26479d901ef87f27a1da06dfc8a2194f7..370347fe47383603950bee6e558543c343cf436d 100644
--- a/src/plugins/builtin/memcmp.ml
+++ b/src/plugins/builtin/memcmp.ml
@@ -125,4 +125,4 @@ let () = Transform.register (module struct
     let generate_prototype = generate_prototype
     let generate_spec = generate_spec
     let args_for_original = args_for_original
-  end)
\ No newline at end of file
+  end)
diff --git a/src/plugins/builtin/memcpy.ml b/src/plugins/builtin/memcpy.ml
index 717c7009ae1390c71b69016b9a20b9883d6ae5d2..2c5674a0c4e915761528ba86b40a83b5b437b4d6 100644
--- a/src/plugins/builtin/memcpy.ml
+++ b/src/plugins/builtin/memcpy.ml
@@ -129,4 +129,4 @@ let () = Transform.register (module struct
     let generate_prototype = generate_prototype
     let generate_spec = generate_spec
     let args_for_original = args_for_original
-  end)
\ No newline at end of file
+  end)
diff --git a/src/plugins/builtin/memmove.ml b/src/plugins/builtin/memmove.ml
index e5b7b6f7ff2640fae0e98409d3d207d0d14aed89..99e4dee3078c2d7aa5192090db288b3c8d8cf29d 100644
--- a/src/plugins/builtin/memmove.ml
+++ b/src/plugins/builtin/memmove.ml
@@ -124,4 +124,4 @@ let () = Transform.register (module struct
     let generate_prototype = generate_prototype
     let generate_spec = generate_spec
     let args_for_original = args_for_original
-  end)
\ No newline at end of file
+  end)
diff --git a/src/plugins/builtin/options.mli b/src/plugins/builtin/options.mli
index bf1d0244b37b5cb2ea30725cf9c0fa517d6d4bc9..3ff7f8a32bc1d63f0d7f8994e199420e27ba3aa1 100644
--- a/src/plugins/builtin/options.mli
+++ b/src/plugins/builtin/options.mli
@@ -25,7 +25,7 @@ include Plugin.General_services
 module Enabled : Parameter_sig.Bool
 module Kfs : Parameter_sig.Kernel_function_set
 
-module NewBuiltin 
+module NewBuiltin
     (B : sig val function_name: string end) : Parameter_sig.Bool
 
 val emitter: Emitter.t
diff --git a/src/plugins/builtin/tests/functions/memmove.c b/src/plugins/builtin/tests/functions/memmove.c
index d97eda5defe9355cb1c26465802bb516bf00b390..7a17026266d3fe21d3af5411715e868baa97c7c1 100644
--- a/src/plugins/builtin/tests/functions/memmove.c
+++ b/src/plugins/builtin/tests/functions/memmove.c
@@ -7,6 +7,6 @@ int main(void){
   int *p = memmove(dest, src, sizeof(src));
 }
 
-int* nested(int (*dest) [10], int (*src) [10], size_t n){
+void nested(int (*dest) [10], int (*src) [10], size_t n) {
   memmove(dest, src, n) ;
 }
\ No newline at end of file
diff --git a/src/plugins/builtin/tests/functions/oracle/memcmp.res.oracle b/src/plugins/builtin/tests/functions/oracle/memcmp.res.oracle
index 15722b108753d340b9ec61cfe67acf2a2ffdb761..77e86fe5a03fe77a5a9665dd6784e0bf69e3b447 100644
--- a/src/plugins/builtin/tests/functions/oracle/memcmp.res.oracle
+++ b/src/plugins/builtin/tests/functions/oracle/memcmp.res.oracle
@@ -3,33 +3,6 @@
 #include "stddef.h"
 #include "string.h"
 #include "strings.h"
-/*@ requires aligned_end: len % 40 ≡ 0;
-    requires
-      valid_read_s1:
-        \let __fc_len = len / 40; \valid_read(s1 + (0 .. __fc_len - 1));
-    requires
-      valid_read_s2:
-        \let __fc_len = len / 40; \valid_read(s2 + (0 .. __fc_len - 1));
-    ensures
-      equals:
-        \let __fc_len = len / 40;
-          \result ≡ 0 ⇔
-          (∀ ℤ j0;
-             0 ≤ j0 < __fc_len ⇒
-             (∀ ℤ j1;
-                0 ≤ j1 < 10 ⇒ (*(s1 + j0))[j1] ≡ (*(s2 + j0))[j1]));
-    assigns \result;
-    assigns \result
-      \from (indirect: (*(s1 + (0 .. len - 1)))[0 .. 10 - 1]),
-            (indirect: (*(s2 + (0 .. len - 1)))[0 .. 10 - 1]);
- */
-int memcmp_arr10_int(int const (*s1)[10], int const (*s2)[10], size_t len)
-{
-  int __retres;
-  __retres = memcmp((void const *)s1,(void const *)s2,len);
-  return __retres;
-}
-
 /*@ requires aligned_end: len % 4 ≡ 0;
     requires
       valid_read_s1:
@@ -64,24 +37,6 @@ int main(void)
   return __retres;
 }
 
-int nested(int (*dest)[10], int (*src)[10], size_t n)
-{
-  int tmp;
-  tmp = memcmp_arr10_int(dest,src,n);
-  return tmp;
-}
-
-
-[kernel] Parsing tests/functions/result/memcmp.c (with preprocessing)
-[kernel] Parsing tests/functions/memcmp.c (with preprocessing)
-[kernel] tests/functions/memcmp.c:3: Warning: 
-  def'n of func main at tests/functions/memcmp.c:3 (sum 2855) conflicts with the one at tests/functions/result/memcmp.c:56 (sum 4629); keeping the one at tests/functions/result/memcmp.c:56.
-[kernel] tests/functions/memcmp.c:10: Warning: 
-  def'n of func nested at tests/functions/memcmp.c:10 (sum 1087) conflicts with the one at tests/functions/result/memcmp.c:66 (sum 1974); keeping the one at tests/functions/result/memcmp.c:66.
-/* Generated by Frama-C */
-#include "stddef.h"
-#include "string.h"
-#include "strings.h"
 /*@ requires aligned_end: len % 40 ≡ 0;
     requires
       valid_read_s1:
@@ -91,13 +46,12 @@ int nested(int (*dest)[10], int (*src)[10], size_t n)
         \let __fc_len = len / 40; \valid_read(s2 + (0 .. __fc_len - 1));
     ensures
       equals:
-        \let __fc_len = \old(len) / 40;
+        \let __fc_len = len / 40;
           \result ≡ 0 ⇔
           (∀ ℤ j0;
              0 ≤ j0 < __fc_len ⇒
              (∀ ℤ j1;
-                0 ≤ j1 < 10 ⇒
-                (*(\old(s1) + j0))[j1] ≡ (*(\old(s2) + j0))[j1]));
+                0 ≤ j1 < 10 ⇒ (*(s1 + j0))[j1] ≡ (*(s2 + j0))[j1]));
     assigns \result;
     assigns \result
       \from (indirect: (*(s1 + (0 .. len - 1)))[0 .. 10 - 1]),
@@ -110,6 +64,24 @@ int memcmp_arr10_int(int const (*s1)[10], int const (*s2)[10], size_t len)
   return __retres;
 }
 
+int nested(int (*dest)[10], int (*src)[10], size_t n)
+{
+  int tmp;
+  tmp = memcmp_arr10_int(dest,src,n);
+  return tmp;
+}
+
+
+[kernel] Parsing tests/functions/result/memcmp.c (with preprocessing)
+[kernel] Parsing tests/functions/memcmp.c (with preprocessing)
+[kernel] tests/functions/memcmp.c:3: Warning: 
+  def'n of func main at tests/functions/memcmp.c:3 (sum 2855) conflicts with the one at tests/functions/result/memcmp.c:29 (sum 4629); keeping the one at tests/functions/result/memcmp.c:29.
+[kernel] tests/functions/memcmp.c:10: Warning: 
+  def'n of func nested at tests/functions/memcmp.c:10 (sum 1087) conflicts with the one at tests/functions/result/memcmp.c:66 (sum 1974); keeping the one at tests/functions/result/memcmp.c:66.
+/* Generated by Frama-C */
+#include "stddef.h"
+#include "string.h"
+#include "strings.h"
 /*@ requires aligned_end: len % 4 ≡ 0;
     requires
       valid_read_s1:
@@ -145,6 +117,34 @@ int main(void)
   return __retres;
 }
 
+/*@ requires aligned_end: len % 40 ≡ 0;
+    requires
+      valid_read_s1:
+        \let __fc_len = len / 40; \valid_read(s1 + (0 .. __fc_len - 1));
+    requires
+      valid_read_s2:
+        \let __fc_len = len / 40; \valid_read(s2 + (0 .. __fc_len - 1));
+    ensures
+      equals:
+        \let __fc_len = \old(len) / 40;
+          \result ≡ 0 ⇔
+          (∀ ℤ j0;
+             0 ≤ j0 < __fc_len ⇒
+             (∀ ℤ j1;
+                0 ≤ j1 < 10 ⇒
+                (*(\old(s1) + j0))[j1] ≡ (*(\old(s2) + j0))[j1]));
+    assigns \result;
+    assigns \result
+      \from (indirect: (*(s1 + (0 .. len - 1)))[0 .. 10 - 1]),
+            (indirect: (*(s2 + (0 .. len - 1)))[0 .. 10 - 1]);
+ */
+int memcmp_arr10_int(int const (*s1)[10], int const (*s2)[10], size_t len)
+{
+  int __retres;
+  __retres = memcmp((void const *)s1,(void const *)s2,len);
+  return __retres;
+}
+
 int nested(int (*dest)[10], int (*src)[10], size_t n)
 {
   int tmp;
diff --git a/src/plugins/builtin/tests/functions/oracle/memcpy.res.oracle b/src/plugins/builtin/tests/functions/oracle/memcpy.res.oracle
index b9fb245dddd9f70ead802df6a15c651fc922753d..7c8012b7448858d598c0bab558ad7559c994f605 100644
--- a/src/plugins/builtin/tests/functions/oracle/memcpy.res.oracle
+++ b/src/plugins/builtin/tests/functions/oracle/memcpy.res.oracle
@@ -3,37 +3,6 @@
 #include "stddef.h"
 #include "string.h"
 #include "strings.h"
-/*@ requires aligned_end: len % 40 ≡ 0;
-    requires
-      valid_dest:
-        \let __fc_len = len / 40; \valid(dest + (0 .. __fc_len - 1));
-    requires
-      valid_read_src:
-        \let __fc_len = len / 40; \valid_read(src + (0 .. __fc_len - 1));
-    requires
-      separation:
-        \let __fc_len = len / 40;
-          \separated(dest + (0 .. __fc_len - 1), src + (0 .. __fc_len - 1));
-    ensures
-      copied:
-        \let __fc_len = len / 40;
-        ∀ ℤ j0;
-          0 ≤ j0 < __fc_len ⇒
-          (∀ ℤ j1;
-             0 ≤ j1 < 10 ⇒ (*(dest + j0))[j1] ≡ (*(src + j0))[j1]);
-    ensures result: \result ≡ dest;
-    assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1], \result;
-    assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1]
-      \from (*(src + (0 .. len - 1)))[0 .. 10 - 1];
-    assigns \result \from dest;
- */
-int (*memcpy_arr10_int(int (*dest)[10], int const (*src)[10], size_t len))[10]
-{
-  int (*__retres)[10];
-  __retres = (int (*)[10])memcpy((void *)dest,(void const *)src,len);
-  return __retres;
-}
-
 /*@ requires aligned_end: len % 4 ≡ 0;
     requires
       valid_dest: \let __fc_len = len / 4; \valid(dest + (0 .. __fc_len - 1));
@@ -70,23 +39,6 @@ int main(void)
   return __retres;
 }
 
-void nested(int (*dest)[10], int (*src)[10], size_t n)
-{
-  memcpy_arr10_int(dest,src,n);
-  return;
-}
-
-
-[kernel] Parsing tests/functions/result/memcpy.c (with preprocessing)
-[kernel] Parsing tests/functions/memcpy.c (with preprocessing)
-[kernel] tests/functions/memcpy.c:3: Warning: 
-  def'n of func main at tests/functions/memcpy.c:3 (sum 1968) conflicts with the one at tests/functions/result/memcpy.c:62 (sum 3742); keeping the one at tests/functions/result/memcpy.c:62.
-[kernel] tests/functions/memcpy.c:10: Warning: 
-  dropping duplicate def'n of func nested at tests/functions/memcpy.c:10 in favor of that at tests/functions/result/memcpy.c:72
-/* Generated by Frama-C */
-#include "stddef.h"
-#include "string.h"
-#include "strings.h"
 /*@ requires aligned_end: len % 40 ≡ 0;
     requires
       valid_dest:
@@ -100,13 +52,12 @@ void nested(int (*dest)[10], int (*src)[10], size_t n)
           \separated(dest + (0 .. __fc_len - 1), src + (0 .. __fc_len - 1));
     ensures
       copied:
-        \let __fc_len = \old(len) / 40;
+        \let __fc_len = len / 40;
         ∀ ℤ j0;
           0 ≤ j0 < __fc_len ⇒
           (∀ ℤ j1;
-             0 ≤ j1 < 10 ⇒
-             (*(\old(dest) + j0))[j1] ≡ (*(\old(src) + j0))[j1]);
-    ensures result: \result ≡ \old(dest);
+             0 ≤ j1 < 10 ⇒ (*(dest + j0))[j1] ≡ (*(src + j0))[j1]);
+    ensures result: \result ≡ dest;
     assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1], \result;
     assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1]
       \from (*(src + (0 .. len - 1)))[0 .. 10 - 1];
@@ -119,6 +70,23 @@ int (*memcpy_arr10_int(int (*dest)[10], int const (*src)[10], size_t len))[10]
   return __retres;
 }
 
+void nested(int (*dest)[10], int (*src)[10], size_t n)
+{
+  memcpy_arr10_int(dest,src,n);
+  return;
+}
+
+
+[kernel] Parsing tests/functions/result/memcpy.c (with preprocessing)
+[kernel] Parsing tests/functions/memcpy.c (with preprocessing)
+[kernel] tests/functions/memcpy.c:3: Warning: 
+  def'n of func main at tests/functions/memcpy.c:3 (sum 1968) conflicts with the one at tests/functions/result/memcpy.c:31 (sum 3742); keeping the one at tests/functions/result/memcpy.c:31.
+[kernel] tests/functions/memcpy.c:10: Warning: 
+  dropping duplicate def'n of func nested at tests/functions/memcpy.c:10 in favor of that at tests/functions/result/memcpy.c:72
+/* Generated by Frama-C */
+#include "stddef.h"
+#include "string.h"
+#include "strings.h"
 /*@ requires aligned_end: len % 4 ≡ 0;
     requires
       valid_dest: \let __fc_len = len / 4; \valid(dest + (0 .. __fc_len - 1));
@@ -156,6 +124,38 @@ int main(void)
   return __retres;
 }
 
+/*@ requires aligned_end: len % 40 ≡ 0;
+    requires
+      valid_dest:
+        \let __fc_len = len / 40; \valid(dest + (0 .. __fc_len - 1));
+    requires
+      valid_read_src:
+        \let __fc_len = len / 40; \valid_read(src + (0 .. __fc_len - 1));
+    requires
+      separation:
+        \let __fc_len = len / 40;
+          \separated(dest + (0 .. __fc_len - 1), src + (0 .. __fc_len - 1));
+    ensures
+      copied:
+        \let __fc_len = \old(len) / 40;
+        ∀ ℤ j0;
+          0 ≤ j0 < __fc_len ⇒
+          (∀ ℤ j1;
+             0 ≤ j1 < 10 ⇒
+             (*(\old(dest) + j0))[j1] ≡ (*(\old(src) + j0))[j1]);
+    ensures result: \result ≡ \old(dest);
+    assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1], \result;
+    assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1]
+      \from (*(src + (0 .. len - 1)))[0 .. 10 - 1];
+    assigns \result \from dest;
+ */
+int (*memcpy_arr10_int(int (*dest)[10], int const (*src)[10], size_t len))[10]
+{
+  int (*__retres)[10];
+  __retres = (int (*)[10])memcpy((void *)dest,(void const *)src,len);
+  return __retres;
+}
+
 void nested(int (*dest)[10], int (*src)[10], size_t n)
 {
   memcpy_arr10_int(dest,(int const (*)[10])src,n);
diff --git a/src/plugins/builtin/tests/functions/oracle/memmove.res.oracle b/src/plugins/builtin/tests/functions/oracle/memmove.res.oracle
index fc95dbda3e23ea98d3165a67ec5d44493d56a740..e6c64844a253de24791d81c903ecf9da780c3fa9 100644
--- a/src/plugins/builtin/tests/functions/oracle/memmove.res.oracle
+++ b/src/plugins/builtin/tests/functions/oracle/memmove.res.oracle
@@ -1,37 +1,8 @@
 [kernel] Parsing tests/functions/memmove.c (with preprocessing)
-[kernel] tests/functions/memmove.c:11: Warning: 
-  Body of function nested falls-through. Adding a return statement
 /* Generated by Frama-C */
 #include "stddef.h"
 #include "string.h"
 #include "strings.h"
-/*@ requires aligned_end: len % 40 ≡ 0;
-    requires
-      valid_dest:
-        \let __fc_len = len / 40; \valid(dest + (0 .. __fc_len - 1));
-    requires
-      valid_read_src:
-        \let __fc_len = len / 40; \valid_read(src + (0 .. __fc_len - 1));
-    ensures
-      moved:
-        \let __fc_len = len / 40;
-        ∀ ℤ j0;
-          0 ≤ j0 < __fc_len ⇒
-          (∀ ℤ j1;
-             0 ≤ j1 < 10 ⇒ (*(dest + j0))[j1] ≡ (*(src + j0))[j1]);
-    ensures result: \result ≡ dest;
-    assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1], \result;
-    assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1]
-      \from (*(src + (0 .. len - 1)))[0 .. 10 - 1];
-    assigns \result \from dest;
- */
-int (*memmove_arr10_int(int (*dest)[10], int const (*src)[10], size_t len))[10]
-{
-  int (*__retres)[10];
-  __retres = (int (*)[10])memmove((void *)dest,(void const *)src,len);
-  return __retres;
-}
-
 /*@ requires aligned_end: len % 4 ≡ 0;
     requires
       valid_dest: \let __fc_len = len / 4; \valid(dest + (0 .. __fc_len - 1));
@@ -64,28 +35,6 @@ int main(void)
   return __retres;
 }
 
-int *nested(int (*dest)[10], int (*src)[10], size_t n)
-{
-  int *__retres;
-  memmove_arr10_int(dest,src,n);
-  /*@ assert missing_return: \false; */ ;
-  __retres = (int *)0;
-  return __retres;
-}
-
-
-[kernel] Parsing tests/functions/result/memmove.c (with preprocessing)
-[kernel] Parsing tests/functions/memmove.c (with preprocessing)
-[kernel] tests/functions/memmove.c:11: Warning: 
-  Body of function nested falls-through. Adding a return statement
-[kernel] tests/functions/memmove.c:3: Warning: 
-  def'n of func main at tests/functions/memmove.c:3 (sum 1968) conflicts with the one at tests/functions/result/memmove.c:54 (sum 3742); keeping the one at tests/functions/result/memmove.c:54.
-[kernel] tests/functions/memmove.c:10: Warning: 
-  def'n of func nested at tests/functions/memmove.c:10 (sum 1974) conflicts with the one at tests/functions/result/memmove.c:64 (sum 4635); keeping the one at tests/functions/result/memmove.c:64.
-/* Generated by Frama-C */
-#include "stddef.h"
-#include "string.h"
-#include "strings.h"
 /*@ requires aligned_end: len % 40 ≡ 0;
     requires
       valid_dest:
@@ -95,13 +44,12 @@ int *nested(int (*dest)[10], int (*src)[10], size_t n)
         \let __fc_len = len / 40; \valid_read(src + (0 .. __fc_len - 1));
     ensures
       moved:
-        \let __fc_len = \old(len) / 40;
+        \let __fc_len = len / 40;
         ∀ ℤ j0;
           0 ≤ j0 < __fc_len ⇒
           (∀ ℤ j1;
-             0 ≤ j1 < 10 ⇒
-             (*(\old(dest) + j0))[j1] ≡ (*(\old(src) + j0))[j1]);
-    ensures result: \result ≡ \old(dest);
+             0 ≤ j1 < 10 ⇒ (*(dest + j0))[j1] ≡ (*(src + j0))[j1]);
+    ensures result: \result ≡ dest;
     assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1], \result;
     assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1]
       \from (*(src + (0 .. len - 1)))[0 .. 10 - 1];
@@ -114,6 +62,23 @@ int (*memmove_arr10_int(int (*dest)[10], int const (*src)[10], size_t len))[10]
   return __retres;
 }
 
+void nested(int (*dest)[10], int (*src)[10], size_t n)
+{
+  memmove_arr10_int(dest,src,n);
+  return;
+}
+
+
+[kernel] Parsing tests/functions/result/memmove.c (with preprocessing)
+[kernel] Parsing tests/functions/memmove.c (with preprocessing)
+[kernel] tests/functions/memmove.c:3: Warning: 
+  def'n of func main at tests/functions/memmove.c:3 (sum 1968) conflicts with the one at tests/functions/result/memmove.c:27 (sum 3742); keeping the one at tests/functions/result/memmove.c:27.
+[kernel] tests/functions/memmove.c:10: Warning: 
+  dropping duplicate def'n of func nested at tests/functions/memmove.c:10 in favor of that at tests/functions/result/memmove.c:64
+/* Generated by Frama-C */
+#include "stddef.h"
+#include "string.h"
+#include "strings.h"
 /*@ requires aligned_end: len % 4 ≡ 0;
     requires
       valid_dest: \let __fc_len = len / 4; \valid(dest + (0 .. __fc_len - 1));
@@ -147,13 +112,38 @@ int main(void)
   return __retres;
 }
 
-int *nested(int (*dest)[10], int (*src)[10], size_t n)
+/*@ requires aligned_end: len % 40 ≡ 0;
+    requires
+      valid_dest:
+        \let __fc_len = len / 40; \valid(dest + (0 .. __fc_len - 1));
+    requires
+      valid_read_src:
+        \let __fc_len = len / 40; \valid_read(src + (0 .. __fc_len - 1));
+    ensures
+      moved:
+        \let __fc_len = \old(len) / 40;
+        ∀ ℤ j0;
+          0 ≤ j0 < __fc_len ⇒
+          (∀ ℤ j1;
+             0 ≤ j1 < 10 ⇒
+             (*(\old(dest) + j0))[j1] ≡ (*(\old(src) + j0))[j1]);
+    ensures result: \result ≡ \old(dest);
+    assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1], \result;
+    assigns (*(dest + (0 .. len - 1)))[0 .. 10 - 1]
+      \from (*(src + (0 .. len - 1)))[0 .. 10 - 1];
+    assigns \result \from dest;
+ */
+int (*memmove_arr10_int(int (*dest)[10], int const (*src)[10], size_t len))[10]
+{
+  int (*__retres)[10];
+  __retres = (int (*)[10])memmove((void *)dest,(void const *)src,len);
+  return __retres;
+}
+
+void nested(int (*dest)[10], int (*src)[10], size_t n)
 {
-  int *__retres;
   memmove_arr10_int(dest,(int const (*)[10])src,n);
-  /*@ assert missing_return: \false; */ ;
-  __retres = (int *)0;
-  return __retres;
+  return;
 }
 
 
diff --git a/src/plugins/builtin/transform.ml b/src/plugins/builtin/transform.ml
index a14e9360266f1d0be691214b54d4eac01a1e6acc..87f5677b0b395c8a6c1fec053c60999e3717aa85 100644
--- a/src/plugins/builtin/transform.ml
+++ b/src/plugins/builtin/transform.ml
@@ -21,148 +21,97 @@
 (**************************************************************************)
 
 open Cil_types
-open Basic_blocks
-
-module type Builtin = sig
-  module Hashtbl: Datatype.Hashtbl
-  type override_key = Hashtbl.key
-
-  val function_name: string
-  val well_typed_call: exp list -> bool
-  val key_from_call: exp list -> override_key
-  val retype_args: override_key -> exp list -> exp list
-  val generate_prototype: override_key -> (string * typ)
-  val generate_spec: override_key -> fundec -> location -> funspec
-  val args_for_original: override_key -> fundec -> exp list
-end
+open Builtin_builder
 
-module type Internal_builtin = sig
-  include Builtin
-  module Enabled: Parameter_sig.Bool
-  val get_override: override_key -> fundec
-  val get_globals: location -> global list
-  val mark_as_computed:  ?project:Project.t -> unit -> unit
-end
+let base : (string, (module Builtin)) Hashtbl.t = Hashtbl.create 17
 
-module Make_internal_builtin (B: Builtin) = struct
-  include B
-  module Enabled = Options.NewBuiltin (B)
-  module Cache = State_builder.Hashtbl (B.Hashtbl) (Cil_datatype.Fundec)
-      (struct
-        let size = 5
-        let dependencies = [Ast.self]
-        let name = "Builtins." ^ B.function_name
-      end)
-
-  let create_and_add key =
-    let (name, typ) = B.generate_prototype key in
-    let fd = Basic_blocks.prepare_definition name typ in
-    let loc  = Cil_datatype.Location.unknown in
-    let open Globals.Functions in
-    let open Extlib in
-    let ret_var = match Cil.getReturnType fd.svar.vtype with
-      | t when Cil.isVoidType t -> None
-      | t -> Some (Cil.makeLocalVar fd "__retres" t)
-    in
-    let call =
-      let orig = get_vi (find_by_name function_name) in
-      let args = B.args_for_original key fd in
-      Instr(call_function (opt_map Cil.var ret_var) orig args)
-    in
-    let ret = Return ( (opt_map Cil.evar ret_var), loc) in
-    fd.sbody <-
-      { (Cil.mkBlock (List.map Cil.mkStmt [ call ; ret ])) 
-        with blocals = list_of_opt ret_var } ;
-    File.must_recompute_cfg fd ;
-    Cache.add key fd
-
-  let get_override key =
-    try
-      Cache.find key
-    with Not_found ->
-      create_and_add key ;
-      Cache.find key
-
-  let get_globals location =
-    let finalize key fd =
-      let spec = B.generate_spec key fd location in
-      Globals.Functions.replace_by_definition spec fd location ;
-      Cil_types.GFun(fd, location)
-    in
-    Cache.fold (fun k vi l -> (finalize k vi) :: l) []
-
-  let mark_as_computed = Cache.mark_as_computed
-end
-
-let base : (string, (module Internal_builtin)) Hashtbl.t = Hashtbl.create 17
-
-let register (module B: Builtin) =
-  let module Internal_builtin = Make_internal_builtin(B) in
-  Hashtbl.add base B.function_name (module Internal_builtin)
+let register (module G: Generator_sig) =
+  let module Builtin = Make_builtin(G) in
+  Hashtbl.add base G.function_name (module Builtin)
 
 let mark_as_computed () =
   let mark_as_computed _ builtin =
-    let module B = (val builtin: Internal_builtin) in B.mark_as_computed ()
+    let module B = (val builtin: Builtin) in B.mark_as_computed ()
   in
   Hashtbl.iter mark_as_computed base
 
-let get_globals loc =
-  let get_globals _ builtin =
-    let module B = (val builtin: Internal_builtin) in B.get_globals loc
+let get_kfs () =
+  let get_kfs _ builtin =
+    let module B = (val builtin: Builtin) in
+    let res = B.get_kfs () in
+    res
   in
-  Hashtbl.fold (fun k v l -> (get_globals k v) @ l) base []
+  Hashtbl.fold (fun k v l -> (get_kfs k v) @ l) base []
 
 let find_stdlib_attr fct =
   if not (Cil.hasAttribute "fc_stdlib" fct.vattr) then raise Not_found
 
-let replace_call (fct, args) =
-  try
-    find_stdlib_attr fct ;
-    let builtin = Hashtbl.find base fct.vname in
-    let module B = (val builtin: Internal_builtin) in
-    if B.well_typed_call args then
-      let key = B.key_from_call args in
-      let fundec = B.get_override key in
-      let new_args = B.retype_args key args in
-      (fundec.svar), new_args
-    else begin
-      Options.warning ~current:true "Ignore call: not well typed" ;
-      (fct, args)
-    end
-  with
-  | Not_found -> (fct, args)
-
-class visitor = object(_)
+module VISet = Cil_datatype.Varinfo.Hptset
+
+class transformer = object(self)
   inherit Visitor.frama_c_inplace
 
+  val introduced_builtins = ref VISet.empty
+  val used_builtin_last_kf = Queue.create ()
+
   method! vfile _ =
-    let after f =
-      let loc = Cil.CurrentLoc.get() in
-      let globals = get_globals loc in
-      f.globals <- globals @ f.globals ;
-      mark_as_computed () ;
+    let post f =
       Ast.mark_as_changed () ;
       Ast.mark_as_grown () ;
-      File.reorder_ast () ;
+      mark_as_computed () ;
       f
     in
-    Cil.DoChildrenPost after
+    Cil.DoChildrenPost post
+
+  method! vglob_aux _ =
+    let post g =
+      let loc = Cil.CurrentLoc.get() in
+      let folding l fd =
+        if VISet.mem fd.svar !introduced_builtins then l
+        else begin
+          introduced_builtins := VISet.add fd.svar !introduced_builtins ;
+          GFun (fd, loc) :: l
+        end
+      in
+      Queue.fold folding g used_builtin_last_kf
+    in
+    Cil.DoChildrenPost post
 
   method! vfunc f =
     let kf = Globals.Functions.get f.svar in
-    if Options.Kfs.is_empty () || Options.Kfs.mem kf then
+    if not (Options.Kfs.is_set ()) || Options.Kfs.mem kf then
       Cil.DoChildren
     else
       Cil.SkipChildren
 
+  method private replace_call (fct, args) =
+    try
+      find_stdlib_attr fct ;
+      let builtin = Hashtbl.find base fct.vname in
+      let module B = (val builtin: Builtin) in
+      if B.Enabled.get () then
+        if B.well_typed_call args then
+          let key = B.key_from_call args in
+          let fundec = B.get_override key in
+          let new_args = B.retype_args key args in
+          Queue.add fundec used_builtin_last_kf ;
+          (fundec.svar), new_args
+        else begin
+          Options.warning ~current:true "Ignore call: not well typed" ;
+          (fct, args)
+        end
+      else (fct, args)
+    with
+    | Not_found -> (fct, args)
+
   method! vinst = function
     | Call(_) | Local_init(_, ConsInit(_, _, Plain_func), _) ->
       let change = function
         | [ Call(r, ({ enode = Lval((Var f), NoOffset) } as e), args, loc) ] ->
-          let f, args = replace_call (f, args) in
+          let f, args = self#replace_call (f, args) in
           [ Call(r, { e with enode = Lval((Var f), NoOffset) }, args, loc) ]
         | [ Local_init(r, ConsInit(f, args, Plain_func), loc) ] ->
-          let f, args = replace_call (f, args) in
+          let f, args = self#replace_call (f, args) in
           [ Local_init(r, ConsInit(f, args, Plain_func), loc) ]
         | _ -> assert false
       in
@@ -170,10 +119,42 @@ class visitor = object(_)
     | _ -> Cil.DoChildren
 end
 
-let transform file =
-  let filter _ b =
-    let module B = (val b : Internal_builtin) in
-    if B.Enabled.get () then Some b else None
+let validate_property p =
+  Property_status.emit Options.emitter ~hyps:[] p Property_status.True
+
+let compute_preconditions_statuses kf =
+  let stmt = Kernel_function.find_first_stmt kf in
+  let _ = Kernel_function.find_all_enclosing_blocks stmt in
+  match stmt.skind with
+  | Instr (Call(_, { enode = Lval ((Var fct), NoOffset) }, _, _)) ->
+    let called_kf = Globals.Functions.get fct in
+    Statuses_by_call.setup_all_preconditions_proxies called_kf ;
+    let reqs = Statuses_by_call.all_call_preconditions_at
+        ~warn_missing:true called_kf stmt
+    in
+    List.iter (fun (_, p) -> validate_property p) reqs ;
+  | _ -> assert false
+
+let compute_statuses_all_calls () =
+  let kfs = get_kfs () in
+  List.iter compute_preconditions_statuses kfs ;
+
+  let module Kfs = Kernel_function.Hptset in
+  let open Property in
+  let kfs = List.fold_left (fun s kf -> Kfs.add kf s) Kfs.empty kfs in
+  let validate_if_builtin_post ip =
+    match ip with
+    (* Constracts of generated functions *)
+    | IPPredicate { ip_kf=kf ; ip_kind=PKEnsures _ }
+    | IPAssigns { ias_kf=kf }
+    | IPFrom { if_kf=kf }
+      when Kfs.mem kf kfs ->
+      validate_property ip
+    | _ -> ()
   in
-  Hashtbl.filter_map_inplace filter base ;
-  Visitor.visitFramacFile (new visitor) file
+  Property_status.iter validate_if_builtin_post
+
+
+let transform file =
+  Visitor.visitFramacFile (new transformer) file ;
+  compute_statuses_all_calls ()
diff --git a/src/plugins/builtin/transform.mli b/src/plugins/builtin/transform.mli
index e5876ab7a520906e99bce092e1fbfa0cc1d0e5e6..10e265d87196c9c5bc6d29d81a6c1510c321ebc0 100644
--- a/src/plugins/builtin/transform.mli
+++ b/src/plugins/builtin/transform.mli
@@ -20,21 +20,5 @@
 (*                                                                        *)
 (**************************************************************************)
 
-open Cil_types
-
-module type Builtin = sig
-  module Hashtbl: Datatype.Hashtbl
-  type override_key = Hashtbl.key
-
-  val function_name: string
-  val well_typed_call: exp list -> bool
-  val key_from_call: exp list -> override_key
-  val retype_args: override_key -> exp list -> exp list
-  val generate_prototype: override_key -> (string * typ)
-  val generate_spec: override_key -> fundec -> location -> funspec
-  val args_for_original: override_key -> fundec -> exp list
-end
-
-val register: (module Builtin) -> unit
-
+val register: (module Builtin_builder.Generator_sig) -> unit
 val transform: Cil_types.file -> unit