From 2d8ce6e0106ba44b840608b40cdcf541ab7c4ab0 Mon Sep 17 00:00:00 2001
From: Allan Blanchard <allan.blanchard@cea.fr>
Date: Wed, 18 Sep 2024 10:46:08 +0200
Subject: [PATCH] [wp] add the MemByte model

---
 src/plugins/wp/MemAddr.ml  |    1 +
 src/plugins/wp/MemAddr.mli |   10 +
 src/plugins/wp/MemBytes.ml | 1122 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1133 insertions(+)
 create mode 100644 src/plugins/wp/MemBytes.ml

diff --git a/src/plugins/wp/MemAddr.ml b/src/plugins/wp/MemAddr.ml
index 8c8dd1fcb4c..cbda49a4c97 100644
--- a/src/plugins/wp/MemAddr.ml
+++ b/src/plugins/wp/MemAddr.ml
@@ -34,6 +34,7 @@ let library = "memaddr"
 
 let t_addr = Qed.Logic.Data(Lang.datatype ~library "addr",[])
 let t_table = Qed.Logic.Data(Lang.datatype ~library "table",[])
+let t_malloc = Qed.Logic.Array(Qed.Logic.Int, Qed.Logic.Int)
 
 let f_base   = Lang.extern_f ~library ~result:Qed.Logic.Int
     ~link:(Qed.Engine.F_subst ("base", "%1.base")) "base"
diff --git a/src/plugins/wp/MemAddr.mli b/src/plugins/wp/MemAddr.mli
index 93685f13772..482a6d876a7 100644
--- a/src/plugins/wp/MemAddr.mli
+++ b/src/plugins/wp/MemAddr.mli
@@ -32,6 +32,16 @@ open Lang.F
 val t_addr : Lang.tau
 (** Pointer type : addr *)
 
+val t_malloc : Lang.tau
+(** Allocation table : [int[base] -> int]
+
+    Note: this is not a binding with the symbol in the theory but the exact same
+    definition, we need this so that Qed knows the underlying type.
+
+    @since Frama-C+dev
+*)
+
+
 (** {2 Basic constructors} *)
 
 val base : term -> term
diff --git a/src/plugins/wp/MemBytes.ml b/src/plugins/wp/MemBytes.ml
new file mode 100644
index 00000000000..2c83da1b35a
--- /dev/null
+++ b/src/plugins/wp/MemBytes.ml
@@ -0,0 +1,1122 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of WP plug-in of Frama-C.                           *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat a l'energie atomique et aux energies              *)
+(*         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 Lang.F
+open Sigs
+open Ctypes
+
+module Logic = Qed.Logic
+
+(* Why3 symbols *)
+
+module Why3 = struct
+  let library = "membytes"
+
+  let t_vblock = Qed.Logic.Array (Qed.Logic.Int, Qed.Logic.Int)
+  let t_memory = Qed.Logic.Array (Qed.Logic.Int,t_vblock)
+  let t_iblock = Qed.Logic.Array (Qed.Logic.Int, Qed.Logic.Bool)
+  let t_init = Qed.Logic.Array   (Qed.Logic.Int,t_iblock)
+
+  let ty_fst_arg = function
+    | Some l :: _ -> l
+    | _ -> raise Not_found
+  let l_havoc = Qed.Engine.F_call "havoc"
+  let f_havoc = Lang.extern_f ~library ~typecheck:ty_fst_arg ~link:l_havoc "havoc"
+  let havoc fresh current loc n =
+    Lang.F.e_fun f_havoc [fresh;current;loc;n]
+
+  let p_cinits = Lang.extern_fp ~coloring:true ~library "cinits"
+  let cinits m = p_call p_cinits [m]
+  let p_sconst = Lang.extern_fp ~coloring:true ~library "sconst"
+  let sconst m = p_call p_sconst [m]
+  let p_eqmem = Lang.extern_fp ~library "eqmem"
+  let eqmem m1 m2 a size = p_call p_eqmem [ m1 ; m2 ; a ; size ]
+  let p_is_init_range = Lang.extern_fp ~library "is_init_range"
+  let is_init_range m a size = p_call p_is_init_range [ m ; a ; size ]
+  let f_set_init_range = Lang.extern_fp ~library "set_init_range"
+  let set_init_range m a size = e_fun f_set_init_range [ m ; a ; size ]
+
+  let ty_fst_arg_val = function
+    | Some (Qed.Logic.Array (_, Qed.Logic.Array (_, t))) :: _ -> t
+    | _ -> raise Not_found
+
+  let f_raw_get = Lang.extern_f ~typecheck:ty_fst_arg_val ~library "raw_get"
+  let raw_get m a = e_fun f_raw_get [ m ; a ]
+  let f_raw_set = Lang.extern_f ~typecheck:ty_fst_arg ~library "raw_set"
+  let raw_set m a v = e_fun f_raw_set [ m ; a ; v]
+
+  let p_bytes = Lang.extern_fp ~library "bytes"
+  let bytes m = p_call p_bytes [ m ]
+
+  (* read/write *)
+  let f_read_uint8 = Lang.extern_f ~result:Qed.Logic.Int ~library "read_uint8"
+  let read_uint8 m a = e_fun f_read_uint8 [ m ; a ]
+  let f_read_uint16 = Lang.extern_f ~result:Qed.Logic.Int ~library "read_uint16"
+  let read_uint16 m a = e_fun f_read_uint16 [ m ; a ]
+  let f_read_uint32 = Lang.extern_f ~result:Qed.Logic.Int ~library "read_uint32"
+  let read_uint32 m a = e_fun f_read_uint32 [ m ; a ]
+  let f_read_uint64 = Lang.extern_f ~result:Qed.Logic.Int ~library "read_uint64"
+  let read_uint64 m a = e_fun f_read_uint64 [ m ; a ]
+  let f_read_sint8 = Lang.extern_f ~result:Qed.Logic.Int ~library "read_sint8"
+  let read_sint8 m a = e_fun f_read_sint8 [ m ; a ]
+  let f_read_sint16 = Lang.extern_f ~result:Qed.Logic.Int ~library "read_sint16"
+  let read_sint16 m a = e_fun f_read_sint16 [ m ; a ]
+  let f_read_sint32 = Lang.extern_f ~result:Qed.Logic.Int ~library "read_sint32"
+  let read_sint32 m a = e_fun f_read_sint32 [ m ; a ]
+  let f_read_sint64 = Lang.extern_f ~result:Qed.Logic.Int ~library "read_sint64"
+  let read_sint64 m a = e_fun f_read_sint64 [ m ; a ]
+  let f_write_uint8 = Lang.extern_f ~result:t_memory ~library "write_uint8"
+  let write_uint8 m a v = e_fun f_write_uint8 [ m ; a ; v ]
+  let f_write_uint16 = Lang.extern_f ~result:t_memory ~library "write_uint16"
+  let write_uint16 m a v = e_fun f_write_uint16 [ m ; a ; v ]
+  let f_write_uint32 = Lang.extern_f ~result:t_memory ~library "write_uint32"
+  let write_uint32 m a v = e_fun f_write_uint32 [ m ; a ; v ]
+  let f_write_uint64 = Lang.extern_f ~result:t_memory ~library "write_uint64"
+  let write_uint64 m a v = e_fun f_write_uint64 [ m ; a ; v ]
+  let f_write_sint8 = Lang.extern_f ~result:t_memory ~library "write_sint8"
+  let write_sint8 m a v = e_fun f_write_sint8 [ m ; a ; v ]
+  let f_write_sint16 = Lang.extern_f ~result:t_memory ~library "write_sint16"
+  let write_sint16 m a v = e_fun f_write_sint16 [ m ; a ; v ]
+  let f_write_sint32 = Lang.extern_f ~result:t_memory ~library "write_sint32"
+  let write_sint32 m a v = e_fun f_write_sint32 [ m ; a ; v ]
+  let f_write_sint64 = Lang.extern_f ~result:t_memory ~library "write_sint64"
+  let write_sint64 m a v = e_fun f_write_sint64 [ m ; a ; v ]
+
+  (* init *)
+  let f_read_init8 = Lang.extern_f ~result:Qed.Logic.Bool ~library "read_init8"
+  let read_init8 m a = e_fun f_read_init8 [ m ; a ]
+  let f_read_init16 = Lang.extern_f ~result:Qed.Logic.Bool ~library "read_init16"
+  let read_init16 m a = e_fun f_read_init16 [ m ; a ]
+  let f_read_init32 = Lang.extern_f ~result:Qed.Logic.Bool ~library "read_init32"
+  let read_init32 m a = e_fun f_read_init32 [ m ; a ]
+  let f_read_init64 = Lang.extern_f ~result:Qed.Logic.Bool ~library "read_init64"
+  let read_init64 m a = e_fun f_read_init64 [ m ; a ]
+  let f_write_init8 = Lang.extern_f ~result:t_init ~library "write_init8"
+  let write_init8 m a v = e_fun f_write_init8 [ m ; a ; v ]
+  let f_write_init16 = Lang.extern_f ~result:t_init ~library "write_init16"
+  let write_init16 m a v = e_fun f_write_init16 [ m ; a ; v ]
+  let f_write_init32 = Lang.extern_f ~result:t_init ~library "write_init32"
+  let write_init32 m a v = e_fun f_write_init32 [ m ; a ; v ]
+  let f_write_init64 = Lang.extern_f ~result:t_init ~library "write_init64"
+  let write_init64 m a v = e_fun f_write_init64 [ m ; a ; v ]
+end
+
+(* Model *)
+let datatype = "MemBytes"
+let lc_name = String.lowercase_ascii datatype
+let dkey_state = Wp_parameters.register_category (lc_name ^ ":state")
+let dkey_model = Wp_parameters.register_category (lc_name ^ ":model")
+
+let configure () =
+  begin
+    let orig_pointer = Context.push Lang.pointer MemAddr.t_addr in
+    let orig_null    = Context.push Cvalues.null (p_equal MemAddr.null) in
+    let rollback () =
+      Context.pop Lang.pointer orig_pointer ;
+      Context.pop Cvalues.null orig_null ;
+    in
+    rollback
+  end
+let no_binder = { bind = fun _ f v -> f v }
+let configure_ia _ = no_binder
+
+let hypotheses p = p
+
+module Chunk =
+struct
+  type t = Mem | Init | Alloc
+  let self = "Chunk" ^ datatype
+  let hash = Hashtbl.hash
+  let equal = (=)
+  let compare c1 c2 =
+    match c1, c2 with
+    | Mem, Mem | Init, Init | Alloc, Alloc -> 0
+    | Mem, _ -> 1
+    | _, Mem -> -1
+    | Init, _ -> 1
+    | _, Init -> -1
+
+  let pretty fmt = function
+    | Mem  -> Format.fprintf fmt "Mem"
+    | Init -> Format.fprintf fmt "Init"
+    | Alloc -> Format.fprintf fmt "Alloc"
+
+  let tau_of_memory = Why3.t_memory
+  let tau_of_init = Why3.t_init
+
+  let tau_of_chunk = function
+    | Mem -> tau_of_memory
+    | Init -> tau_of_init
+    | Alloc -> Logic.Array (Logic.Int, Logic.Int)
+
+  let val_of_chunk = function
+    | Mem -> Logic.Int
+    | Init -> Logic.Bool
+    | Alloc -> Logic.Int
+
+  let basename_of_chunk = function
+    | Mem -> "mem"
+    | Init -> "init"
+    | Alloc -> "alloc"
+
+  let is_framed _ = false
+end
+
+module Heap = Qed.Collection.Make(Chunk)
+module Sigma = Sigma.Make(Chunk)(Heap)
+
+type loc = term
+
+let pretty fmt =
+  Format.fprintf fmt "l:(%a)" pp_term
+
+let vars = vars
+let occurs = occurs
+
+type chunk = Chunk.t
+type sigma = Sigma.t
+type domain = Sigma.domain
+type segment = loc rloc
+
+let comp_cluster () =
+  Definitions.cluster ~id:"Compound" ~title:"Memory Compound Loader" ()
+
+let shift_cluster () =
+  Definitions.cluster ~id:"Shifts" ~title:"Shifts Definitions" ()
+
+
+(* ********************************************************************** *)
+(* SIZE                                                                   *)
+(* ********************************************************************** *)
+
+module OPAQUE_COMP_LENGTH = WpContext.Generator(Cil_datatype.Compinfo)
+    (struct
+      let name = "MemBytes.EmptyCompLength"
+      type key = Cil_types.compinfo
+      type data = Lang.lfun
+      let compile c =
+        if c.Cil_types.cfields <> None then
+          Wp_parameters.fatal
+            "Asking for opaque struct length on non opaque struct" ;
+        let result = Lang.t_int in
+        let size =
+          Lang.generated_f ~params:[] ~result "Length_of_%s" (Lang.comp_id c)
+        in
+        (* Registration *)
+        Definitions.define_symbol {
+          d_cluster = Definitions.compinfo c ;
+          d_lfun = size ; d_types = 0 ; d_params = [] ;
+          d_definition = Logic result ;
+        } ;
+        Definitions.define_lemma {
+          l_kind = Admit ;
+          l_name = "Positive_Length_of_" ^ Lang.comp_id c ;
+          l_triggers = [] ; l_forall = [] ;
+          l_cluster = Definitions.compinfo c ;
+          l_lemma = Lang.F.(p_lt e_zero (e_fun size []))
+        } ;
+        size
+    end)
+
+let protected_sizeof_object = function
+  | C_comp ({ cfields = None } as c) ->
+    e_fun (OPAQUE_COMP_LENGTH.get c) []
+  | obj -> e_int @@ Ctypes.sizeof_object obj
+
+(* ********************************************************************** *)
+(* SHIFT                                                                  *)
+(* ********************************************************************** *)
+
+type shift =
+  | RS_Field of Cil_types.fieldinfo * term (* offset of the field *)
+  | RS_Index of term  (* size of the shift *)
+
+let phi_base = function
+  | p::_ -> MemAddr.base p
+  | _ -> raise Not_found
+
+let phi_field offset = function
+  | [p] -> e_add (MemAddr.offset p) offset
+  | _ -> raise Not_found
+
+let phi_index size = function
+  | [p;k] -> e_add (MemAddr.offset p) (e_mul size k)
+  | _ -> raise Not_found
+
+module RegisterShift = WpContext.Static
+    (struct
+      type key = Lang.lfun
+      type data = shift
+      let name = "MemBytes.RegisterShift"
+      include Lang.Fun
+    end)
+
+let field_offset ci field =
+  let comp = Cil_types.TComp(ci, []) in
+  let field = Cil_types.Field(field, NoOffset) in
+  let bits_offset, bits_size = Cil.bitsOffset comp field in
+  if 0 <> bits_offset mod 8 || 0 <> bits_size mod 8 then
+    Wp_parameters.error "Bitfields not allowed in Bytes model" ;
+  bits_offset / 8
+
+
+module ShiftFieldDef = WpContext.StaticGenerator(Cil_datatype.Fieldinfo)
+    (struct
+      let name = "MemBytes.ShiftFieldDef"
+      type key = Cil_types.fieldinfo
+      type data = Definitions.dfun
+
+      let generate f =
+        let result = MemAddr.t_addr in
+        let lfun = Lang.generated_f ~result "shiftfield_%s" (Lang.field_id f) in
+        (* Since its a generated it is the unique name given *)
+        let p = Lang.freshvar ~basename:"p" MemAddr.t_addr in
+        let tp = e_var p in
+        let position = e_int @@ field_offset f.fcomp f in
+        let def = MemAddr.shift tp position in
+        let dfun = Definitions.Function( result , Def , def) in
+        RegisterShift.define lfun (RS_Field(f,position)) ;
+        MemAddr.register ~base:phi_base ~offset:(phi_field position) lfun ;
+        Definitions.{
+          d_lfun = lfun ; d_types = 0 ;
+          d_params = [p] ;
+          d_definition = dfun ;
+          d_cluster = Definitions.dummy () ;
+        }
+
+      let compile = Lang.local generate
+    end)
+
+module ShiftField = WpContext.Generator(Cil_datatype.Fieldinfo)
+    (struct
+      let name = "MemBytes.ShiftField"
+      type key = Cil_types.fieldinfo
+      type data = Lang.lfun
+      let compile fd =
+        let dfun = ShiftFieldDef.get fd in
+        let d_cluster = shift_cluster () in
+        Definitions.define_symbol { dfun with d_cluster } ;
+        dfun.d_lfun
+    end)
+
+module Cobj =
+struct
+  type t = c_object
+  let pretty = C_object.pretty
+  let compare = compare_ptr_conflated
+end
+
+(* This is a model-independent generator,
+   which will be inherited from the model-dependent clusters *)
+module ShiftGen = WpContext.StaticGenerator(Cobj)
+    (struct
+      let name = "MemBytes.ShiftDef"
+      type key = Cobj.t
+      type data = Definitions.dfun
+
+      let rec c_object_id fmt = function
+        | C_int i -> pp_int fmt i
+        | C_float f -> pp_float fmt f
+        | C_pointer _ -> Format.fprintf fmt "PTR"
+        | C_comp c -> Format.pp_print_string fmt (Lang.comp_id c)
+        | C_array a ->
+          let te = object_of a.arr_element in
+          match a.arr_flat with
+          | None -> Format.fprintf fmt "A_%a" c_object_id te
+          | Some f -> Format.fprintf fmt "A%d_%a" f.arr_size c_object_id te
+
+      let c_object_id c = Format.asprintf "%a@?" c_object_id c
+
+      let generate obj =
+        let result = MemAddr.t_addr in
+        let shift = Lang.generated_f ~result "shift_%s" (c_object_id obj) in
+        let size = protected_sizeof_object obj in
+        (* Since its a generated it is the unique name given *)
+        let p = Lang.freshvar ~basename:"p" MemAddr.t_addr in
+        let tp = e_var p in
+        let k = Lang.freshvar ~basename:"k" Qed.Logic.Int in
+        let tk = e_var k in
+        let def = MemAddr.shift tp (e_mul size tk) in
+        let dfun = Definitions.Function( result , Def , def) in
+        RegisterShift.define shift (RS_Index size) ;
+        MemAddr.register ~base:phi_base ~offset:(phi_index size)
+          ~linear:true shift ;
+        Definitions.{
+          d_lfun = shift ; d_types = 0 ;
+          d_params = [p;k] ;
+          d_definition = dfun ;
+          d_cluster = Definitions.dummy () ;
+        }
+
+      let compile = Lang.local generate
+    end)
+
+(* The model-dependent derivation of model-independent ShiftDef *)
+module Shift = WpContext.Generator(Cobj)
+    (struct
+      let name = "MemBytes.Shift"
+      type key = Cobj.t
+      type data = Lang.lfun
+      let compile obj =
+        let dfun = ShiftGen.get obj in
+        let d_cluster = shift_cluster () in
+        Definitions.define_symbol { dfun with d_cluster } ;
+        dfun.d_lfun
+    end)
+
+let field loc f =
+  Wp_parameters.debug ~level:3 ~dkey:dkey_model
+    "%s.field %a.%a" datatype pretty loc Cil_printer.pp_field f ;
+  e_fun (ShiftField.get f) [loc]
+let shift loc obj k =
+  Wp_parameters.debug ~level:3 ~dkey:dkey_model
+    "%s.shift %a + %a(%a)" datatype pretty loc pp_term k Ctypes.pp_object obj ;
+  e_fun (Shift.get obj) [loc;k]
+
+(* ********************************************************************** *)
+(* VALIDITY and SEPARATION                                                *)
+(* ********************************************************************** *)
+
+let allocated sigma l = e_get (Sigma.value sigma Alloc) (MemAddr.base l)
+
+let s_valid sigma acs p n =
+  let valid = match acs with
+    | RW -> MemAddr.valid_rw
+    | RD -> MemAddr.valid_rd
+    | OBJ -> (fun m p _ -> MemAddr.valid_obj m p)
+  in
+  valid (Sigma.value sigma Alloc) p n
+
+let s_invalid sigma p n =
+  MemAddr.invalid (Sigma.value sigma Alloc) p n
+
+let segment phi = function
+  | Rloc(obj,l) ->
+    phi l @@ protected_sizeof_object obj
+  | Rrange(l,obj,Some a,Some b) ->
+    let l = shift l obj a in
+    let n = e_mul (protected_sizeof_object obj) (e_range a b) in
+    phi l n
+  | Rrange(l,_,a,b) ->
+    Wp_parameters.abort ~current:true
+      "Invalid infinite range @[<hov 2>%a+@,(%a@,..%a)@]"
+      Lang.F.pp_term l Vset.pp_bound a Vset.pp_bound b
+
+let valid sigma acs =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_model "%s.valid _ _" datatype ;
+  segment (s_valid sigma acs)
+let invalid sigma =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_model "%s.invalid _ _" datatype ;
+  segment (s_invalid sigma)
+
+let included =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_model "%s.included _ _" datatype ;
+  let addrof l = l in
+  let sizeof obj = protected_sizeof_object obj in
+  MemAddr.included ~shift ~addrof ~sizeof
+
+let separated =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_model "%s.separated _ _" datatype ;
+  let addrof l = l in
+  let sizeof obj = protected_sizeof_object obj in
+  MemAddr.separated ~shift ~addrof ~sizeof
+
+(* Prepare loader *)
+
+let float_cluster () =
+  Definitions.cluster ~id:"MemBytes.Float" ~title:"MemBytes definitions" ()
+
+module Float = struct
+  type t = Ctypes.c_float
+  let pretty = Ctypes.pp_float
+  let compare = Ctypes.compare_c_float
+  let ikind = function
+    | Float32 -> UInt32
+    | Float64 -> UInt64
+end
+
+module CODEC_FLOAT = WpContext.Generator(Float)
+    (struct
+      let name = "MemBytes.LOAD_FLOAT"
+      type key = Float.t
+      type data = Lang.lfun * Lang.lfun
+
+      let decode ft =
+        let result = Cfloat.tau_of_float ft in
+        let f = Lang.freshvar ~basename:"f" Lang.t_int in
+        let decode =
+          Lang.generated_f ~result "int_to_%a" Float.pretty ft in
+        Definitions.define_symbol {
+          d_lfun = decode ;
+          d_cluster = float_cluster () ; d_types = 0 ;
+          d_params = [ f ] ;
+          d_definition = Logic result ;
+        } ;
+        decode
+
+      let encode ft =
+        let result = Lang.t_int in
+        let f = Lang.freshvar ~basename:"f" @@ Cfloat.tau_of_float ft in
+        let encode =
+          Lang.generated_f ~result "%a_to_int" Float.pretty ft in
+        Definitions.define_symbol {
+          d_lfun = encode ;
+          d_cluster = float_cluster () ; d_types = 0 ;
+          d_params = [ f ] ;
+          d_definition = Logic result ;
+        } ;
+        encode
+
+      let add_decode_encode ft encode decode =
+        let f = Lang.freshvar ~basename:"f" @@ Cfloat.tau_of_float ft in
+        let tf = e_var f in
+        let name = Format.asprintf "decode_encode_%a" Float.pretty ft in
+        let lemma =
+          p_equal tf (e_fun decode [e_fun encode [tf]]) in
+        Definitions.define_lemma {
+          l_kind = Admit ;
+          l_name = name ;
+          l_triggers = [] ;
+          l_forall = [f] ;
+          l_cluster = float_cluster () ;
+          l_lemma = lemma
+        }
+
+      let add_encode_decode ft encode decode =
+        let i = Lang.freshvar ~basename:"i" Lang.t_int in
+        let ti = e_var i in
+        let name = Format.asprintf "encode_decode_%a" Float.pretty ft in
+        let lemma =
+          p_equal ti (e_fun encode [e_fun decode [ti]]) in
+        Definitions.define_lemma {
+          l_kind = Admit ;
+          l_name = name ;
+          l_triggers = [] ;
+          l_forall = [i] ;
+          l_cluster = float_cluster () ;
+          l_lemma = lemma
+        }
+
+      let add_encode_bounds ft encode =
+        let f = Lang.freshvar ~basename:"f" @@ Cfloat.tau_of_float ft in
+        let tf = e_var f in
+        let name = Format.asprintf "encode_bounds_%a" Float.pretty ft in
+        let lemma = Cint.range (Float.ikind ft) @@ e_fun encode [ tf ] in
+        Definitions.define_lemma {
+          l_kind = Admit ;
+          l_name = name ;
+          l_triggers = [] ;
+          l_forall = [f] ;
+          l_cluster = float_cluster () ;
+          l_lemma = lemma
+        }
+
+      let compile ft =
+        let encode = encode ft in
+        let decode = decode ft in
+        add_encode_decode ft encode decode ;
+        add_decode_encode ft encode decode ;
+        add_encode_bounds ft encode ;
+        encode, decode
+    end)
+
+let float_to_int fkind f =
+  e_fun (fst @@ CODEC_FLOAT.get fkind) [ f ]
+
+let int_to_float fkind f =
+  e_fun (snd @@ CODEC_FLOAT.get fkind) [ f ]
+
+let load_int_raw memory kind addr =
+  let read = match kind with
+    | CBool -> Why3.read_uint8
+    | UInt8 -> Why3.read_uint8
+    | SInt8 -> Why3.read_sint8
+    | UInt16 -> Why3.read_uint16
+    | SInt16 -> Why3.read_sint16
+    | UInt32 -> Why3.read_uint32
+    | SInt32 -> Why3.read_sint32
+    | UInt64 -> Why3.read_uint64
+    | SInt64 -> Why3.read_sint64
+  in
+  read memory addr
+
+let load_int sigma kind addr =
+  load_int_raw (Sigma.value sigma Chunk.Mem) kind addr
+
+let load_float sigma kind addr =
+  int_to_float kind @@ load_int sigma (Float.ikind kind) addr
+
+let load_pointer_raw memory _ty loc =
+  MemAddr.addr_of_int @@ load_int_raw memory (Ctypes.c_ptr ()) loc
+
+let load_pointer sigma _ty loc =
+  MemAddr.addr_of_int @@ load_int sigma (Ctypes.c_ptr ()) loc
+
+let load_init memory size loc =
+  match size with
+  | 1 -> Why3.read_init8  memory loc
+  | 2 -> Why3.read_init16 memory loc
+  | 4 -> Why3.read_init32 memory loc
+  | 8 -> Why3.read_init64 memory loc
+  | _ -> assert false
+
+let is_init_atom sigma obj loc =
+  let init_memory = Sigma.value sigma Init in
+  let size = sizeof_object obj in
+  load_init init_memory size loc
+
+let store_int sigma kind addr v =
+  let write = match kind with
+    | CBool -> Why3.write_uint8
+    | UInt8 -> Why3.write_uint8
+    | SInt8 -> Why3.write_sint8
+    | UInt16 -> Why3.write_uint16
+    | SInt16 -> Why3.write_sint16
+    | UInt32 -> Why3.write_uint32
+    | SInt32 -> Why3.write_sint32
+    | UInt64 -> Why3.write_uint64
+    | SInt64 -> Why3.write_sint64
+  in
+  Chunk.Mem, write (Sigma.value sigma Mem) addr v
+
+let store_float sigma kind addr v =
+  store_int sigma (Float.ikind kind) addr @@ float_to_int kind v
+
+let store_pointer sigma _kind addr v =
+  store_int sigma (Ctypes.c_ptr ()) addr @@ MemAddr.int_of_addr v
+
+let store_init_raw m size loc v =
+  let write = match size with
+    | 1 -> Why3.write_init8
+    | 2 -> Why3.write_init16
+    | 4 -> Why3.write_init32
+    | 8 -> Why3.write_init64
+    | _ -> assert false
+  in
+  write m loc v
+
+let set_init_atom sigma obj loc v =
+  let init_memory = Sigma.value sigma Init in
+  let size = sizeof_object obj in
+  Chunk.Init, store_init_raw init_memory size loc v
+
+module Model = struct
+  module Chunk = Chunk
+  module Sigma = Sigma
+
+  let name = "MemBytes.Loader"
+
+  type nonrec loc = loc
+
+  let sizeof = protected_sizeof_object
+  let field = field
+  let shift = shift
+
+  let to_addr l = l
+  let to_region_pointer l = 0,l
+  let of_region_pointer _r _obj l = l
+
+  let value_footprint _ _ = Sigma.Chunk.Set.singleton Chunk.Mem
+  let init_footprint _ _ = Sigma.Chunk.Set.singleton Chunk.Init
+
+  let frames  ~addr:p ~offset:n ?(basename="mem") tau =
+    let t_block = Qed.Logic.Array (Qed.Logic.Int, tau) in
+    let t_mem = Qed.Logic.Array(Qed.Logic.Int, t_block) in
+    let m  = e_var (Lang.freshvar ~basename t_mem) in
+    let m' = e_var (Lang.freshvar ~basename t_mem) in
+    let p' = e_var (Lang.freshvar ~basename:"q" MemAddr.t_addr) in
+    let n' = e_var (Lang.freshvar ~basename:"n" Qed.Logic.Int) in
+    let mh = Why3.havoc m' m p' n' in
+    let v' = e_var (Lang.freshvar ~basename:"v" tau) in
+    let meq = Why3.eqmem m m' p' n' in
+    let diff = p_call MemAddr.p_separated [p;n;p';e_one] in
+    let sep = p_call MemAddr.p_separated [p;n;p';n'] in
+    let inc = p_call MemAddr.p_included [p;n;p';n'] in
+    let teq = Definitions.Trigger.of_pred meq in
+    [
+      "update" , []    , [diff]    , m , Why3.raw_set m p' v' ;
+      "eqmem"  , [teq] , [inc;meq] , m , m' ;
+      "havoc"  , []    , [sep]     , m , mh ;
+    ]
+
+  let frames obj addr = function
+    | Chunk.Alloc -> []
+    | m ->
+      let offset = sizeof obj in
+      let tau = Chunk.val_of_chunk m in
+      let basename = Chunk.basename_of_chunk m in
+      frames ~addr ~offset ~basename tau
+
+  let last sigma obj l =
+    let n = protected_sizeof_object obj in
+    e_sub (e_div (allocated sigma l) n) e_one
+
+  let havoc obj loc ~length chunk ~fresh ~current =
+    if chunk <> Chunk.Alloc then
+      let n = e_mul (e_int @@ sizeof_object obj) length in
+      Why3.havoc fresh current loc n
+    else fresh
+
+  let eqmem obj loc _chunk m1 m2 =
+    Why3.eqmem m1 m2 loc @@ sizeof obj
+
+  let eqmem_forall obj loc _chunk m1 m2 =
+    let xp = Lang.freshvar ~basename:"p" MemAddr.t_addr in
+    let p = e_var xp in
+    let addrof l = l in
+    let separated =
+      MemAddr.separated
+        ~shift ~addrof ~sizeof (Rloc (C_int UInt8, p)) (Rloc (obj, loc))
+    in
+    let equal = p_equal (Why3.raw_get m1 p) (Why3.raw_get m2 p) in
+    [xp],separated,equal
+
+  let load_int = load_int
+  let load_float = load_float
+  let load_pointer = load_pointer
+
+  let store_int = store_int
+  let store_float = store_float
+  let store_pointer = store_pointer
+
+  let is_init_atom = is_init_atom
+  let is_init_range sigma obj loc length =
+    let n = e_mul (sizeof obj) length in
+    Why3.is_init_range (Sigma.value sigma Init) loc n
+
+  let set_init_atom = set_init_atom
+  let set_init obj loc ~length _chunk ~current =
+    let n = e_mul (sizeof obj) length in
+    Why3.set_init_range current loc n
+
+end
+
+include MemLoader.Make(Model)
+
+(* ********************************************************************** *)
+(* BASES                                                                  *)
+(* ********************************************************************** *)
+
+let cluster_globals () =
+  Definitions.cluster ~id:"Globals" ~title:"Global Variables" ()
+let globals = 0
+let locals = 1
+let formals = 2
+
+module RegisterBASE = WpContext.Index
+    (struct
+      type key = Lang.lfun
+      type data = Cil_types.varinfo
+      let name = "MemBytes.RegisterBASE"
+      include Lang.Fun
+    end)
+
+module BASE = WpContext.Generator(Cil_datatype.Varinfo)
+    (struct
+      let name = datatype ^ ".BASE"
+      type key = Cil_types.varinfo
+      type data = Lang.F.term
+
+      open Cil_types
+
+      let region prefix x base =
+        let name = prefix ^ "_region" in
+        let region =
+          if x.vglob then globals
+          else if x.vformal then formals
+          else locals
+        in
+        Definitions.define_lemma {
+          l_kind = Admit ;
+          l_name = name ; l_triggers = [] ; l_forall = [] ;
+          l_lemma = p_equal (MemAddr.region base) (e_int region) ;
+          l_cluster = cluster_globals () ;
+        }
+
+      let sizeof x =
+        Warning.handle
+          ~handler:(fun _ -> None)
+          ~effect:(Printf.sprintf "No allocation size for variable '%s'" x.vname)
+          (fun obj -> Some (protected_sizeof_object obj))
+          (Ctypes.object_of x.vtype)
+
+      let alloc prefix x base =
+        let name = prefix ^ "_linked" in
+        let size =
+          if x.vglob then sizeof x else Some e_zero
+        in
+        match size with
+        | None -> ()
+        | Some size ->
+          let a = Lang.freshvar ~basename:"alloc" MemAddr.t_malloc in
+          let m = e_var a in
+          let base_size = p_equal (Lang.F.e_get m base) size in
+          Definitions.define_lemma {
+            l_kind = Admit ;
+            l_name = name ;
+            l_triggers = [] ; l_forall = [] ;
+            l_lemma = p_forall [a] (p_imply (MemAddr.linked m) base_size) ;
+            l_cluster = cluster_globals () ;
+          }
+
+      (* Specializes the lemma in Pointers below for globals *)
+      let pointer_type prefix base =
+        let name = prefix ^ "_is_pointer" in
+        let typed =
+          MemAddr.in_uintptr_range (MemAddr.global base)
+        in
+        Definitions.define_lemma {
+          l_kind = Admit ;
+          l_name = name ;
+          l_triggers = [] ; l_forall = [] ;
+          l_lemma = typed ;
+          l_cluster = cluster_globals () ;
+        }
+
+      let compile vi =
+        let result = Logic.Int in
+        let acs_rd = Cil.typeHasQualifier "const" vi.vtype in
+        let prefix =
+          if vi.vglob
+          then if acs_rd then "K" else "G"
+          else if vi.vformal then "P" else "L" in
+        let lfun = Lang.generated_f
+            ~category:Logic.Constructor ~result:Logic.Int "%s_%s_%d"
+            prefix vi.vorig_name vi.vid in
+        Definitions.define_symbol {
+          d_lfun = lfun ; d_types = 0 ; d_params = [ ] ;
+          d_definition = Definitions.Function (result, Def, e_int vi.vid) ;
+          d_cluster = cluster_globals () ;
+        } ;
+        let prefix = Lang.Fun.debug lfun in
+        let base = e_fun lfun [] in
+        RegisterBASE.define lfun vi ;
+        region prefix vi base ;
+        alloc prefix vi base ;
+        pointer_type prefix base ;
+        base
+    end)
+
+module LITERAL =
+struct
+  type t = int * Cstring.cst
+  let compare (a:t) (b:t) = Stdlib.compare (fst a) (fst b)
+  let pretty fmt (eid,cst) = Format.fprintf fmt "%a@%d" Cstring.pretty cst eid
+end
+
+module EID = State_builder.Ref(Datatype.Int)
+    (struct
+      let name = datatype ^ ".EID"
+      let dependencies = [Ast.self]
+      let default () = 0
+    end)
+
+module STRING = WpContext.Generator(LITERAL)
+    (struct
+      let name = datatype ^ ".STRING"
+      type key = LITERAL.t
+      type data = term
+
+      let linked prefix base cst =
+        let name = prefix ^ "_linked" in
+        let a = Lang.freshvar ~basename:"alloc" (Chunk.tau_of_chunk Alloc) in
+        let m = e_var a in
+        let alloc = Lang.F.e_get m base in (* The size is alloc-1 *)
+        let sized = Cstring.str_len cst (Lang.F.(e_add alloc e_minus_one)) in
+        Definitions.define_lemma {
+          l_kind = Admit ;
+          l_name = name ;
+          l_triggers = [] ; l_forall = [] ;
+          l_lemma = p_forall [a] (p_imply (MemAddr.linked m) sized) ;
+          l_cluster = Cstring.cluster () ;
+        }
+
+      let region prefix base cst =
+        let name = prefix ^ "_region" in
+        let re = - Cstring.str_id cst in
+        Definitions.define_lemma {
+          l_kind = Admit ;
+          l_name = name ; l_triggers = [] ; l_forall = [] ;
+          l_lemma = p_equal (MemAddr.region base) (e_int re) ;
+          l_cluster = Cstring.cluster () ;
+        }
+
+      let sconst prefix base cst =
+        (* describe the content of literal strings *)
+        let name = prefix ^ "_literal" in
+        let i = Lang.freshvar ~basename:"i" Lang.t_int in
+        let c = Cstring.char_at cst (e_var i) in
+        let ikind = Ctypes.c_char () in
+        let m = Lang.freshvar ~basename:"mchar" (Chunk.tau_of_chunk Mem) in
+        let addr = shift (MemAddr.global base) (C_int ikind) (e_var i) in
+        let v = load_int_raw (e_var m) ikind addr in
+        let read = Lang.F.(p_equal c v) in
+        Definitions.define_lemma {
+          l_kind = Admit ;
+          l_name = name ; l_triggers = [] ;
+          l_forall = [m;i] ;
+          l_cluster = Cstring.cluster () ;
+          l_lemma = Lang.F.p_imply (Why3.sconst @@ e_var m) read ;
+        }
+
+      let fresh () =
+        let eid = succ (EID.get ()) in
+        EID.set eid ; eid
+
+      let compile (_,cst) =
+        let eid = fresh () in
+        let lfun = Lang.generated_f ~result:Lang.t_int "Str_%d" eid in
+        (* Since its a generated it is the unique name given *)
+        let prefix = Lang.Fun.debug lfun in
+        let base = Lang.F.e_fun lfun [] in
+        Definitions.define_symbol {
+          d_lfun = lfun ; d_types = 0 ; d_params = [] ;
+          d_definition = Logic Lang.t_int ;
+          d_cluster = Cstring.cluster () ;
+        } ;
+        Definitions.define_lemma {
+          l_name = prefix ^ "_base" ;
+          l_kind = Admit ;
+          l_triggers = [] ; l_forall = [] ;
+          l_lemma = Lang.F.(p_lt base e_zero) ;
+          l_cluster = Cstring.cluster () ;
+        } ;
+        region prefix base cst ;
+        linked prefix base cst ;
+        sconst prefix base cst ;
+        base
+
+    end)
+
+let pretty fmt loc =
+  Format.fprintf fmt "l:(%a)" Lang.F.pp_term loc
+
+let null = MemAddr.null
+
+let literal ~eid cst =
+  Wp_parameters.debug ~level:3 ~dkey:dkey_model
+    "%s.literal %d _" datatype eid ;
+  shift (MemAddr.global (STRING.get (eid,cst))) (C_int (Ctypes.c_char ())) e_zero
+
+let cvar vi =
+  Wp_parameters.debug ~level:3 ~dkey:dkey_model
+    "%s.cvar %a" datatype Cil_printer.pp_varinfo vi ;
+  MemAddr.global (BASE.get vi)
+
+let global _sigma p =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_model "%s.global _ _" datatype ;
+  p_leq (MemAddr.region @@ MemAddr.base p) e_zero
+
+(* ********************************************************************** *)
+(* STATE                                                                  *)
+(* ********************************************************************** *)
+
+type state = chunk Tmap.t
+
+let rec lookup_a e =
+  match repr e with
+  | Fun( f , [e] ) when MemAddr.is_f_global f -> lookup_a e
+  | Fun( f , es ) -> lookup_f f es
+  | _ -> raise Not_found
+
+and lookup_f f es =
+  try match RegisterShift.find f , es with
+    | RS_Field(fd,_) , [e] -> Mstate.field (lookup_lv e) fd
+    | RS_Index _ , [e;k] -> Mstate.index (lookup_lv e) k
+    | _ -> raise Not_found
+  with Not_found when es = [] ->
+    Sigs.(Mvar (RegisterBASE.find f),[])
+
+and lookup_lv e = try lookup_a e with Not_found -> Sigs.(Mmem e,[])
+
+let mchunk c =
+  match c with
+  | Chunk.Init -> Sigs.Mchunk (Pretty_utils.to_string Chunk.pretty c, KInit)
+  | _ -> Sigs.Mchunk (Pretty_utils.to_string Chunk.pretty c, KValue)
+
+let lookup s e =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_state "%s.lookup _ %a"
+    datatype pp_term e ;
+  try mchunk (Tmap.find e s)
+  with Not_found ->
+  try match repr e with
+    | Fun( f , es ) -> Sigs.Maddr (lookup_f f es)
+    | Aget( m , k ) when Tmap.find m s = Init ->
+      Sigs.Mlval (lookup_lv k, KInit)
+    | Aget( m , k ) when Tmap.find m s <> Alloc ->
+      Sigs.Mlval (lookup_lv k, KValue)
+    | _ -> Sigs.Mterm
+  with Not_found -> Sigs.Mterm
+
+let state sigma =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_state "%s.state _" datatype ;
+  let s = ref Tmap.empty in
+  Sigma.iter (fun c x -> s := Tmap.add (e_var x) c !s) sigma ; !s
+
+let iter f s =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_state "%s.iter _ _" datatype ;
+  Tmap.iter (fun m c -> f (mchunk c) m) s
+
+let updates _ _ =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_state "%s.updates _ _" datatype ;
+  Bag.empty
+
+let apply f s =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_state "%s.apply _ _" datatype ;
+  Tmap.fold (fun m c w -> Tmap.add (f m) c w) s Tmap.empty
+
+(* ********************************************************************** *)
+(* POINTERS OPS                                                           *)
+(* ********************************************************************** *)
+
+let pointer_loc t = t
+let pointer_val t = t
+
+let base_addr loc =
+  Wp_parameters.debug ~level:3 ~dkey:dkey_model
+    "%s.base_addr %a" datatype pretty loc ;
+  MemAddr.mk_addr (MemAddr.base loc) e_zero
+let base_offset loc =
+  Wp_parameters.debug ~level:3 ~dkey:dkey_model
+    "%s.base_offset %a" datatype pretty loc ;
+  MemAddr.base_offset (MemAddr.base loc) (MemAddr.offset loc)
+let block_length sigma _obj loc =
+  Wp_parameters.debug ~level:3 ~dkey:dkey_model
+    "%s.block_length _ _ _ " datatype ;
+  e_get (Sigma.value sigma Chunk.Alloc) (MemAddr.base loc)
+
+let cast _ loc =
+  Wp_parameters.debug ~level:3 ~dkey:dkey_model
+    "%s.cast _ %a" datatype pretty loc ;
+  loc
+
+let loc_of_int _ loc =
+  Wp_parameters.debug ~level:3 ~dkey:dkey_model
+    "%s.loc_of_int _ %a" datatype pretty loc ;
+  MemAddr.addr_of_int loc
+
+let int_of_loc _ loc =
+  Wp_parameters.debug ~level:3 ~dkey:dkey_model
+    "%s.int_of_loc _ %a" datatype pretty loc ;
+  MemAddr.int_of_addr loc
+
+
+(* -------------------------------------------------------------------------- *)
+
+let domain _ _ = Sigma.Chunk.Set.of_list [ Init ; Mem ]
+
+let is_null = p_equal null
+let loc_eq = p_equal
+let loc_lt = MemAddr.addr_lt
+let loc_leq = MemAddr.addr_le
+let loc_neq l1 l2 = p_not @@ loc_eq l1 l2
+
+let loc_diff _ l1 l2 =
+  let byte_size = Ctypes.sizeof_object (C_int (Ctypes.c_char ())) in
+  e_div (e_sub (MemAddr.offset l1) (MemAddr.offset l2)) (e_int byte_size)
+
+let pointer_cluster () =
+  Definitions.cluster
+    ~id:"MemBytes.PointersProperties" ~title:"MemBytes definitions" ()
+
+module PointersProperties = WpContext.Generator(Datatype.Unit)
+    (struct
+      let name = datatype ^ ".POINTERS"
+      type key = unit
+      type data = Lang.lfun
+
+      let ranges () =
+        let a = Lang.freshvar ~basename:"a" MemAddr.t_addr in
+        let prop = MemAddr.in_uintptr_range (e_var a) in
+        Definitions.define_lemma {
+          l_kind = Admit ; l_name = "pointers_int_range" ;
+          l_triggers = [] ; l_forall = [a] ;
+          l_cluster = pointer_cluster () ;
+          l_lemma = prop ;
+        }
+
+      let compile () =
+        let lfun = Lang.generated_p "framed" in
+        let m = Lang.freshvar ~basename:"m" Why3.t_memory in
+        let a = Lang.freshvar ~basename:"a" MemAddr.t_addr in
+        let p = load_pointer_raw (e_var m) (Cil_const.voidPtrType) (e_var a) in
+        let ba = MemAddr.base (e_var a) and bp = MemAddr.base p in
+        let body =
+          p_forall [a] @@ p_imply
+            (p_leq (MemAddr.region ba) e_zero)
+            (p_leq (MemAddr.region bp) e_zero)
+        in
+        Definitions.define_symbol {
+          d_lfun = lfun ;
+          d_cluster = pointer_cluster () ; d_types = 0 ;
+          d_params = [ m ] ; d_definition = Predicate (Def, body)
+        };
+        ranges () ;
+        lfun
+    end)
+
+let framed m =
+  p_call (PointersProperties.get ()) [ m ]
+
+let frame sigma =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_model "%s.frame _" datatype ;
+  let wellformed_frame phi chunk =
+    if Sigma.mem sigma chunk
+    then [ phi (Sigma.value sigma chunk) ]
+    else []
+  in
+  wellformed_frame MemAddr.linked Alloc @
+  wellformed_frame Why3.cinits Init @
+  wellformed_frame Why3.sconst Mem @
+  [ framed (Sigma.value sigma Mem) ]
+
+let is_well_formed s =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_model "%s.is_well_formed _" datatype ;
+  Why3.bytes (Sigma.value s Mem)
+
+(* ********************************************************************** *)
+(* ALLOCATION                                                             *)
+(* ********************************************************************** *)
+
+let alloc sigma xs =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_model
+    "%s.alloc %a %a"
+    datatype Sigma.pretty sigma (Pretty_utils.pp_list Cil_printer.pp_varinfo) xs ;
+  if xs = [] then sigma else Sigma.havoc_chunk sigma Alloc
+
+let scope seq scope xs =
+  Wp_parameters.debug ~level:2 ~dkey:dkey_model
+    "%s.scope { %a ; %a } %s %a"
+    datatype Sigma.pretty seq.pre Sigma.pretty seq.post
+    (if scope = Sigs.Enter then "Enter" else "Leave")
+    (Pretty_utils.pp_list Cil_printer.pp_varinfo) xs ;
+  if xs = [] then [] else
+    let alloc =
+      List.fold_left
+        (fun m x ->
+           let size = match scope with
+             | Sigs.Leave -> e_zero
+             | Sigs.Enter ->
+               protected_sizeof_object @@ Ctypes.object_of x.Cil_types.vtype
+           in e_set m (BASE.get x) size)
+        (Sigma.value seq.pre Alloc) xs in
+    [ p_equal (Sigma.value seq.post Alloc) alloc ]
-- 
GitLab