From 4c3dbd906232d031824a19c2edd8c322efce6c27 Mon Sep 17 00:00:00 2001
From: Valentin Perrelle <valentin.perrelle@cea.fr>
Date: Tue, 5 Apr 2022 20:35:56 +0200
Subject: [PATCH] [Eva] multidim: reorganize files

- split Abstract_memory into multiple file
- move from kernel_services to value/domains/multidim
---
 Makefile                                      |   15 +-
 headers/header_spec.txt                       |   25 +-
 .../abstract_interp/abstract_memory.ml        | 1967 -----------------
 .../value/domains/multidim/abstract_memory.ml |  194 ++
 .../domains/multidim/abstract_memory.mli      |  100 +
 .../domains/multidim}/abstract_offset.ml      |   29 +-
 .../domains/multidim}/abstract_offset.mli     |    4 +-
 .../domains/multidim/abstract_structure.ml    |  355 +++
 .../domains/multidim/abstract_structure.mli   |   81 +
 .../value/domains/multidim/lattice_extrema.ml |   81 +
 .../value/domains/multidim}/multidim.ml       |    0
 .../value/domains/multidim}/multidim.mli      |    0
 .../domains/{ => multidim}/multidim_domain.ml |    2 +-
 .../{ => multidim}/multidim_domain.mli        |    0
 .../value/domains/multidim/pretty_memory.ml   |   46 +
 .../value/domains/multidim/pretty_memory.mli  |   42 +
 .../value/domains/multidim/segmentation.ml    |  827 +++++++
 .../value/domains/multidim/segmentation.mli   |   70 +
 .../value/domains/multidim/typed_memory.ml    |  596 +++++
 .../value/domains/multidim/typed_memory.mli}  |   59 +-
 20 files changed, 2439 insertions(+), 2054 deletions(-)
 delete mode 100644 src/kernel_services/abstract_interp/abstract_memory.ml
 create mode 100644 src/plugins/value/domains/multidim/abstract_memory.ml
 create mode 100644 src/plugins/value/domains/multidim/abstract_memory.mli
 rename src/{kernel_services/abstract_interp => plugins/value/domains/multidim}/abstract_offset.ml (95%)
 rename src/{kernel_services/abstract_interp => plugins/value/domains/multidim}/abstract_offset.mli (98%)
 create mode 100644 src/plugins/value/domains/multidim/abstract_structure.ml
 create mode 100644 src/plugins/value/domains/multidim/abstract_structure.mli
 create mode 100644 src/plugins/value/domains/multidim/lattice_extrema.ml
 rename src/{kernel_services/abstract_interp => plugins/value/domains/multidim}/multidim.ml (100%)
 rename src/{kernel_services/abstract_interp => plugins/value/domains/multidim}/multidim.mli (100%)
 rename src/plugins/value/domains/{ => multidim}/multidim_domain.ml (99%)
 rename src/plugins/value/domains/{ => multidim}/multidim_domain.mli (100%)
 create mode 100644 src/plugins/value/domains/multidim/pretty_memory.ml
 create mode 100644 src/plugins/value/domains/multidim/pretty_memory.mli
 create mode 100644 src/plugins/value/domains/multidim/segmentation.ml
 create mode 100644 src/plugins/value/domains/multidim/segmentation.mli
 create mode 100644 src/plugins/value/domains/multidim/typed_memory.ml
 rename src/{kernel_services/abstract_interp/abstract_memory.mli => plugins/value/domains/multidim/typed_memory.mli} (84%)

diff --git a/Makefile b/Makefile
index 8d1f4aeae6d..b32bad3aca3 100644
--- a/Makefile
+++ b/Makefile
@@ -605,9 +605,6 @@ KERNEL_CMO=\
 	src/kernel_services/abstract_interp/locations.cmo               \
 	src/kernel_services/abstract_interp/lmap.cmo                    \
 	src/kernel_services/abstract_interp/lmap_bitwise.cmo            \
-	src/kernel_services/abstract_interp/multidim.cmo                \
-	src/kernel_services/abstract_interp/abstract_offset.cmo         \
-	src/kernel_services/abstract_interp/abstract_memory.cmo         \
 	src/kernel_services/visitors/visitor.cmo                        \
 	src/kernel_services/ast_data/statuses_by_call.cmo               \
 	src/kernel_services/ast_printing/printer_tag.cmo                \
@@ -822,7 +819,7 @@ PLUGIN_NAME:=Eva
 PLUGIN_DIR:=src/plugins/value
 PLUGIN_EXTRA_DIRS:=engine values domains api domains/cvalue domains/apron \
 	domains/gauges domains/equality legacy partitioning utils gui_files \
-	api values/numerors domains/numerors
+	api values/numerors domains/numerors domains/multidim
 PLUGIN_TESTS_DIRS+=value/traces
 PLUGIN_GENERATED:=$(PLUGIN_DIR)/Eva.mli
 PLUGIN_DISTRIB_EXTERNAL+=gen-api.sh
@@ -905,7 +902,15 @@ PLUGIN_CMO:= partitioning/split_strategy domains/domain_mode self parameters \
 	engine/transfer_logic engine/transfer_stmt engine/transfer_specification \
 	engine/mem_exec engine/iterator engine/initialization \
 	engine/compute_functions engine/analysis register \
-	domains/multidim_domain \
+	domains/multidim/lattice_extrema \
+	domains/multidim/multidim \
+	domains/multidim/abstract_offset \
+	domains/multidim/abstract_memory \
+	domains/multidim/pretty_memory \
+	domains/multidim/abstract_structure \
+	domains/multidim/segmentation \
+	domains/multidim/typed_memory \
+	domains/multidim/multidim_domain \
 	domains/taint_domain \
 	$(APRON_CMO) $(NUMERORS_CMO) \
 	utils/eva_results \
diff --git a/headers/header_spec.txt b/headers/header_spec.txt
index a2efff415c7..ad96867463c 100644
--- a/headers/header_spec.txt
+++ b/headers/header_spec.txt
@@ -474,10 +474,6 @@ src/kernel_services/README.md: .ignore
 src/kernel_services/abstract_interp/README.md: .ignore
 src/kernel_services/abstract_interp/abstract_interp.ml: CEA_LGPL
 src/kernel_services/abstract_interp/abstract_interp.mli: CEA_LGPL
-src/kernel_services/abstract_interp/abstract_memory.ml: CEA_LGPL
-src/kernel_services/abstract_interp/abstract_memory.mli: CEA_LGPL
-src/kernel_services/abstract_interp/abstract_offset.ml: CEA_LGPL
-src/kernel_services/abstract_interp/abstract_offset.mli: CEA_LGPL
 src/kernel_services/abstract_interp/base.ml: CEA_LGPL
 src/kernel_services/abstract_interp/base.mli: CEA_LGPL
 src/kernel_services/abstract_interp/eva_lattice_type.mli: CEA_LGPL
@@ -516,8 +512,6 @@ src/kernel_services/abstract_interp/locations.ml: CEA_LGPL
 src/kernel_services/abstract_interp/locations.mli: CEA_LGPL
 src/kernel_services/abstract_interp/map_lattice.ml: CEA_LGPL
 src/kernel_services/abstract_interp/map_lattice.mli: CEA_LGPL
-src/kernel_services/abstract_interp/multidim.ml: CEA_LGPL
-src/kernel_services/abstract_interp/multidim.mli: CEA_LGPL
 src/kernel_services/abstract_interp/offsetmap.ml: CEA_LGPL
 src/kernel_services/abstract_interp/offsetmap.mli: CEA_LGPL
 src/kernel_services/abstract_interp/offsetmap_bitwise_sig.mli: CEA_LGPL
@@ -1416,8 +1410,23 @@ src/plugins/value/domains/hcexprs.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/hcexprs.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/inout_domain.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/inout_domain.mli: CEA_LGPL_OR_PROPRIETARY
-src/plugins/value/domains/multidim_domain.ml: CEA_LGPL_OR_PROPRIETARY
-src/plugins/value/domains/multidim_domain.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/abstract_memory.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/abstract_memory.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/abstract_offset.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/abstract_offset.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/abstract_structure.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/abstract_structure.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/lattice_extrema.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/multidim.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/multidim.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/multidim_domain.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/multidim_domain.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/pretty_memory.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/pretty_memory.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/segmentation.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/segmentation.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/typed_memory.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/multidim/typed_memory.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/octagons.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/octagons.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/offsm_domain.ml: CEA_LGPL_OR_PROPRIETARY
diff --git a/src/kernel_services/abstract_interp/abstract_memory.ml b/src/kernel_services/abstract_interp/abstract_memory.ml
deleted file mode 100644
index f92571a8bfb..00000000000
--- a/src/kernel_services/abstract_interp/abstract_memory.ml
+++ /dev/null
@@ -1,1967 +0,0 @@
-(**************************************************************************)
-(*                                                                        *)
-(*  This file is part of Frama-C.                                         *)
-(*                                                                        *)
-(*  Copyright (C) 2007-2022                                               *)
-(*    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).            *)
-(*                                                                        *)
-(**************************************************************************)
-
-(* Ocaml compiler incorrectly considers that module MemorySafe is unused and
-   emits a warning *)
-[@@@warning "-60"]
-[@@@warning "-33"]
-
-exception Not_implemented
-
-open Abstract_offset
-
-module Bot =
-struct
-  [@@@warning "-32"]
-
-  include Bottom.Type
-  let zip x y =
-    match x, y with
-    | `Bottom, _ | _, `Bottom -> `Bottom
-    | `Value x, `Value y -> `Value (x,y)
-
-  (* Applicative syntax *)
-  let ( let+ ) = (>>-:)
-  let ( and+ ) = zip
-  (* Monad syntax *)
-  let ( let* ) = (>>-)
-  let ( and* ) = zip
-end
-
-module Top =
-struct
-  [@@@warning "-32"]
-
-  let zip x y =
-    match x, y with
-    | `Top, _ | _, `Top -> `Top
-    | `Value x, `Value y -> `Value (x,y)
-
-  let (>>-) t f = match t with
-    | `Top  -> `Top
-    | `Value t -> f t
-
-  let (>>-:) t f = match t with
-    | `Top  -> `Top
-    | `Value t -> `Value (f t)
-
-  let (let+) = (>>-:)
-  let (and+) = zip
-  let (let*) = (>>-)
-  let (and*) = zip
-  let of_option = function
-    | None -> `Top
-    | Some v -> `Value v
-end
-
-module TopBot =
-struct
-  let (>>-) t f = match t with
-    | `Top  -> `Top
-    | `Bottom -> `Bottom
-    | `Value t -> f t
-
-  let (>>-:) t f = match t with
-    | `Top  -> `Top
-    | `Bottom -> `Bottom
-    | `Value t -> `Value (f t)
-
-  let (let+) = (>>-:)
-  let (let*) = (>>-)
-end
-
-type 'a or_bottom = [`Bottom | `Value of 'a]
-type 'a or_top = [`Top | `Value of 'a]
-type 'a or_top_bottom = [`Top | `Bottom | `Value of 'a]
-
-type size = Integer.t
-
-(* Composition operator for compare function *)
-
-let (<?>) c lcmp =
-  if c = 0 then 0 else Lazy.force lcmp
-
-(* Pretty printing for iterators - inspired by Pretty_utils.pp_iter *)
-
-let pp_iter
-    ?(pre=format_of_string "{@;<1 2>")
-    ?(sep=format_of_string ",@;<1 2>")
-    ?(suf=format_of_string "@ }")
-    ?(format=format_of_string "@[<hv>%a@]")
-    iter pp fmt v =
-  let need_sep = ref false in
-  Format.fprintf fmt pre;
-  iter (fun v ->
-      if !need_sep then Format.fprintf fmt sep else need_sep := true;
-      Format.fprintf fmt format pp v;
-    ) v;
-  Format.fprintf fmt suf
-
-let pp_iter2 ?pre ?sep ?suf ?(format=format_of_string "@[<hv>%a%a@]")
-    iter2 pp_key pp_val fmt v =
-  let iter f = iter2 (fun k v -> f (k,v)) in
-  let pp fmt (k,v) = Format.fprintf fmt format pp_key k pp_val v in
-  pp_iter ?pre ?sep ?suf ~format:"%a" iter pp fmt v
-
-(* Types compatibility *)
-
-let typ_size t =
-  Integer.of_int (Cil.bitsSizeOf t)
-
-let are_typ_compatible t1 t2 =
-  Integer.equal (typ_size t1) (typ_size t2)
-
-
-(* ------------------------------------------------------------------------ *)
-(* --- Imprecise bits abstraction                                       --- *)
-(* ------------------------------------------------------------------------ *)
-
-type initialization =
-  | SurelyInitialized
-  | MaybeUninitialized
-
-type bit =
-  | Uninitialized
-  | Zero of initialization
-  | Any of Base.SetLattice.t * initialization
-
-module Initialization =
-struct
-  let pretty fmt = function
-    | SurelyInitialized -> ()
-    | MaybeUninitialized ->
-      Format.fprintf fmt "or UNINITIALIZED"
-
-  let hash = function
-    | SurelyInitialized -> 3
-    | MaybeUninitialized -> 7
-
-  let equal i1 i2 =
-    match i1, i2 with
-    | SurelyInitialized, SurelyInitialized
-    | MaybeUninitialized, MaybeUninitialized -> true
-    | _, _ -> false
-
-  let compare i1 i2 =
-    match i1, i2 with
-    | SurelyInitialized, SurelyInitialized
-    | MaybeUninitialized, MaybeUninitialized -> 0
-    | SurelyInitialized, MaybeUninitialized -> -1
-    | MaybeUninitialized, SurelyInitialized -> 1
-
-  let is_included i1 i2 =
-    match i1, i2 with
-    | SurelyInitialized, _
-    | _, MaybeUninitialized -> true
-    | MaybeUninitialized, SurelyInitialized -> false
-
-  let join i1 i2 =
-    match i1, i2 with
-    | SurelyInitialized, SurelyInitialized -> SurelyInitialized
-    | MaybeUninitialized, _
-    | _, MaybeUninitialized -> MaybeUninitialized
-end
-
-module Bit =
-struct
-  module Bases = Base.SetLattice
-
-  type t = bit
-
-  let uninitialized = Uninitialized
-  let zero = Zero SurelyInitialized
-  let numerical = Any (Bases.empty, SurelyInitialized)
-  let top = Any (Bases.top, MaybeUninitialized)
-
-
-  let pretty fmt = function
-    | Uninitialized ->
-      Format.fprintf fmt "UNINITIALIZED"
-    | Zero i ->
-      Format.fprintf fmt "0%a" Initialization.pretty i
-    | Any (Set set, i) when Base.SetLattice.O.is_empty set ->
-      Format.fprintf fmt "[--..--]%a" Initialization.pretty i
-    | Any _ -> Format.fprintf fmt "T"
-
-  let is_any = function Any _ -> true | _ -> false
-
-  let hash = function
-    | Uninitialized -> 7
-    | Zero i -> Hashtbl.hash (3, Initialization.hash i)
-    | Any (set, i)-> Hashtbl.hash (53, Bases.hash set, Initialization.hash i)
-
-  let equal d1 d2 =
-    match d1,d2 with
-    | Uninitialized, Uninitialized -> true
-    | Zero i1, Zero i2 -> Initialization.equal i1 i2
-    | Any (set1,i1), Any (set2,i2) ->
-      Bases.equal set1 set2 && Initialization.equal i1 i2
-    | _, _ -> false
-
-  let compare d1 d2 =
-    match d1,d2 with
-    | Uninitialized, Uninitialized -> 0
-    | Zero i1, Zero i2 -> Initialization.compare i1 i2
-    | Any (set1,i1), Any (set2,i2) ->
-      Bases.compare set1 set2 <?> lazy (Initialization.compare i1 i2)
-    | Uninitialized, _ -> 1
-    | _, Uninitialized -> -1
-    | Zero _, _ -> 1
-    | _, Zero _-> -1
-
-  let initialization = function
-    | Uninitialized -> MaybeUninitialized
-    | Zero i -> i
-    | Any (_,i) -> i
-
-  let is_included d1 d2 =
-    Initialization.is_included (initialization d1) (initialization d2) &&
-    match d1, d2 with
-    | Uninitialized, _ -> true
-    | _, Uninitialized -> false
-    | Zero _, _ -> true
-    | _, Zero _ -> false
-    | Any (set1,_), Any (set2,_) -> Bases.is_included set1 set2
-
-  let join d1 d2 =
-    match d1, d2 with
-    | Uninitialized, Uninitialized -> Uninitialized
-    | Zero i1, Zero i2 ->
-      Zero (Initialization.join i1 i2)
-    | Any (set1,i1), Any (set2,i2) ->
-      Any (Bases.join set1 set2, Initialization.join i1 i2)
-    | Zero _, Uninitialized
-    | Uninitialized, Zero _ -> Zero MaybeUninitialized
-    | Any (set,i), (Zero _ | Uninitialized as b)
-    | (Zero _ | Uninitialized as b), Any (set,i) ->
-      Any (set, Initialization.join i (initialization b))
-end
-
-
-(* ------------------------------------------------------------------------ *)
-(* --- Inputs parameters for Memory abstraction                         --- *)
-(* ------------------------------------------------------------------------ *)
-
-module type Value =
-sig
-  type t
-
-  val name : string
-
-  val hash : t -> int
-  val equal : t -> t -> bool
-  val compare : t -> t -> int
-
-  val pretty : Format.formatter -> t -> unit
-  val of_bit : typ:Cil_types.typ -> bit -> t
-  val to_bit : t -> bit
-  val to_integer : t -> Integer.t option
-  val is_included : t -> t -> bool
-  val join : t -> t -> t
-end
-
-module type Config =
-sig
-  val deps : State.t list
-  val slice_limit : unit -> int
-  val disjunctive_invariants : unit -> bool
-end
-
-
-(* ------------------------------------------------------------------------ *)
-(* --- Proto memory abstraction                                         --- *)
-(* ------------------------------------------------------------------------ *)
-
-type side = Left | Right
-type oracle = Cil_types.exp -> Int_val.t
-type bioracle = side -> oracle
-type strength = Strong | Weak | Reinforce (* update strength *)
-
-let no_oracle = fun _exp -> Int_val.top
-
-module type ProtoMemory =
-sig
-  type t
-  type value
-
-  val pretty : Format.formatter -> t -> unit
-  val pretty_root : Format.formatter -> t -> unit
-  val hash : t -> int
-  val equal : t -> t -> bool
-  val compare : t -> t -> int
-
-  val of_raw : bit -> t
-  val raw : t -> bit
-  val of_value : Cil_types.typ -> value -> t
-  val to_value : Cil_types.typ -> t -> value
-  val to_singleton_int : t -> Integer.t option
-  val weak_erase : bit -> t -> t
-  val is_included : t -> t -> bool
-  val unify : oracle:bioracle ->
-    (size:size -> value -> value -> value) -> t -> t -> t
-  val join : oracle:bioracle -> t -> t -> t
-  val smash : oracle:oracle -> t -> t -> t
-  val read : oracle:oracle -> (Cil_types.typ -> t -> 'a) -> ('a -> 'a -> 'a) ->
-    Abstract_offset.t -> t -> 'a
-  val update : oracle:oracle ->
-    (weak:bool -> Cil_types.typ -> t -> t or_bottom) ->
-    weak:bool -> Abstract_offset.t -> t -> t or_bottom
-  val incr_bound : oracle:oracle -> Cil_types.varinfo -> Integer.t option ->
-    t -> t
-  val add_segmentation_bounds : oracle:oracle -> typ:Cil_types.typ ->
-    Cil_types.exp list -> t -> t
-end
-
-
-(* ------------------------------------------------------------------------ *)
-(* --- C struct abstraction                                             --- *)
-(* ------------------------------------------------------------------------ *)
-
-module type Structures =
-sig
-  type t
-  type submemory
-  val pretty : Format.formatter -> t -> unit
-  val hash : t -> int
-  val equal : t -> t -> bool
-  val compare : t -> t -> int
-  val raw : t -> Bit.t
-  val of_raw : Bit.t -> t
-  val weak_erase : Bit.t -> t -> t
-  val is_included : t -> t -> bool
-  val unify : (submemory -> submemory -> submemory) -> t -> t -> t
-  val read : t -> Cil_types.fieldinfo -> submemory
-  val update : (submemory -> submemory or_bottom) -> t -> Cil_types.fieldinfo ->
-    t or_bottom
-  val map : (submemory -> submemory) -> t -> t
-end
-
-module Structures (Config : Config) (M : ProtoMemory) =
-struct
-  module Field =
-  struct
-    include Cil_datatype.Fieldinfo
-    let id f = f.Cil_types.forder (* At each node, all fields come from the same comp *)
-  end
-  module Values =
-  struct
-    include Datatype.Make (
-      struct
-        include Datatype.Serializable_undefined
-        include M
-        let name = "Abstract_Memory.Structures.Values"
-        let reprs = [ of_raw Bit.zero ]
-      end)
-    let pretty_debug = pretty
-  end
-  module Initial_Values = struct let v = [[]] end
-  module Deps = struct let l = Config.deps end
-
-  module FieldMap =
-    Hptmap.Make (Field) (Values) (Hptmap.Comp_unused) (Initial_Values) (Deps)
-
-  type t = {
-    padding: Bit.t;
-    fields: FieldMap.t;
-  }
-
-  type submemory = M.t
-
-  let pretty fmt m =
-    pp_iter2 ~format:"@[<hv>.%a%a@]"
-      FieldMap.iter Field.pretty M.pretty fmt m.fields
-
-  let hash m =
-    Hashtbl.hash (m.padding, FieldMap.hash m.fields)
-
-  let equal m1 m2 =
-    FieldMap.equal m1.fields m2.fields &&
-    Bit.equal m1.padding m2.padding
-
-  let compare m1 m2 =
-    FieldMap.compare m1.fields m2.fields <?>
-    lazy (Bit.compare m1.padding m2.padding)
-
-  let raw m =
-    FieldMap.fold (fun _ x acc -> Bit.join acc (M.raw x)) m.fields m.padding
-
-  let of_raw m =
-    { padding = m ; fields = FieldMap.empty }
-
-  let weak_erase b m =
-    {
-      padding = Bit.join b m.padding ;
-      fields = FieldMap.map (M.weak_erase b) m.fields ;
-    }
-
-  let is_included m1 m2 =
-    Bit.is_included m1.padding m2.padding &&
-    let decide_fast s t = if s == t then FieldMap.PTrue else PUnknown in
-    let decide_fst _fi m1 = M.is_included m1 (M.of_raw m2.padding) in
-    let decide_snd _fi m2 = M.is_included (M.of_raw m1.padding) m2 in
-    let decide_both _fi m1 m2 = M.is_included m1 m2 in
-    FieldMap.binary_predicate Hptmap_sig.NoCache UniversalPredicate
-      ~decide_fast ~decide_fst ~decide_snd ~decide_both
-      m1.fields m2.fields
-
-  let unify f m1 m2 = (* f is not symmetric *)
-    let decide_both _fi = fun m1 m2 -> Some (f m1 m2)
-    and decide_left = FieldMap.Traversing (fun _fi m1 ->
-        Some (f m1 (M.of_raw m2.padding)))
-    and decide_right = FieldMap.Traversing (fun _fi m2 ->
-        Some (f (M.of_raw m1.padding) m2))
-    in
-    let fields = FieldMap.merge
-        ~cache:Hptmap_sig.NoCache ~symmetric:false ~idempotent:true
-        ~decide_both ~decide_left ~decide_right
-        m1.fields m2.fields
-    in { padding = Bit.join m1.padding m2.padding ; fields }
-
-  let read m fi =
-    try
-      FieldMap.find fi m.fields
-    with Not_found -> (* field undefined *)
-      M.of_raw m.padding
-
-  let update f m fi =
-    try
-      let update' opt =
-        let x = Option.value ~default:(M.of_raw m.padding) opt in
-        match f x with
-        | `Value v -> Some v
-        | `Bottom -> raise Exit
-      in
-      `Value { m with fields = FieldMap.replace update' fi m.fields }
-    with Exit ->
-      `Bottom
-
-  let map f m =
-    { m with fields = FieldMap.map f m.fields }
-end
-
-
-(* ------------------------------------------------------------------------ *)
-(* --- Disjunctions of structures                                       --- *)
-(* ------------------------------------------------------------------------ *)
-
-(* This abstraction focus on catching structures invariants of the form
-
-   x.f = c₁ ⇒ ϕ₁(x)  ⋁  x.f = c₂ ⇒ ϕ₂(x)  ⋁  ...
-
-   where f is a field, c₁, c₂, ... are constants and ϕ₁, ϕ₂, ... properties
-   about the structure x.
-
-   During the analysis, we track fields f, called keys which are assigned
-   constant values. As the analysis  progresses, the set of keys is reduced each
-   time we discover one of the field is actually not a key because it is
-   assigned with non-singleton values. *)
-
-module type Disjunction =
-sig
-  type t
-  type submemory
-  type structure
-  val pretty : Format.formatter -> t -> unit
-  val hash : t -> int
-  val equal : t -> t -> bool
-  val compare : t -> t -> int
-  val raw : t -> Bit.t
-  val of_raw : Cil_types.compinfo -> Bit.t -> t
-  val of_struct : Cil_types.compinfo -> structure -> t
-  val to_struct : oracle:oracle -> t -> structure
-  val weak_erase : Cil_types.compinfo -> Bit.t -> t -> t
-  val is_included : t -> t -> bool
-  val unify : oracle:bioracle -> (submemory -> submemory -> submemory) ->
-    t -> t -> t
-  val read : (submemory -> 'a) -> ('a -> 'a -> 'a)  ->
-    t -> Cil_types.fieldinfo -> 'a
-  val update : oracle:oracle -> (submemory -> submemory or_bottom) ->
-    t -> Cil_types.fieldinfo -> t or_bottom
-  val map : (submemory -> submemory) -> t -> t
-end
-
-module Disjunction (Config : Config) (M : ProtoMemory)
-    (S : Structures with type submemory = M.t) =
-struct
-  module Valuation =
-  struct
-    module FMap = Cil_datatype.Fieldinfo.Map
-    module FSet = Cil_datatype.Fieldinfo.Set
-
-    include FMap
-    include FMap.Make (Datatype.Integer)
-    let keys map = fold (fun k _ set -> FSet.add k set) map FSet.empty
-  end
-
-  module Map = (* 'a Map.t : (fi -> int) -> 'a *)
-  struct
-    module Info = struct let module_name = "Abstract_memory.Disjunction.Map" end
-    include Datatype.Map (Map.Make (Valuation)) (Valuation) (Info)
-
-    (* Defined only for Ocaml >= 4.11 *)
-    let filter_map f m =
-      fold (fun k x m -> match f k x with None -> m | Some y -> add k y m) m empty
-  end
-
-  module S = (* Structures in the disjunction *)
-  struct
-    include Datatype.Serializable_undefined
-    include S
-    let name = "Abstract_Memory.Disjunction.S"
-    let reprs = [ of_raw Bit.zero ]
-    let eval_key key m = M.to_singleton_int (read m key)
-    let suitable_key key m = Option.is_some (eval_key key m)
-    let keys_candidates ci m =
-      let fields = Option.value ~default:[] ci.Cil_types.cfields in
-      List.filter (fun fi -> suitable_key fi m) fields |>
-      Valuation.FSet.of_list
-    let evaluate keys m : Valuation.t =
-      let update_key fi map =
-        eval_key fi m |> (* should be Some *)
-        Option.fold ~none:map ~some:(fun v -> Valuation.add fi v map)
-      in
-      Valuation.FSet.fold update_key keys Valuation.empty
-  end
-
-  include Map.Make (Datatype.Make (S))
-
-  type submemory = M.t
-  type structure = S.t
-
-  let keys (m : t) = Valuation.keys (fst (Map.choose m))
-
-  let pick (m : t) =
-    match Map.choose_opt m with
-    | None -> Kernel.fatal "The disjunction should never be empty"
-    | Some (k,s) -> k,s,Map.remove k m
-
-  let pretty fmt (m : t) =
-    match Map.bindings m with
-    | [] -> assert false
-    | [(_,s)] -> S.pretty fmt s
-    | bindings ->
-      let l = List.map snd bindings in
-      pp_iter ~format:"@[<hv>%a@]" ~sep:" or @;<1 2>" List.iter S.pretty fmt l
-
-  let hash (m : t) =
-    Hashtbl.hash (Map.fold (fun _ s acc -> s :: acc) m [])
-
-  let reevaluate ~oracle keys (m : t) : t =
-    let update _k s acc =
-      let merge prev =
-        Extlib.merge_opt (fun () -> S.unify (M.smash ~oracle)) () (Some s) prev
-      in
-      Map.update (S.evaluate keys s) merge acc
-    in
-    Map.fold update m Map.empty
-
-  let smash ?(oracle=no_oracle) (m : t) =
-    let _k,s,m = pick m in
-    Map.fold (fun _k -> S.unify (M.smash ~oracle)) m s
-
-  let of_struct (ci : Cil_types.compinfo) (s : S.t) =
-    let keys = S.keys_candidates ci s in
-    Map.singleton (S.evaluate keys s) s
-
-  let to_struct ~oracle (m : t) =
-    smash ~oracle m
-
-  let raw (m : t) : bit =
-    let f _ x acc =
-      Bottom.join Bit.join acc (`Value (S.raw x))
-    in
-    Bottom.non_bottom (Map.fold f m `Bottom) (* The map should not be empty *)
-
-  let of_raw (ci : Cil_types.compinfo) (b : bit) : t =
-    let s = S.of_raw b in
-    of_struct ci s
-
-  let weak_erase (ci : Cil_types.compinfo) (b : bit) (m : t) : t =
-    (* if b is Zero, more could be done as all scalar fields might be a key *)
-    (* weak_erase is probably linear in the number of join operators, so we
-       weak_erase befoire joining as the join on erased memory is faster. *)
-    let m = Map.map (fun m -> S.weak_erase b m) m in
-    let s = smash m in
-    of_struct ci s
-
-  let is_included (m1 : t) (m2 : t) : bool  =
-    (* Imprecise inclusion: we only consider inclusion of disjunctions where
-       the set of field keys is the same *)
-    let aux k s1 =
-      match Map.find_opt k m2 with
-      | None -> false
-      | Some s2 -> S.is_included s1 s2
-    in
-    Map.for_all aux m1
-
-  let unify ~oracle f (m1 : t) (m2 : t) =
-    let keys = Valuation.FSet.inter (keys m1) (keys m2) in
-    let m1 = reevaluate ~oracle:(oracle Left) keys m1
-    and m2 = reevaluate ~oracle:(oracle Right) keys m2 in
-    Map.union (fun _k m1 m2 -> Some (S.unify f m1 m2)) m1 m2
-
-  let read map reduce (m : t) (fi : Cil_types.fieldinfo) =
-    let aux _k s acc =
-      map (S.read s fi) |>
-      Option.fold ~none:Fun.id ~some:reduce acc |>
-      Option.some
-    in
-    Option.get (Map.fold aux m None) (* If the map is not empty, result is Some *)
-
-  let update ~oracle f (m : t) (fi : Cil_types.fieldinfo) =
-    let m = Map.filter_map (fun _k s -> Bottom.to_option (S.update f s fi)) m in
-    if Map.is_empty m then
-      `Bottom
-    else
-      let is_key = Map.for_all (fun _k s -> S.suitable_key fi s) m in
-      let old_keys = keys m in
-      if is_key || Valuation.FSet.mem fi old_keys then
-        let new_keys =
-          if is_key
-          then Valuation.FSet.add fi old_keys
-          else Valuation.FSet.remove fi old_keys
-        in
-        `Value (reevaluate ~oracle new_keys m)
-      else
-        `Value (m)
-
-  let map f m =
-    Map.map (S.map f) m
-end
-
-
-(* ------------------------------------------------------------------------ *)
-(* --- Comparison operators                                             --- *)
-(* ------------------------------------------------------------------------ *)
-
-
-type comparison =
-  | Equal
-  | Lower
-  | Greater
-  | LowerOrEqual
-  | GreaterOrEqual
-  | Uncomparable
-
-module type Operators =
-sig
-  [@@@warning "-32"] (* unused operators... for now*)
-
-  type t
-  val (<) : t -> t -> bool
-  val (>) : t -> t -> bool
-  val (<=) : t -> t -> bool
-  val (>=) : t -> t -> bool
-  val (===) : t -> t -> bool
-end
-
-let operators :
-  type a . (a -> a -> comparison) -> (module Operators with type t = a) =
-  fun f ->
-  (module struct
-    type t = a
-
-    let (<) b1 b2 = match f b1 b2 with
-      | Lower -> true
-      | LowerOrEqual | Equal | Greater | GreaterOrEqual | Uncomparable -> false
-
-    let (<=) b1 b2 = match f b1 b2 with
-      | Lower | Equal | LowerOrEqual -> true
-      | Greater | GreaterOrEqual | Uncomparable -> false
-
-    let (>) b1 b2 = match f b1 b2 with
-      | Greater -> true
-      | Equal | LowerOrEqual | Lower | GreaterOrEqual | Uncomparable -> false
-
-    let (>=) b1 b2 = match f b1 b2 with
-      | Greater | GreaterOrEqual | Equal -> true
-      | Lower | LowerOrEqual | Uncomparable -> false
-
-    let (===) b1 b2 = match f b1 b2 with
-      | Equal -> true
-      | GreaterOrEqual | Greater | Lower | LowerOrEqual | Uncomparable -> false
-  end)
-
-
-(* ------------------------------------------------------------------------ *)
-(* --- Arrays abstraction                                               --- *)
-(* ------------------------------------------------------------------------ *)
-
-module Bound =
-struct
-  module Var = Cil_datatype.Varinfo
-  module Exp =
-  struct
-    include Cil_datatype.ExpStructEq
-    let equal e1 e2 =
-      if e1 == e2 then true else equal e1 e2
-  end
-
-  type t =
-    | Const of Integer.t
-    | Exp of Cil_types.exp * Integer.t (* x + c *)
-    | Ptroffset of Cil_types.exp * Cil_types.offset * Integer.t (* (x - &b.offset) + c *)
-
-  let pretty fmt : t -> unit = function
-    | Const i -> Integer.pretty fmt i
-    | Exp (e,i) when Integer.is_zero i -> Exp.pretty fmt e
-    | Exp (e,i) when Integer.(lt i zero) ->
-      Format.fprintf fmt "%a - %a" Exp.pretty e Integer.pretty (Integer.neg i)
-    | Exp (e,i) ->
-      Format.fprintf fmt "%a + %a" Exp.pretty e Integer.pretty i
-    | _ -> raise Not_implemented
-
-  let hash : t -> int = function
-    | Const i -> Hashtbl.hash (1, Integer.hash i)
-    | Exp (e, i) -> Hashtbl.hash (2, Exp.hash e, Integer.hash i)
-    | Ptroffset _ -> raise Not_implemented
-
-  let compare (b1 : t) (b2 : t) : int =
-    match b1, b2 with
-    | Const i1, Const i2 -> Integer.compare i1 i2
-    | Exp (e1, i1), Exp (e2, i2) ->
-      Exp.compare e1 e2 <?> lazy (Integer.compare i1 i2)
-    | Ptroffset _, Ptroffset _ -> raise Not_implemented
-    | Const _, _ -> 1
-    | _, Const _-> -1
-    | Exp _, _ -> 1
-    | _, Exp _ -> -1
-
-  let equal (b1 : t) (b2 : t) : bool =
-    match b1, b2 with
-    | Const i1, Const i2 -> Integer.equal i1 i2
-    | Exp (e1, i1), Exp (e2, i2) ->
-      Exp.equal e1 e2 && Integer.equal i1 i2
-    | Ptroffset _, Ptroffset _ -> raise Not_implemented
-    | _, _ -> false
-
-  let of_integer (i : Integer.t) : t =
-    Const i
-
-  exception UnsupportedBoundExpression
-  exception NonLinear
-
-  (* Find a coefficient before vi in exp *)
-  let rec linearity vi exp =
-    match exp.Cil_types.enode with
-    | Const _
-    | SizeOf _ | SizeOfE _ | SizeOfStr _ | AlignOf _ | AlignOfE _
-    | AddrOf _ | StartOf _ -> Integer.zero
-    | Lval (Var vi', NoOffset) ->
-      if Var.equal  vi' vi
-      then Integer.one
-      else Integer.zero
-    | Lval _ -> raise UnsupportedBoundExpression
-    | UnOp (Neg, e, _typ) ->
-      Integer.neg (linearity vi e)
-    | UnOp (_, e, _typ) | CastE (_typ, e) ->
-      if Integer.is_zero (linearity vi e)
-      then Integer.zero
-      else raise NonLinear
-    | BinOp (op, e1, e2, _typ) ->
-      let l1 = linearity vi e1 and l2 = linearity vi e2 in
-      match op with
-      | PlusA|PlusPI -> Integer.add l1 l2
-      | MinusA|MinusPI -> Integer.sub l1 l2
-      | _ ->
-        if Integer.(is_zero l1 && is_zero l2)
-        then Integer.zero
-        else raise NonLinear
-
-  let check_support exp =
-    (* Check that the linearity of any variable is not hidden into a mem access *)
-    ignore (linearity Var.dummy exp)
-
-  let of_exp exp =
-    check_support exp;
-    (* Normalizes x + c, c + x and x - c *)
-    match Cil.constFoldToInt exp with
-    | Some i -> Const i
-    | None ->
-      match exp.Cil_types.enode with
-      | BinOp ((PlusA|PlusPI), e1, e2, _typ) ->
-        begin match Cil.constFoldToInt e1, Cil.constFoldToInt e2 with
-          | None, Some i -> Exp (e1, i)
-          | Some i, None -> Exp (e2, i)
-          | _ -> Exp (exp, Integer.zero)
-        end
-      | BinOp ((MinusA|MinusPI), e1, e2, _typ) ->
-        begin match Cil.constFoldToInt e2 with
-          | Some i -> Exp (e1, Integer.neg i)
-          | None -> Exp (exp, Integer.zero)
-        end
-      | _ -> Exp (exp, Integer.zero)
-
-  let _of_ptr ~base_offset e =
-    (* TODO: verify type compatibility between e and base_offset *)
-    match of_exp e with
-    | Exp (e, c) -> Ptroffset (e, base_offset, c)
-    | Const _ -> assert false (* should not happen ? even with absolute adresses ? *)
-    | Ptroffset _ -> assert false (* Not produced by of_exp *)
-
-  (* Convert bound to interval using oracle *)
-  let to_int_val ~oracle = function
-    | Const i -> Int_val.inject_singleton i
-    | Exp (e, i) -> Int_val.add_singleton i (oracle e)
-    | Ptroffset _ -> raise Not_implemented
-
-  let to_integer ~oracle = function
-    | Const i -> Some i
-    | Exp (e, i) ->
-      begin try
-          Some (Integer.add (Int_val.project_int (oracle e)) i)
-        with Ival.Not_Singleton_Int ->
-          None
-      end
-    | Ptroffset _ -> raise Not_implemented
-
-  let succ = function
-    | Const i -> Const (Integer.succ i)
-    | Exp (e, i) -> Exp (e, Integer.succ i)
-    | Ptroffset _ -> raise Not_implemented
-
-  let pred = function
-    | Const i -> Const (Integer.pred i)
-    | Exp (e, i) -> Exp (e, Integer.pred i)
-    | Ptroffset _ -> raise Not_implemented
-
-  let incr vi i b =
-    try
-      match b with
-      | Const _ -> Some b
-      | Exp (e, j) ->
-        let l = linearity vi e in
-        if Integer.is_zero l
-        then Some b
-        else i |> Option.map (fun i -> Exp (e, Integer.(sub j (mul l i))))
-      | Ptroffset (e, base, j) ->
-        let l = linearity vi e in
-        if Integer.is_zero l
-        then Some b
-        else
-          i |> Option.map (fun i ->
-              Ptroffset (e, base, Integer.(sub j (mul l i))))
-    with NonLinear -> None
-
-  let incr_or_constantify ~oracle vi i b =
-    match incr vi i b with
-    | Some v -> Some v
-    | None -> Option.map (fun c -> Const c) (to_integer ~oracle b)
-
-  let cmp_int i1 i2 =
-    let r = Integer.sub i1 i2 in
-    if Integer.is_zero r
-    then Equal
-    else if Integer.(lt r zero) then Lower else Greater
-
-  let cmp ~oracle b1 b2 =
-    if b1 == b2
-    then Equal
-    else
-      match b1, b2 with
-      | Const i1, Const i2 -> cmp_int i1 i2
-      | Exp (e1, i1), Exp (e2, i2) when Exp.equal e1 e2 -> cmp_int i1 i2
-      | Ptroffset _, _ | _, Ptroffset _ -> raise Not_implemented
-      | _, _ ->
-        let i1 = to_int_val ~oracle b1 and i2 = to_int_val ~oracle b2 in
-        let r = Int_val.(add i1 (neg i2)) in
-        match Int_val.min_and_max r with
-        | Some min, Some max when Integer.is_zero min && Integer.is_zero max ->
-          Equal
-        | Some l, _ when Integer.(ge l zero) ->
-          if Integer.(gt l zero) then Greater else GreaterOrEqual
-        | _, Some u when Integer.(le u zero) ->
-          if Integer.(lt u zero) then Lower else LowerOrEqual
-        | _ -> Uncomparable
-
-  let eq ?(oracle=no_oracle) b1 b2 =
-    cmp ~oracle b1 b2 = Equal
-
-  let lower_integer ~oracle b =
-    match Int_val.min_int (to_int_val ~oracle b) with
-    | Some l -> `Value l
-    | None ->
-      Kernel.warning ~current:true "cannot retrieve a lower bound for %a"
-        pretty b;
-      `Top
-
-  let upper_integer ~oracle b =
-    match Int_val.max_int (to_int_val ~oracle b) with
-    | Some u -> `Value u
-    | None ->
-      Kernel.warning ~current:true "cannot retrieve an upper bound for %a"
-        pretty b;
-      `Top
-
-  let lower_bound ~oracle b1 b2 =
-    if b1 == b2 || eq b1 b2 then `Value b1 else
-      let open Top in
-      let+ i1,i2 = Top.zip
-          (lower_integer ~oracle:(oracle Left) b1)
-          (lower_integer ~oracle:(oracle Right) b2) in
-      Const (Integer.min i1 i2)
-
-  let upper_bound ~oracle b1 b2 =
-    if b1 == b2 || eq b1 b2 then `Value b1 else
-      let open Top in
-      let+ i1,i2 = Top.zip
-          (upper_integer ~oracle:(oracle Left) b1)
-          (upper_integer ~oracle:(oracle Right) b2) in
-      Const (Integer.max i1 i2)
-
-  let lower_const ~oracle b =
-    let open Top in
-    lower_integer ~oracle b >>-: of_integer
-
-  let upper_const ~oracle b =
-    let open Top in
-    upper_integer ~oracle b >>-: of_integer
-end
-
-module AgingBound =
-struct
-  type age = Integer.t
-  type t = Bound.t * age
-
-  let pretty fmt (b,_) = Bound.pretty fmt b
-  let hash (b,_a) = Bound.hash b
-  let compare (b1,_a1) (b2,_a2) =
-    Bound.compare b1 b2
-  let equal_regardless_age (b1,_a1) (b2,_a2) =
-    Bound.equal b1 b2
-  let equal = equal_regardless_age
-  let pred (b,a) = (Bound.pred b, a)
-  let incr_or_constantify ~oracle vi i (b,a) =
-    Bound.incr_or_constantify ~oracle vi i b |> Option.map (fun b -> b,a)
-  let cmp ~oracle (b1,_a1) (b2,_a2) = Bound.cmp ~oracle b1 b2
-  let eq ?oracle (b1,_a1) (b2,_a2) = Bound.eq ?oracle b1 b2
-  let lower_bound ~oracle (b1,a1) (b2,a2) =
-    let open Top in
-    Bound.lower_bound ~oracle b1 b2 >>-: fun b -> b, Integer.min a1 a2
-  let upper_bound ~oracle (b1,a1) (b2,a2) =
-    let open Top in
-    Bound.upper_bound ~oracle b1 b2 >>-: fun b -> b, Integer.min a1 a2
-  let lower_const ~oracle (b,_a) = Bound.lower_const ~oracle b
-  let upper_const ~oracle (b,_a) = Bound.upper_const ~oracle b
-  let birth b = b, Integer.zero
-  let aging (b,a) = b, Integer.succ a
-  let age (_,a) = a
-  let unify_age ~other:(_,a') (b,a) = (b, Integer.min a' a)
-  let operators oracle : (module Operators with type t = t) =
-    operators (cmp ~oracle)
-end
-
-
-module type Segmentation =
-sig
-  type bound = Bound.t
-  type submemory
-  type t
-  val pretty : Format.formatter -> t -> unit
-  val hash : t -> int
-  val equal : t -> t -> bool
-  val compare : t -> t -> int
-  val hull : oracle:oracle -> t -> (bound * bound) or_top
-  val raw : t -> Bit.t
-  val weak_erase : Bit.t -> t -> t
-  val is_included : t -> t -> bool
-  val unify : oracle:bioracle -> (submemory -> submemory -> submemory) ->
-    t -> t -> t or_top
-  val single : bit -> bound -> bound -> submemory -> t
-  val read : oracle:oracle ->
-    (submemory -> 'a) -> ('a -> 'a -> 'a) -> t -> bound -> bound -> 'a
-  val update : oracle:oracle -> (submemory -> submemory or_bottom) ->
-    t -> bound -> bound -> t or_top_bottom
-  val incr_bound :
-    oracle:oracle -> Bound.Var.t -> Integer.t option -> t ->
-    [ `Value of t | `Top ]
-  val map : (submemory -> submemory) -> t -> t
-  val fold : (submemory -> 'a -> 'a) -> (bit -> 'b -> 'a) -> t -> 'b -> 'a
-  val add_segmentation_bounds : oracle:oracle -> bound list -> t -> t
-end
-
-module Segmentation (Config : Config) (M : ProtoMemory) =
-struct
-  module B = AgingBound
-
-  type bound = Bound.t
-  type submemory = M.t
-
-  type t = {
-    start: B.t;
-    segments: (M.t * B.t) list; (* should not be empty *)
-    padding: bit; (* padding at the left and right of the segmentation *)
-  }
-
-  type segments = (M.t * B.t) list
-
-  let _pretty_debug fmt (l,s) : unit =
-    Format.fprintf fmt " {%a} " B.pretty l;
-    let pp fmt (v,u) =
-      Format.fprintf fmt "%a {%a} " M.pretty v B.pretty u
-    in
-    List.iter (pp fmt) s
-
-  let pretty_segments fmt ((l : B.t), (s : (M.t * B.t) list)) : unit =
-    let pp fmt (l,v,u) =
-      let u = B.pred u in (* Upper bound is not included *)
-      (* Less strict than B.equal even with no oracles *)
-      if B.eq l u then
-        Format.fprintf fmt "[%a]%a" B.pretty l M.pretty v
-      else
-        Format.fprintf fmt "[%a .. %a]%a" B.pretty l B.pretty u M.pretty v
-    in
-    match s with
-    | [] -> Format.fprintf fmt "[]" (* should not happen *)
-    | [(v,u)] -> pp fmt (l,v,u)
-    | _ :: _ ->
-      let iter l f segments =
-        (* fold the previous upper bound = the current lower bound *)
-        ignore (List.fold_left (fun l (v,u) -> f (l,v,u) ; u) l segments)
-      in
-      pp_iter (iter l) pp fmt s
-
-  let pretty fmt (m : t) : unit =
-    pretty_segments fmt (m.start,m.segments)
-
-  let hash (m : t) : int =
-    Hashtbl.hash (
-      B.hash m.start,
-      List.map (fun (v,u) -> Hashtbl.hash (M.hash v, B.hash u)) m.segments,
-      Bit.hash m.padding)
-
-  let compare (m1 : t) (m2 : t) : int =
-    let compare_segments (v1,u1) (v2,u2) =
-      M.compare v1 v2 <?> lazy (B.compare u1 u2)
-    in
-    B.compare m1.start m2.start <?>
-    lazy (Transitioning.List.compare compare_segments m1.segments m2.segments) <?>
-    lazy (Bit.compare m1.padding m2.padding)
-
-  let equal (m1 : t) (m2 : t) : bool =
-    let equal_segments (v1,u1) (v2,u2) =
-      M.equal v1 v2 && B.equal u1 u2
-    in
-    B.equal m1.start m2.start &&
-    Transitioning.List.equal equal_segments m1.segments m2.segments &&
-    Bit.equal m1.padding m2.padding
-
-  let raw (m : t) : bit =
-    (* Perhaps some segments are empty, but we are not going to test it for now *)
-    List.fold_left
-      (fun acc (v,_u) -> Bit.join acc (M.raw v))
-      m.padding m.segments
-
-  let weak_erase (b : bit) (m : t) : t =
-    {
-      m with
-      segments = List.map (fun (v,u) -> M.weak_erase b v, u) m.segments ;
-      padding = Bit.join b m.padding ;
-    }
-
-  let is_included (m1 : t) (m2 : t) : bool =
-    let included_segments (v1,u1) (v2,u2) =
-      M.is_included v1 v2 && B.eq u1 u2
-    in
-    B.eq m1.start m2.start &&
-    Bit.is_included m1.padding m2.padding &&
-    try
-      List.for_all2 included_segments m1.segments m2.segments
-    with Invalid_argument _ -> false (* Segmentations have different sizes *)
-
-  let rec last_bound = function
-    | [] -> assert false
-    | [(_v,u)] -> u
-    | _ :: t -> last_bound t
-
-  let hull ~oracle (m : t) : (bound * bound) or_top =
-    let l = m.start and u = last_bound m.segments in
-    Top.zip (B.lower_const ~oracle l) (B.upper_const ~oracle u)
-
-  let is_empty_segment ~oracle l u =
-    let open (val (B.operators oracle)) in
-    l >= u
-
-  let check m = (* temporarily checks type invariant; TODO: remove *)
-    match m.segments with
-    | [] -> assert false
-    | _ :: _  -> m
-
-  (* Merge the two first slices of a segmentation *)
-  exception NothingToMerge
-  let smash_head ~oracle l = function
-    | [] | [_] -> raise NothingToMerge
-    | (v1,m) :: (v2,u) :: tail ->
-      let v1' = if is_empty_segment ~oracle l m then `Bottom else `Value v1
-      and v2' = if is_empty_segment ~oracle m u then `Bottom else `Value v2
-      in
-      match Bottom.join (M.smash ~oracle) v1' v2' with
-      | `Bottom -> tail
-      | `Value v -> (v,u) :: tail
-
-  let rec smash_all ~oracle l = function
-    | [] -> `Bottom, l
-    | [(v,u)] -> `Value v, u
-    | t -> smash_all ~oracle l (smash_head ~oracle l t)
-
-  (* Unify two arrays m1 and m2 *)
-  let unify ~oracle f (m1 (*Left*): t) (m2 (*Right*) : t) : t or_top =
-    let open Top in
-    (* Shortcuts *)
-    let left = oracle Left and right = oracle Right in
-    let equals side b1 b2 = B.cmp ~oracle:(oracle side) b1 b2 = Equal in
-    let smash side = Bottom.join (M.smash ~oracle:(oracle side)) in
-    let {start=l1 ; segments=s1 ; padding=p1 } = m1
-    and {start=l2 ; segments=s2 ; padding=p2 } = m2 in
-    (* Unify the segmentation start *)
-    let* start = B.lower_bound ~oracle l1 l2 in
-    let s1 = if equals Left start l1 then s1 else (M.of_raw p1, l1) :: s1
-    and s2 = if equals Right start l2 then s2 else (M.of_raw p2, l2) :: s2 in
-    (* Unify the segmentation end *)
-    let merge_first side = smash_head ~oracle:(oracle side) in
-    let unify_end l s1 s2 acc =
-      let v1, u1 = smash_all ~oracle:left l s1
-      and v2, u2 = smash_all ~oracle:right l s2 in
-      let+ u = B.upper_bound ~oracle u1 u2 in
-      let w1 =
-        if equals Left u u1 then v1 else smash Left (`Value (M.of_raw p1)) v1
-      and w2 =
-        if equals Right u u2 then v2 else smash Right (`Value (M.of_raw p2)) v2
-      in
-      match Bottom.join f w1 w2 with
-      | `Bottom -> acc (* should not happen, but acc is still correct *)
-      | `Value w -> ((w,u) :: acc)
-    in
-    (* +----+-------+-----
-       | v1 | v1'   |
-       +----+-------+-----
-       l    u1
-       +------+-------+---
-       | v2   | v2'   |
-       +------+-------+---
-       l      u2 *)
-    let rec aux l s1 s2 acc =
-      (* Look for emerging slices *)
-      let left_slice_emerges = match s1 with
-        | (v1,u1) :: t1 when equals Right l u1 -> Some (v1,u1,t1)
-        | _ -> None
-      and right_slice_emerges = match s2 with
-        | (v2,u2) :: t2 when equals Left l u2 -> Some (v2,u2,t2)
-        | _ -> None
-      in
-      match left_slice_emerges, right_slice_emerges with
-      | Some (v1,u1,t1), None -> (* left slice emerges *)
-        if equals Left l u1 then (* actually empty both sides*)
-          aux l t1 s2 acc
-        else
-          aux u1 t1 s2 ((v1,B.unify_age ~other:l u1) :: acc)
-      | None, Some (v2,u2,t2) -> (* right slice emerges *)
-        if equals Right l u2 then (* actually empty both sides *)
-          aux l s1 t2 acc
-        else
-          aux u2 s1 t2 ((v2,B.unify_age ~other:l u2) :: acc)
-      | Some _, Some _ (* both emerges, can't choose *)
-      | None, None -> (* none emerges *)
-        match s1, s2 with (* Are we done yet ? *)
-        | [], [] -> `Value acc
-        | _ :: _, [] | [], _ :: _-> unify_end l s1 s2 acc
-        | (v1,u1) :: t1, (v2,u2) :: t2 ->
-          try
-            match B.cmp ~oracle:left u1 u2, B.cmp ~oracle:right u1 u2 with (* Compare bounds *)
-            | _, Equal ->
-              (* u1 and u2 can be indeferently used right side
-                 -> use u1 as next bound
-                 Note: Asymetric choice, u2 may also be a good choice *)
-              aux u1 t1 t2 ((f v1 v2, B.unify_age ~other:u2 u1) :: acc)
-            | Equal, _ ->
-              (* u1 and u2 can be indeferently used left side
-                 -> use u2 as next bound *)
-              aux u2 t1 t2 ((f v1 v2, B.unify_age ~other:u1 u2) :: acc)
-            | (Greater | GreaterOrEqual),
-              (Greater | GreaterOrEqual | Uncomparable) ->
-              (* u1 > u2 on the left side, we are sure u2 doesn't appear left
-                 -> remove u2, merge slices *)
-              aux l s1 (merge_first Right l s2) acc
-            | (Lower | LowerOrEqual | Uncomparable),
-              (Lower | LowerOrEqual) ->
-              (* u1 < u2 on the right side, we are sure u1 doesn't appear right
-                 -> remove u1, merge slices *)
-              aux l (merge_first Left l s1) s2 acc
-            | (Greater | GreaterOrEqual), (Lower | LowerOrEqual) (* Can't choose which bound to remove first *)
-            | (Lower | LowerOrEqual | Uncomparable),
-              (Greater | GreaterOrEqual | Uncomparable) ->
-              aux l (merge_first Left l s1) (merge_first Right l s2) acc
-          with NothingToMerge -> (* There is nothing left to merge *)
-            unify_end l s1 s2 acc
-    in
-    let+ rev_segments = aux start s1 s2 [] in
-    let segments = List.rev rev_segments in
-    (* Iterate through segmentations *)
-    check { start ; segments ; padding = Bit.join p1 p2 }
-
-  let single padding lindex (* included *) uindex (* excluded *) value =
-    check {
-      padding ;
-      start = B.birth lindex ;
-      segments = [(value,B.birth uindex)] ;
-    }
-
-  let read ~oracle map reduce m lindex uindex =
-    let open (val (B.operators oracle)) in
-    let lindex = B.birth lindex and uindex = B.birth uindex in
-    let fold acc x =
-      Bottom.join reduce acc (`Value (map x))
-    in
-    let aux (l,acc) (v,u) =
-      u, if l > uindex || u <= lindex then acc else fold acc v
-    in
-    let acc = `Bottom in
-    let acc = if m.start <= lindex then acc else fold acc (M.of_raw m.padding) in
-    let last,acc = List.fold_left aux (m.start,acc) m.segments in
-    let acc = if last > uindex then acc else fold acc (M.of_raw m.padding) in
-    match acc with
-    | `Bottom -> assert false (* TODO: ensure that with typing *)
-    | `Value v -> v
-
-  let aging m = (* Extremities do not age *)
-    let rec aux acc = function
-      | [] -> acc
-      | [x] -> x :: acc
-      | (v,b) :: t -> aux ((v,B.aging b) :: acc) t
-    in
-    { m with segments = List.rev (aux [] m.segments) }
-
-  let age m = (* Age of the segmentation / age of the oldest bound *)
-    match m.segments with (* ignore m.start bound *)
-    | [] -> None
-    | (_,b) :: t ->
-      let rec aux acc = function
-        | [] -> None
-        | [_] -> Some acc (* ignore last bound *)
-        | (_,b) :: t -> aux (max acc (B.age b)) t
-      in
-      aux (B.age b) t
-
-  let remove_elderlies ~oracle m =
-    match age m with
-    | None -> m (* no inner bounds, should not happen if segments_limit > 2 *)
-    | Some oldest_age ->
-      (* Remove all bounds of this age *)
-      let rec aux acc l = function
-        | ([] | [_]) as t -> List.rev (t @ acc)
-        | ((v,u) :: t) as s ->
-          if B.age u = oldest_age then
-            aux acc l (smash_head ~oracle l s)
-          else
-            aux ((v,u) :: acc) u t
-      in
-      { m with segments = aux [] m.start m.segments }
-
-  let limit_size ~oracle m =
-    let limit = max 1 (Config.slice_limit ()) in
-    let rec aux m =
-      if List.length m.segments <= limit
-      then m
-      else aux (remove_elderlies ~oracle m)
-    in
-    aux m
-
-
-  (* TODO: partitioning strategies
-     1. reinforcement without loss
-     2. weak update without singularization
-     3. update reduces the number of segments to 3 *)
-  let update ~oracle f m lindex (* included *) uindex (* excluded *) = (* lindex < uindex *)
-    let open TopBot in
-    let open (val (B.operators oracle)) in
-    let same_bounds = lindex == uindex in (* happens when adding partitioning hints *)
-    let lindex = B.birth lindex and uindex = B.birth uindex in
-    (* (start,head) : segmentation kept identical below the update indexes,
-                      head is a list in reverse order
-       (l,v,u) : the segment (l,u) beeing overwriten with previous value v
-
-       head = (_,l) :: _
-    *)
-    let rec aux_before l s =
-      if lindex >= l
-      then aux_below l [] l s
-      else
-        let* l = B.lower_bound ~oracle:(fun _ -> oracle) lindex l in
-        aux_over l [] l (M.of_raw m.padding) l s
-    and aux_below start head l = fun t ->
-      match t with (* l <= lindex *)
-      | [] ->
-        aux_end start head l (M.of_raw m.padding) uindex []
-      | (v,u) :: t ->
-        if lindex >= u
-        then aux_below start ((v,u) :: head) u t
-        else aux_over start head l v u t
-    and aux_over start head l v u s = (* l <= lindex *)
-      if uindex <= u then
-        aux_end start head l v u s
-      else
-        match s with
-        | [] ->
-          let* u = B.upper_bound ~oracle:(fun _ -> oracle) u uindex in
-          let v = M.smash ~oracle v (M.of_raw m.padding) in
-          aux_end start head l v u []
-        | (v',u') :: t ->
-          (* TODO: do not smash for overwrites if the slices are covered by the write *)
-          aux_over start head l (M.smash ~oracle v v') u' t
-    and aux_end start head l v u tail = (* l <= lindex < uindex <= u*)
-      let+ new_v = f v in
-      let previous_is_empty = is_empty_segment ~oracle l lindex
-      and next_is_empty = is_empty_segment ~oracle uindex u in
-      let tail' =
-        (if previous_is_empty then [] else [(v,lindex)]) @
-        (if same_bounds then [] else [(new_v,uindex)]) @
-        (if next_is_empty then [] else [(v,u)]) @
-        tail
-      and head',start' = match head with (* change last bound to match lindex *)
-        | (v',_u) :: t when previous_is_empty -> (v',lindex) :: t, start
-        | [] when previous_is_empty -> [], lindex
-        | head -> head, start
-      in
-      check {
-        padding = m.padding;
-        segments = List.rev_append head' tail';
-        start = start';
-      }
-    in
-    aux_before m.start m.segments >>-:
-    aging >>-:
-    limit_size ~oracle
-
-  let remove_empty_segments m =
-    let unify_head_age (b' : B.t) : segments -> segments = function
-      | [] -> [] (* Start of segmentation, age is 0, do nothing *)
-      | (v,b) :: t -> (v, B.unify_age ~other:b' b) :: t
-    in
-    let rec aux l acc = function
-      | [] -> List.rev acc
-      | (v,u) :: t ->
-        (* Oracle cannot be used in case of incr here as it represents the
-           value of the bounds before the instruction, while we iterate
-           here over a segmentation interpreted after the instruction. *)
-        if B.equal_regardless_age l u then (* empty segment, remove v *)
-          aux l (unify_head_age u acc) t
-        else
-          aux u ((v,u) :: acc) t
-    in
-    check { m with segments = aux m.start [] m.segments }
-
-  let incr_bound ~oracle vi x m =
-    let incr = B.incr_or_constantify ~oracle vi x in
-    let rec incr_start p l = function
-      | [] -> `Top (* No more segments, top segmentation *)
-      | (v,u) :: t as s ->
-        match incr l with
-        | Some l' -> incr_end p l' (List.rev s)
-        | None -> (* No replacement, try to find a lower bound instead *)
-          match B.lower_const ~oracle l with
-          | `Top -> (* No lower bound, completely remove the segment *)
-            let p' = Bit.join p (M.raw v) in
-            incr_start p' u t
-          | `Value l' ->
-            let v' = M.smash ~oracle (M.of_raw p) v in
-            incr_end p (B.birth l') (List.rev ((v',u) :: t))
-    and incr_end p l = function (* In reverse order *)
-      | [] -> `Top (* No more segments, top segmentation *)
-      | (v,u) :: t ->
-        match incr u with
-        | Some u' -> incr_inner p l [] ((v,u') :: t)
-        | None -> (* No replacement, try to find an upper bound instead *)
-          match B.upper_const ~oracle u with
-          | `Top -> (* No upper bound, completely remove the segment *)
-            let p' = Bit.join p (M.raw v) in
-            incr_end p' l t
-          | `Value u' ->
-            let v' = M.smash ~oracle (M.of_raw p) v in
-            incr_inner p l [] ((v',B.birth u') :: t)
-    and incr_inner p l acc (* In right order *) = function (* In reverse order *)
-      | [] -> assert false
-      | [s] ->
-        check { start=l ; padding=p ; segments=s :: acc } |>
-        remove_empty_segments |>
-        fun m -> `Value m
-      | (v1,u) :: (v2,m) :: t -> (* u is already increased *)
-        match incr m with
-        | Some m' ->
-          incr_inner p l ((v1,u) :: acc) ((v2,m') :: t)
-        | None ->
-          let v' = M.smash ~oracle v1 v2 in
-          incr_inner p l acc ((v',u) :: t)
-    in
-    incr_start m.padding m.start m.segments
-
-  let map f m =
-    check { m with segments=List.map (fun (v,u) -> f v, u) m.segments }
-
-  let fold fs fp m acc =
-    List.fold_left (fun acc (v,_) -> fs v acc) (fp m.padding acc) m.segments
-
-  let add_segmentation_bounds ~oracle bounds m =
-    let open TopBot in
-    let add_bound m b =
-      match update ~oracle (fun x -> `Value x) m b b with
-      | `Value m -> m
-      | `Bottom -> assert false
-      | `Top ->
-        Kernel.warning ~current:true
-          "failed to introduce %a inside the array segmentation"
-          Bound.pretty b;
-        m
-    in
-    List.fold_left add_bound m bounds
-end
-
-
-(* ------------------------------------------------------------------------ *)
-(* --- Typed memory abstraction                                         --- *)
-(* ------------------------------------------------------------------------ *)
-
-module TypedMemory (Config : Config) (V : Value) =
-struct
-  (* Recursively instanciate the typed memory *)
-  module rec ProtoMemory : ProtoMemory with type value = V.t =
-  struct
-    type value = V.t
-
-    type t =
-      | Raw of bit
-      | Scalar of memory_scalar
-      | Struct of memory_struct
-      | Disjunct of memory_disjunct
-      | Union of memory_union
-      | Array of memory_array
-    and memory_scalar = {
-      scalar_value: V.t;
-      scalar_type: Cil_types.typ;
-    }
-    and memory_struct = {
-      struct_value: S.t;
-      struct_type: Cil_types.compinfo;
-    }
-    and memory_disjunct = {
-      disj_value: D.t;
-      disj_type: Cil_types.compinfo;
-    }
-    (* unions are handled separately from struct to avoid confusion and error *)
-    and memory_union = {
-      union_value: t;
-      union_field: Cil_types.fieldinfo;
-      union_padding: bit;
-    }
-    and memory_array = {
-      array_value: A.t;
-      array_cell_type: Cil_types.typ;
-    }
-
-
-    let are_scalar_compatible s1 s2 =
-      are_typ_compatible s1.scalar_type s2.scalar_type
-
-    let are_aray_compatible a1 a2 =
-      are_typ_compatible a1.array_cell_type a2.array_cell_type
-
-    let are_compinfo_compatible ci1 ci2 =
-      ci1.Cil_types.ckey = ci2.Cil_types.ckey
-
-    let are_structs_compatible s1 s2 =
-      s1.struct_type.ckey = s2.struct_type.ckey
-
-    let are_disjuncts_compatible d1 d2 =
-      d1.disj_type.ckey = d2.disj_type.ckey
-
-    let are_union_compatible u1 u2 =
-      Cil_datatype.Fieldinfo.equal u1.union_field u2.union_field
-
-    let rec pp ~root fmt =
-      let prefix fmt =
-        if not root then Format.fprintf fmt " = "
-      in
-      function
-      | Raw b ->
-        Format.fprintf fmt "%t%a" prefix Bit.pretty b
-      | Scalar { scalar_value } ->
-        Format.fprintf fmt "%t%a" prefix V.pretty scalar_value
-      | Struct s ->
-        Format.fprintf fmt "%t%a" prefix S.pretty s.struct_value
-      | Disjunct d ->
-        Format.fprintf fmt "%t%a" prefix D.pretty d.disj_value
-      | Union u ->
-        Format.fprintf fmt ".%s%a"
-          u.union_field.Cil_types.fname
-          (pp ~root:false) u.union_value
-      | Array a ->
-        Format.fprintf fmt "%a" A.pretty a.array_value
-
-    let pretty fmt m = pp ~root:false fmt m
-    let pretty_root fmt m =
-      Format.fprintf fmt "@[<hv>%a@]" (pp ~root:true) m
-
-    let rec hash m = match m with
-      | Raw b -> Hashtbl.hash (
-          1,
-          Bit.hash b)
-      | Scalar s -> Hashtbl.hash (
-          2,
-          V.hash s.scalar_value,
-          Cil_datatype.Typ.hash s.scalar_type)
-      | Struct s -> Hashtbl.hash (
-          3,
-          S.hash s.struct_value,
-          Cil_datatype.Compinfo.hash s.struct_type)
-      | Disjunct d -> Hashtbl.hash (
-          4,
-          D.hash d.disj_value,
-          Cil_datatype.Compinfo.hash d.disj_type
-        )
-      | Union u -> Hashtbl.hash (
-          5,
-          hash u.union_value,
-          Cil_datatype.Fieldinfo.hash u.union_field,
-          Bit.hash u.union_padding)
-      | Array a -> Hashtbl.hash (
-          6,
-          A.hash a.array_value,
-          Cil_datatype.Typ.hash a.array_cell_type)
-
-    let rec equal m1 m2 =
-      match m1, m2 with
-      | Raw b1, Raw b2 -> Bit.equal b1 b2
-      | Scalar s1, Scalar s2 ->
-        V.equal s1.scalar_value s2.scalar_value &&
-        Cil_datatype.Typ.equal s1.scalar_type s2.scalar_type
-      | Struct s1, Struct s2 ->
-        S.equal s1.struct_value s2.struct_value &&
-        Cil_datatype.Compinfo.equal s1.struct_type s2.struct_type
-      | Disjunct d1, Disjunct d2 ->
-        D.equal d1.disj_value d2.disj_value &&
-        Cil_datatype.Compinfo.equal d1.disj_type d2.disj_type
-      | Union u1, Union u2 ->
-        equal u1.union_value u2.union_value &&
-        Bit.equal u1.union_padding u2.union_padding &&
-        Cil_datatype.Fieldinfo.equal u1.union_field u2.union_field
-      | Array a1, Array a2 ->
-        A.equal a1.array_value a2.array_value &&
-        Cil_datatype.Typ.equal a1.array_cell_type a2.array_cell_type
-      | (Raw _ | Scalar _ | Struct _ | Disjunct _ |  Union _ | Array _), _ ->
-        false
-
-    let rec compare m1 m2 =
-      let (<?>) c (cmp,x,y) =
-        if c = 0 then cmp x y else c
-      in
-      match m1, m2 with
-      | Raw b1, Raw b2 -> Bit.compare b1 b2
-      | Scalar s1, Scalar s2 ->
-        V.compare s1.scalar_value s2.scalar_value <?>
-        (Cil_datatype.Typ.compare, s1.scalar_type, s2.scalar_type)
-      | Struct s1, Struct s2 ->
-        S.compare s1.struct_value s2.struct_value <?>
-        (Cil_datatype.Compinfo.compare, s1.struct_type, s2.struct_type)
-      | Disjunct d1, Disjunct d2 ->
-        D.compare d1.disj_value d2.disj_value <?>
-        (Cil_datatype.Compinfo.compare, d1.disj_type, d2.disj_type)
-      | Union u1, Union u2 ->
-        compare u1.union_value u2.union_value <?>
-        (Bit.compare, u1.union_padding, u2.union_padding) <?>
-        (Cil_datatype.Fieldinfo.compare, u1.union_field, u2.union_field)
-      | Array a1, Array a2 ->
-        A.compare a1.array_value a2.array_value <?>
-        (Cil_datatype.Typ.compare, a1.array_cell_type, a2.array_cell_type)
-      | Raw _, _ -> 1
-      | _, Raw _ -> -1
-      | Scalar _, _ -> 1
-      | _, Scalar _ -> -1
-      | Struct _, _ -> 1
-      | _, Struct _ -> -1
-      | Disjunct _, _ -> 1
-      | _, Disjunct _ -> -1
-      | Union _, _ -> 1
-      | _, Union _ -> -1
-
-    let of_raw b = Raw b
-
-    let rec raw m = match m with
-      | Raw b -> b
-      | Scalar s -> V.to_bit s.scalar_value
-      | Struct s -> S.raw s.struct_value
-      | Disjunct d -> D.raw d.disj_value
-      | Union u -> raw u.union_value
-      | Array a -> A.raw a.array_value
-
-    let of_value scalar_type scalar_value =
-      Scalar { scalar_type ; scalar_value }
-
-    let to_value typ = function
-      | Scalar s when are_typ_compatible s.scalar_type typ -> s.scalar_value
-      | m -> V.of_bit ~typ (raw m)
-
-    let rec to_singleton_int = function
-      | Scalar s -> V.to_integer s.scalar_value
-      | Raw (Zero _) -> Some Integer.zero
-      | Union u -> to_singleton_int u.union_value
-      | _ -> None
-
-    let rec weak_erase b = function
-      | Raw b' ->
-        Raw (Bit.join b' b)
-      | Scalar s when Bit.is_any b ->
-        Raw (Bit.join (V.to_bit s.scalar_value) b)
-      | Scalar s ->
-        let typ = s.scalar_type in
-        Scalar { s with scalar_value = V.(join (of_bit ~typ b) s.scalar_value) }
-      | Array a ->
-        Array { a with array_value = A.weak_erase b a.array_value }
-      | Struct s ->
-        Struct { s with struct_value = S.weak_erase b s.struct_value }
-      | Disjunct d ->
-        Disjunct { d with disj_value = D.weak_erase d.disj_type b d.disj_value }
-      | Union u -> Union {
-          u with
-          union_padding = Bit.join u.union_padding b;
-          union_value = weak_erase b u.union_value;
-        }
-
-    let rec is_included m1 m2 = match m1, m2 with
-      | _, Raw r -> Bit.is_included (raw m1) r
-      | Scalar s1, Scalar s2 ->
-        are_scalar_compatible s1 s2 &&
-        V.is_included s1.scalar_value s2.scalar_value
-      | Array a1, Array a2 ->
-        are_aray_compatible a1 a2 &&
-        A.is_included a1.array_value a2.array_value
-      | Struct s1, Struct s2 ->
-        are_structs_compatible s1 s2 &&
-        S.is_included s1.struct_value s2.struct_value
-      | Disjunct d1, Disjunct d2 ->
-        are_disjuncts_compatible d1 d2 &&
-        D.is_included d1.disj_value d2.disj_value
-      | Union u1, Union u2 ->
-        are_union_compatible u1 u2 &&
-        Bit.is_included u1.union_padding u2.union_padding &&
-        is_included u1.union_value u2.union_value
-      | (Raw _ | Scalar _ | Array _ | Struct _ | Disjunct _ | Union _), _ ->
-        false
-
-    let to_struct ~oracle ci = function
-      | Struct s when are_compinfo_compatible s.struct_type ci ->
-        s.struct_value
-      | Disjunct d when are_compinfo_compatible d.disj_type ci ->
-        D.to_struct ~oracle d.disj_value
-      | m -> S.of_raw (raw m)
-
-    let to_disj ci = function
-      | Struct s when are_compinfo_compatible s.struct_type ci ->
-        D.of_struct ci s.struct_value
-      | Disjunct d when are_compinfo_compatible d.disj_type ci ->
-        d.disj_value
-      | m -> D.of_raw ci (raw m)
-
-    let unify ~oracle f =
-      let open Top in
-      let raw_to_array side prototype b =
-        A.hull ~oracle:(oracle side) prototype >>-:
-        fun (l,u) -> A.single b l u (Raw b)
-      in
-      let rec aux m1 m2 =
-        match m1, m2 with
-        | Raw b1, Raw b2 -> Raw (Bit.join b1 b2)
-        | Scalar s1, Scalar s2
-          when are_typ_compatible s1.scalar_type s2.scalar_type ->
-          let size = typ_size s1.scalar_type in
-          let scalar_value = f ~size s1.scalar_value s2.scalar_value in
-          Scalar { s1 with scalar_value }
-        | Array a1, Array a2 when are_aray_compatible a1 a2 ->
-          begin match A.unify ~oracle aux a1.array_value a2.array_value with
-            | `Top -> Raw (Bit.join (raw m1) (raw m2))
-            | `Value array_value -> Array { a1 with array_value }
-          end
-        | Array a1, Raw b2 ->
-          let m =
-            let* array_value2 = raw_to_array Left a1.array_value b2 in
-            let+ array_value = A.unify ~oracle aux a1.array_value array_value2 in
-            Array { a1 with array_value }
-          in
-          begin match m with
-            | `Top -> weak_erase b2 (Array a1) (* Should not happen unless oracle is very unprecise *)
-            | `Value m -> m
-          end
-        | Raw b1, Array a2 ->
-          let m =
-            let* array_value1 = raw_to_array Right a2.array_value b1 in
-            let+ array_value = A.unify ~oracle aux array_value1 a2.array_value in
-            Array { a2 with array_value }
-          in
-          begin match m with
-            | `Top -> weak_erase b1 (Array a2) (* Should not happen unless oracle is very unprecise *)
-            | `Value m -> m
-          end
-        | Struct s1, Struct s2 when are_structs_compatible s1 s2 ->
-          let struct_value = S.unify aux s1.struct_value s2.struct_value in
-          Struct { s1 with struct_value }
-        | Struct s, Raw _ | Raw _, Struct s ->
-          (* Previously implemented as weak_erase, it is useful to continue
-             the recursive unification as there can be an emerging array segment
-             in the sub structure. *)
-          let v1 = to_struct ~oracle:(oracle Left) s.struct_type m1
-          and v2 = to_struct ~oracle:(oracle Right) s.struct_type m2 in
-          let struct_value = S.unify aux v1 v2 in
-          Struct { s with struct_value }
-        | Disjunct d1, Disjunct d2 when are_disjuncts_compatible d1 d2 ->
-          let disj_value = D.unify ~oracle aux d1.disj_value d2.disj_value in
-          Disjunct { d1 with disj_value }
-        | Disjunct d, Raw _ | Raw _, Disjunct d ->
-          let v1 = to_disj d.disj_type m1
-          and v2 = to_disj d.disj_type m2 in
-          let disj_value = D.unify ~oracle aux v1 v2 in
-          Disjunct { d with disj_value }
-        | Union u1, Union u2 when are_union_compatible u1 u2 ->
-          Union {
-            u1 with
-            union_value = aux u1.union_value u2.union_value;
-            union_padding = Bit.join u1.union_padding u2.union_padding;
-          }
-        | m, Raw b | Raw b, m -> weak_erase b m
-        | _,_ -> Raw (Bit.join (raw m1) (raw m2))
-      in
-      aux
-
-    let join ~oracle = unify ~oracle (fun ~size:_ -> V.join)
-
-    let smash ~oracle = join ~oracle:(fun _ -> oracle)
-
-    let read ~oracle (map : Cil_types.typ -> t -> 'a) (reduce : 'a -> 'a -> 'a) =
-      let rec aux offset m =
-        match offset, m with
-        | NoOffset t, m ->
-          map t m
-        | Field (fi, offset'), Struct s
-          when s.struct_type.ckey = fi.fcomp.ckey ->
-          aux offset' (S.read s.struct_value fi)
-        | Field (fi, offset'), Disjunct d
-          when d.disj_type.ckey = fi.fcomp.ckey ->
-          D.read (aux offset') reduce d.disj_value fi
-        | Field (fi, offset'), Union u
-          when Cil_datatype.Fieldinfo.equal u.union_field fi ->
-          aux offset' u.union_value
-        | Index (exp, index, elem_type, offset'), Array a
-          when are_typ_compatible a.array_cell_type elem_type ->
-          let open Top in
-          let m =
-            let+ lindex, uindex = match Option.map Bound.of_exp exp with
-              | Some b -> `Value (b, b)
-              | None | exception Bound.UnsupportedBoundExpression ->
-                let l, u = Int_val.min_and_max index in
-                let+ l = Top.of_option l
-                and+ u = Top.of_option u in
-                Bound.of_integer l, Bound.of_integer u
-            in
-            A.read ~oracle (aux offset') reduce a.array_value lindex uindex
-          in
-          begin match m with
-            | `Value v -> v
-            | `Top ->
-              A.fold
-                (fun m' acc -> reduce (aux offset' m') acc)
-                (fun p () -> aux offset' (Raw p))
-                a.array_value ()
-          end
-        | _, m -> (* structure mismatch *)
-          let r = Raw (raw m) in
-          match offset with
-          | NoOffset t -> map t r
-          | Field (_, offset') | Index (_, _, _, offset') -> aux offset' r
-      in
-      aux
-
-    let update ~oracle (f : weak:bool -> Cil_types.typ -> t -> t or_bottom) =
-      let open Bot in
-      let rec aux ~weak offset m =
-        match offset with
-        | NoOffset t ->
-          f ~weak t m
-        | Field (fi, offset') ->
-          if fi.fcomp.cstruct then (* Structures *)
-            if Config.disjunctive_invariants () then
-              let old = to_disj fi.fcomp m in
-              let+ disj_value = D.update ~oracle (aux ~weak offset') old fi in
-              Disjunct { disj_type = fi.fcomp ; disj_value }
-            else
-              let old = to_struct ~oracle fi.fcomp m in
-              let+ struct_value = S.update (aux ~weak offset') old fi in
-              Struct { struct_type = fi.fcomp; struct_value }
-          else (* Unions *)
-            let old = match m with
-              | Union u when Cil_datatype.Fieldinfo.equal u.union_field fi -> u
-              | _ ->
-                let b = raw m in
-                { union_value = Raw b ; union_field = fi ; union_padding = b }
-            in
-            let+ union_value = aux ~weak offset' old.union_value in
-            Union { old with union_value }
-        | Index (exp, index, elem_type, offset') ->
-          let m' =
-            let open TopBot in
-            let* lindex, uindex, weak =
-              match Option.map Bound.of_exp exp with
-              | Some b -> `Value (b, Bound.succ b, weak)
-              | None | exception Bound.UnsupportedBoundExpression ->
-                begin match Int_val.min_and_max index with
-                  | None, _ | _, None -> `Top
-                  | Some l, Some u ->
-                    let l' = Bound.of_integer l
-                    and u' = Bound.(succ (of_integer u)) in
-                    `Value (l', u', weak || Integer.equal l u)
-                end
-            in
-            match m with
-            | Array a when are_typ_compatible a.array_cell_type elem_type ->
-              let+ array_value = A.update ~oracle (aux ~weak offset')
-                  a.array_value lindex uindex in
-              Array { a with array_value }
-            | _ ->
-              let b = raw m in
-              let+ new_value = aux ~weak offset' (Raw b) in
-              let array_value = A.single b lindex uindex new_value in
-              Array { array_cell_type = elem_type ; array_value }
-          in
-          match m' with
-          | `Top -> (* No suitable bound for the partition *)
-            let+ v = f ~weak:true Cil.voidType m in v
-          | `Bottom | `Value _ as m -> m
-      in aux
-
-    let incr_bound ~oracle vi x m = (* TODO: keep subtree when nothing changes *)
-      let rec aux = function
-        | (Raw _ | Scalar _) as m -> m
-        | Struct s -> Struct { s with struct_value = S.map aux s.struct_value }
-        | Disjunct d -> Disjunct { d with disj_value = D.map aux d.disj_value }
-        | Union u -> Union { u with union_value = aux u.union_value }
-        | Array a ->
-          match  A.incr_bound ~oracle vi x a.array_value with
-          | `Top -> Raw (A.raw a.array_value)
-          | `Value array_value ->
-            Array { a with array_value=A.map aux array_value }
-      in
-      aux m
-
-    let add_segmentation_bounds ~oracle ~typ bounds =
-      let convert_bound exp =
-        try
-          Some (Bound.of_exp exp)
-        with Bound.UnsupportedBoundExpression -> None
-      in
-      let bounds = List.filter_map convert_bound bounds in
-      function
-      | (Raw b as m) ->
-        begin match bounds with
-          | l :: u :: t ->
-            let array_value = A.single b l u m in
-            let array_value = A.add_segmentation_bounds ~oracle t array_value in
-            Array { array_cell_type = typ ; array_value }
-          | _ -> m (* Can't build a segmentation with only one bound *)
-        end
-      | Array a ->
-        let array_value = A.add_segmentation_bounds ~oracle bounds a.array_value
-        in
-        Array { a with array_value }
-      | m -> m (* Ignore segmentation hints on non-array *)
-  end
-  and S : Structures with type submemory = ProtoMemory.t =
-    Structures (Config) (ProtoMemory)
-  and A : Segmentation with type submemory = ProtoMemory.t =
-    Segmentation (Config) (ProtoMemory)
-  and D : Disjunction with type submemory = ProtoMemory.t and type structure = S.t =
-    Disjunction (Config) (ProtoMemory) (S)
-
-  include ProtoMemory
-
-  type location = Abstract_offset.t
-
-  let pretty = pretty_root
-
-  (* Constuctors *)
-
-  let top = of_raw Bit.top
-  let zero = of_raw Bit.zero
-  let is_top m = m = top
-
-  (* Widening *)
-
-  let widen = unify
-
-  (* Read/Write accesses *)
-
-  let get ~oracle (m : t) (loc : location) : value =
-    read ~oracle to_value V.join loc m
-
-  let extract ~oracle (m : t) (loc : location) : t =
-    read ~oracle (fun _typ x -> x) (smash ~oracle) loc m
-
-  let update' ~oracle ~weak f offset m =
-    let f' ~weak typ m =
-      `Value (f ~weak typ m)
-    in
-    Bottom.non_bottom (update ~oracle ~weak f' offset m)
-
-  let set ~oracle ~weak m offset new_v =
-    let f ~weak typ m =
-      of_value typ (if weak then V.join (to_value typ m) new_v else new_v)
-    in
-    update' ~oracle ~weak f offset m
-
-  let reinforce ~oracle f m offset =
-    let open Bottom in
-    let f' ~weak typ m =
-      if weak
-      then `Value m
-      else f (to_value typ m) >>-: of_value typ
-    in
-    update ~oracle ~weak:false f' offset m
-
-  let erase ~oracle ~weak m offset b =
-    let f ~weak _typ m =
-      if weak then
-        weak_erase b m
-      else
-        of_raw b
-    in
-    update' ~oracle ~weak f offset m
-
-  let overwrite ~oracle ~weak dst offset src =
-    let f ~weak _typ m =
-      if weak then
-        smash ~oracle m src
-      else
-        src
-    in
-    update' ~oracle ~weak f offset dst
-
-  let segmentation_hint ~oracle m offset bounds =
-    let f ~weak:_ typ m =
-      add_segmentation_bounds ~oracle ~typ bounds m
-    in
-    update' ~oracle ~weak:false f offset m
-end
diff --git a/src/plugins/value/domains/multidim/abstract_memory.ml b/src/plugins/value/domains/multidim/abstract_memory.ml
new file mode 100644
index 00000000000..ce501db31be
--- /dev/null
+++ b/src/plugins/value/domains/multidim/abstract_memory.ml
@@ -0,0 +1,194 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    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 Lattice_extrema
+
+
+(* Composition operator for compare function *)
+
+let (<?>) c lcmp =
+  if c = 0 then 0 else Lazy.force lcmp
+
+
+(* Imprecise bits abstraction *)
+
+type initialization =
+  | SurelyInitialized
+  | MaybeUninitialized
+
+type bit =
+  | Uninitialized
+  | Zero of initialization
+  | Any of Base.SetLattice.t * initialization
+
+module Initialization =
+struct
+  let pretty fmt = function
+    | SurelyInitialized -> ()
+    | MaybeUninitialized ->
+      Format.fprintf fmt "or UNINITIALIZED"
+
+  let hash = function
+    | SurelyInitialized -> 3
+    | MaybeUninitialized -> 7
+
+  let equal i1 i2 =
+    match i1, i2 with
+    | SurelyInitialized, SurelyInitialized
+    | MaybeUninitialized, MaybeUninitialized -> true
+    | _, _ -> false
+
+  let compare i1 i2 =
+    match i1, i2 with
+    | SurelyInitialized, SurelyInitialized
+    | MaybeUninitialized, MaybeUninitialized -> 0
+    | SurelyInitialized, MaybeUninitialized -> -1
+    | MaybeUninitialized, SurelyInitialized -> 1
+
+  let is_included i1 i2 =
+    match i1, i2 with
+    | SurelyInitialized, _
+    | _, MaybeUninitialized -> true
+    | MaybeUninitialized, SurelyInitialized -> false
+
+  let join i1 i2 =
+    match i1, i2 with
+    | SurelyInitialized, SurelyInitialized -> SurelyInitialized
+    | MaybeUninitialized, _
+    | _, MaybeUninitialized -> MaybeUninitialized
+end
+
+module Bit =
+struct
+  module Bases = Base.SetLattice
+
+  type t = bit
+
+  let uninitialized = Uninitialized
+  let zero = Zero SurelyInitialized
+  let numerical = Any (Bases.empty, SurelyInitialized)
+  let top = Any (Bases.top, MaybeUninitialized)
+
+
+  let pretty fmt = function
+    | Uninitialized ->
+      Format.fprintf fmt "UNINITIALIZED"
+    | Zero i ->
+      Format.fprintf fmt "0%a" Initialization.pretty i
+    | Any (Set set, i) when Base.SetLattice.O.is_empty set ->
+      Format.fprintf fmt "[--..--]%a" Initialization.pretty i
+    | Any _ -> Format.fprintf fmt "T"
+
+  let is_any = function Any _ -> true | _ -> false
+
+  let hash = function
+    | Uninitialized -> 7
+    | Zero i -> Hashtbl.hash (3, Initialization.hash i)
+    | Any (set, i)-> Hashtbl.hash (53, Bases.hash set, Initialization.hash i)
+
+  let equal d1 d2 =
+    match d1,d2 with
+    | Uninitialized, Uninitialized -> true
+    | Zero i1, Zero i2 -> Initialization.equal i1 i2
+    | Any (set1,i1), Any (set2,i2) ->
+      Bases.equal set1 set2 && Initialization.equal i1 i2
+    | _, _ -> false
+
+  let compare d1 d2 =
+    match d1,d2 with
+    | Uninitialized, Uninitialized -> 0
+    | Zero i1, Zero i2 -> Initialization.compare i1 i2
+    | Any (set1,i1), Any (set2,i2) ->
+      Bases.compare set1 set2 <?> lazy (Initialization.compare i1 i2)
+    | Uninitialized, _ -> 1
+    | _, Uninitialized -> -1
+    | Zero _, _ -> 1
+    | _, Zero _-> -1
+
+  let initialization = function
+    | Uninitialized -> MaybeUninitialized
+    | Zero i -> i
+    | Any (_,i) -> i
+
+  let is_included d1 d2 =
+    Initialization.is_included (initialization d1) (initialization d2) &&
+    match d1, d2 with
+    | Uninitialized, _ -> true
+    | _, Uninitialized -> false
+    | Zero _, _ -> true
+    | _, Zero _ -> false
+    | Any (set1,_), Any (set2,_) -> Bases.is_included set1 set2
+
+  let join d1 d2 =
+    match d1, d2 with
+    | Uninitialized, Uninitialized -> Uninitialized
+    | Zero i1, Zero i2 ->
+      Zero (Initialization.join i1 i2)
+    | Any (set1,i1), Any (set2,i2) ->
+      Any (Bases.join set1 set2, Initialization.join i1 i2)
+    | Zero _, Uninitialized
+    | Uninitialized, Zero _ -> Zero MaybeUninitialized
+    | Any (set,i), (Zero _ | Uninitialized as b)
+    | (Zero _ | Uninitialized as b), Any (set,i) ->
+      Any (set, Initialization.join i (initialization b))
+end
+
+
+(* Early stage of memory abstraction building *)
+
+type size = Integer.t
+type side = Left | Right
+type oracle = Cil_types.exp -> Int_val.t
+type bioracle = side -> oracle
+
+module type ProtoMemory =
+sig
+  type t
+  type value
+
+  val pretty : Format.formatter -> t -> unit
+  val pretty_root : Format.formatter -> t -> unit
+  val hash : t -> int
+  val equal : t -> t -> bool
+  val compare : t -> t -> int
+
+  val of_raw : bit -> t
+  val raw : t -> bit
+  val of_value : Cil_types.typ -> value -> t
+  val to_value : Cil_types.typ -> t -> value
+  val to_singleton_int : t -> Integer.t option
+  val weak_erase : bit -> t -> t
+  val is_included : t -> t -> bool
+  val unify : oracle:bioracle ->
+    (size:size -> value -> value -> value) -> t -> t -> t
+  val join : oracle:bioracle -> t -> t -> t
+  val smash : oracle:oracle -> t -> t -> t
+  val read : oracle:oracle -> (Cil_types.typ -> t -> 'a) -> ('a -> 'a -> 'a) ->
+    Abstract_offset.t -> t -> 'a
+  val update : oracle:oracle ->
+    (weak:bool -> Cil_types.typ -> t -> t or_bottom) ->
+    weak:bool -> Abstract_offset.t -> t -> t or_bottom
+  val incr_bound : oracle:oracle -> Cil_types.varinfo -> Integer.t option ->
+    t -> t
+  val add_segmentation_bounds : oracle:oracle -> typ:Cil_types.typ ->
+    Cil_types.exp list -> t -> t
+end
diff --git a/src/plugins/value/domains/multidim/abstract_memory.mli b/src/plugins/value/domains/multidim/abstract_memory.mli
new file mode 100644
index 00000000000..46287f68168
--- /dev/null
+++ b/src/plugins/value/domains/multidim/abstract_memory.mli
@@ -0,0 +1,100 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    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 Lattice_extrema
+
+(* Memory initialization *)
+type initialization =
+  | SurelyInitialized
+  | MaybeUninitialized
+
+(* Abstraction of an unstructured bit in the memory *)
+type bit =
+  | Uninitialized (* Uninitialized everywhere *)
+  | Zero of initialization (* Zero or uninitialized everywhere *)
+  | Any of Base.SetLattice.t * initialization
+  (* Undetermined anywhere, and can contain bits
+     of pointers. If the base set is empty,
+     the bit can only come from numerical values. *)
+
+module Bit :
+sig
+  type t = bit
+
+  val uninitialized : t
+  val zero : t
+  val numerical : t
+  val top : t
+
+  val is_any : t -> bool
+  val initialization : t -> initialization
+
+  val pretty : Format.formatter -> t -> unit
+  val hash : t -> int
+  val equal : t -> t -> bool
+  val compare : t -> t -> int
+
+  val is_included : t -> t -> bool
+  val join : t -> t -> t
+end
+
+(* Size type for memory abstraction *)
+type size = Integer.t
+
+(* Oracles for memory abstraction *)
+type side = Left | Right
+type oracle = Cil_types.exp -> Int_val.t
+type bioracle = side -> oracle
+
+(* Early stage of memory abstraction building *)
+module type ProtoMemory =
+sig
+  type t
+  type value
+
+  val pretty : Format.formatter -> t -> unit
+  val pretty_root : Format.formatter -> t -> unit
+  val hash : t -> int
+  val equal : t -> t -> bool
+  val compare : t -> t -> int
+
+  val of_raw : bit -> t
+  val raw : t -> bit
+  val of_value : Cil_types.typ -> value -> t
+  val to_value : Cil_types.typ -> t -> value
+  val to_singleton_int : t -> Integer.t option
+  val weak_erase : bit -> t -> t
+  val is_included : t -> t -> bool
+  val unify : oracle:bioracle ->
+    (size:size -> value -> value -> value) -> t -> t -> t
+  val join : oracle:bioracle -> t -> t -> t
+  val smash : oracle:oracle -> t -> t -> t
+  val read : oracle:oracle -> (Cil_types.typ -> t -> 'a) -> ('a -> 'a -> 'a) ->
+    Abstract_offset.t -> t -> 'a
+  val update : oracle:oracle ->
+    (weak:bool -> Cil_types.typ -> t -> t or_bottom) ->
+    weak:bool -> Abstract_offset.t -> t -> t or_bottom
+  val incr_bound : oracle:oracle -> Cil_types.varinfo -> Integer.t option ->
+    t -> t
+  val add_segmentation_bounds : oracle:oracle -> typ:Cil_types.typ ->
+    Cil_types.exp list -> t -> t
+end
diff --git a/src/kernel_services/abstract_interp/abstract_offset.ml b/src/plugins/value/domains/multidim/abstract_offset.ml
similarity index 95%
rename from src/kernel_services/abstract_interp/abstract_offset.ml
rename to src/plugins/value/domains/multidim/abstract_offset.ml
index c7a77dc76b6..4fc70e66946 100644
--- a/src/kernel_services/abstract_interp/abstract_offset.ml
+++ b/src/plugins/value/domains/multidim/abstract_offset.ml
@@ -20,34 +20,7 @@
 (*                                                                        *)
 (**************************************************************************)
 
-module Top =
-struct
-  [@@@warning "-32"]
-
-  let zip x y =
-    match x, y with
-    | `Top, _ | _, `Top -> `Top
-    | `Value x, `Value y -> `Value (x,y)
-
-  let (>>-) t f = match t with
-    | `Top  -> `Top
-    | `Value t -> f t
-
-  let (>>-:) t f = match t with
-    | `Top  -> `Top
-    | `Value t -> `Value (f t)
-
-  let (let+) = (>>-:)
-  let (and+) = zip
-  let (let*) = (>>-)
-
-  let of_option = function
-    | None -> `Top
-    | Some x -> `Value x
-end
-
-type 'a or_top = [`Value of 'a | `Top]
-
+open Lattice_extrema
 open Top
 
 type t =
diff --git a/src/kernel_services/abstract_interp/abstract_offset.mli b/src/plugins/value/domains/multidim/abstract_offset.mli
similarity index 98%
rename from src/kernel_services/abstract_interp/abstract_offset.mli
rename to src/plugins/value/domains/multidim/abstract_offset.mli
index 457393b96da..30022025814 100644
--- a/src/kernel_services/abstract_interp/abstract_offset.mli
+++ b/src/plugins/value/domains/multidim/abstract_offset.mli
@@ -20,13 +20,13 @@
 (*                                                                        *)
 (**************************************************************************)
 
+open Lattice_extrema
+
 type t =
   | NoOffset of Cil_types.typ
   | Index of Cil_types.exp option * Int_val.t * Cil_types.typ * t
   | Field of Cil_types.fieldinfo * t
 
-type 'a or_top = [`Value of 'a | `Top]
-
 val pretty : Format.formatter -> t -> unit
 
 val of_var_address : Cil_types.varinfo -> t
diff --git a/src/plugins/value/domains/multidim/abstract_structure.ml b/src/plugins/value/domains/multidim/abstract_structure.ml
new file mode 100644
index 00000000000..2fab9a02a81
--- /dev/null
+++ b/src/plugins/value/domains/multidim/abstract_structure.ml
@@ -0,0 +1,355 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    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 Lattice_extrema
+open Abstract_memory
+open Pretty_memory
+
+let no_oracle = fun _exp -> Int_val.top
+
+(* Composition operator for compare function *)
+
+let (<?>) c lcmp =
+  if c = 0 then 0 else Lazy.force lcmp
+
+
+(* ------------------------------------------------------------------------ *)
+(* --- C struct abstraction                                             --- *)
+(* ------------------------------------------------------------------------ *)
+
+module type Config =
+sig
+  val deps : State.t list
+end
+
+module type Structure =
+sig
+  type t
+  type submemory
+  val pretty : Format.formatter -> t -> unit
+  val hash : t -> int
+  val equal : t -> t -> bool
+  val compare : t -> t -> int
+  val raw : t -> Bit.t
+  val of_raw : Bit.t -> t
+  val weak_erase : Bit.t -> t -> t
+  val is_included : t -> t -> bool
+  val unify : (submemory -> submemory -> submemory) -> t -> t -> t
+  val read : t -> Cil_types.fieldinfo -> submemory
+  val update : (submemory -> submemory or_bottom) -> t -> Cil_types.fieldinfo ->
+    t or_bottom
+  val map : (submemory -> submemory) -> t -> t
+end
+
+module Make (Config : Config) (M : ProtoMemory) =
+struct
+  module Field =
+  struct
+    include Cil_datatype.Fieldinfo
+    let id f = f.Cil_types.forder (* At each node, all fields come from the same comp *)
+  end
+  module Values =
+  struct
+    include Datatype.Make (
+      struct
+        include Datatype.Serializable_undefined
+        include M
+        let name = "Abstract_Memory.Structure.Values"
+        let reprs = [ of_raw Bit.zero ]
+      end)
+    let pretty_debug = pretty
+  end
+  module Initial_Values = struct let v = [[]] end
+  module Deps = struct let l = Config.deps end
+
+  module FieldMap =
+    Hptmap.Make (Field) (Values) (Hptmap.Comp_unused) (Initial_Values) (Deps)
+
+  type t = {
+    padding: Bit.t;
+    fields: FieldMap.t;
+  }
+
+  type submemory = M.t
+
+  let pretty fmt m =
+    pp_iter2 ~format:"@[<hv>.%a%a@]"
+      FieldMap.iter Field.pretty M.pretty fmt m.fields
+
+  let hash m =
+    Hashtbl.hash (m.padding, FieldMap.hash m.fields)
+
+  let equal m1 m2 =
+    FieldMap.equal m1.fields m2.fields &&
+    Bit.equal m1.padding m2.padding
+
+  let compare m1 m2 =
+    FieldMap.compare m1.fields m2.fields <?>
+    lazy (Bit.compare m1.padding m2.padding)
+
+  let raw m =
+    FieldMap.fold (fun _ x acc -> Bit.join acc (M.raw x)) m.fields m.padding
+
+  let of_raw m =
+    { padding = m ; fields = FieldMap.empty }
+
+  let weak_erase b m =
+    {
+      padding = Bit.join b m.padding ;
+      fields = FieldMap.map (M.weak_erase b) m.fields ;
+    }
+
+  let is_included m1 m2 =
+    Bit.is_included m1.padding m2.padding &&
+    let decide_fast s t = if s == t then FieldMap.PTrue else PUnknown in
+    let decide_fst _fi m1 = M.is_included m1 (M.of_raw m2.padding) in
+    let decide_snd _fi m2 = M.is_included (M.of_raw m1.padding) m2 in
+    let decide_both _fi m1 m2 = M.is_included m1 m2 in
+    FieldMap.binary_predicate Hptmap_sig.NoCache UniversalPredicate
+      ~decide_fast ~decide_fst ~decide_snd ~decide_both
+      m1.fields m2.fields
+
+  let unify f m1 m2 = (* f is not symmetric *)
+    let decide_both _fi = fun m1 m2 -> Some (f m1 m2)
+    and decide_left = FieldMap.Traversing (fun _fi m1 ->
+        Some (f m1 (M.of_raw m2.padding)))
+    and decide_right = FieldMap.Traversing (fun _fi m2 ->
+        Some (f (M.of_raw m1.padding) m2))
+    in
+    let fields = FieldMap.merge
+        ~cache:Hptmap_sig.NoCache ~symmetric:false ~idempotent:true
+        ~decide_both ~decide_left ~decide_right
+        m1.fields m2.fields
+    in { padding = Bit.join m1.padding m2.padding ; fields }
+
+  let read m fi =
+    try
+      FieldMap.find fi m.fields
+    with Not_found -> (* field undefined *)
+      M.of_raw m.padding
+
+  let update f m fi =
+    try
+      let update' opt =
+        let x = Option.value ~default:(M.of_raw m.padding) opt in
+        match f x with
+        | `Value v -> Some v
+        | `Bottom -> raise Exit
+      in
+      `Value { m with fields = FieldMap.replace update' fi m.fields }
+    with Exit ->
+      `Bottom
+
+  let map f m =
+    { m with fields = FieldMap.map f m.fields }
+end
+
+
+(* ------------------------------------------------------------------------ *)
+(* --- Disjunctions of structures                                       --- *)
+(* ------------------------------------------------------------------------ *)
+
+(* This abstraction focus on catching structures invariants of the form
+
+   x.f = c₁ ⇒ ϕ₁(x)  ⋁  x.f = c₂ ⇒ ϕ₂(x)  ⋁  ...
+
+   where f is a field, c₁, c₂, ... are constants and ϕ₁, ϕ₂, ... properties
+   about the structure x.
+
+   During the analysis, we track fields f, called keys which are assigned
+   constant values. As the analysis  progresses, the set of keys is reduced each
+   time we discover one of the field is actually not a key because it is
+   assigned with non-singleton values. *)
+
+module type Disjunction =
+sig
+  type t
+  type submemory
+  type structure
+  val pretty : Format.formatter -> t -> unit
+  val hash : t -> int
+  val equal : t -> t -> bool
+  val compare : t -> t -> int
+  val raw : t -> Bit.t
+  val of_raw : Cil_types.compinfo -> Bit.t -> t
+  val of_struct : Cil_types.compinfo -> structure -> t
+  val to_struct : oracle:oracle -> t -> structure
+  val weak_erase : Cil_types.compinfo -> Bit.t -> t -> t
+  val is_included : t -> t -> bool
+  val unify : oracle:bioracle -> (submemory -> submemory -> submemory) ->
+    t -> t -> t
+  val read : (submemory -> 'a) -> ('a -> 'a -> 'a)  ->
+    t -> Cil_types.fieldinfo -> 'a
+  val update : oracle:oracle -> (submemory -> submemory or_bottom) ->
+    t -> Cil_types.fieldinfo -> t or_bottom
+  val map : (submemory -> submemory) -> t -> t
+end
+
+module Disjunction (Config : Config) (M : ProtoMemory)
+    (S : Structure with type submemory = M.t) =
+struct
+  module Valuation =
+  struct
+    module FMap = Cil_datatype.Fieldinfo.Map
+    module FSet = Cil_datatype.Fieldinfo.Set
+
+    include FMap
+    include FMap.Make (Datatype.Integer)
+    let keys map = fold (fun k _ set -> FSet.add k set) map FSet.empty
+  end
+
+  module Map = (* 'a Map.t : (fi -> int) -> 'a *)
+  struct
+    module Info = struct let module_name = "Abstract_memory.Disjunction.Map" end
+    include Datatype.Map (Map.Make (Valuation)) (Valuation) (Info)
+
+    (* Defined only for Ocaml >= 4.11 *)
+    let filter_map f m =
+      fold (fun k x m -> match f k x with None -> m | Some y -> add k y m) m empty
+  end
+
+  module S = (* Structures in the disjunction *)
+  struct
+    include Datatype.Serializable_undefined
+    include S
+
+    let name = "Abstract_Memory.Disjunction.S"
+    let reprs = [ of_raw Bit.zero ]
+    let eval_key key m = M.to_singleton_int (read m key)
+    let suitable_key key m = Option.is_some (eval_key key m)
+    let keys_candidates ci m =
+      let fields = Option.value ~default:[] ci.Cil_types.cfields in
+      List.filter (fun fi -> suitable_key fi m) fields |>
+      Valuation.FSet.of_list
+    let evaluate keys m : Valuation.t =
+      let update_key fi map =
+        eval_key fi m |> (* should be Some *)
+        Option.fold ~none:map ~some:(fun v -> Valuation.add fi v map)
+      in
+      Valuation.FSet.fold update_key keys Valuation.empty
+  end
+
+  include Map.Make (Datatype.Make (S))
+
+  type submemory = M.t
+  type structure = S.t
+
+  let keys (m : t) = Valuation.keys (fst (Map.choose m))
+
+  let pick (m : t) =
+    match Map.choose_opt m with
+    | None -> Kernel.fatal "The disjunction should never be empty"
+    | Some (k,s) -> k,s,Map.remove k m
+
+  let pretty fmt (m : t) =
+    match Map.bindings m with
+    | [] -> assert false
+    | [(_,s)] -> S.pretty fmt s
+    | bindings ->
+      let l = List.map snd bindings in
+      pp_iter ~format:"@[<hv>%a@]" ~sep:" or @;<1 2>" List.iter S.pretty fmt l
+
+  let hash (m : t) =
+    Hashtbl.hash (Map.fold (fun _ s acc -> s :: acc) m [])
+
+  let reevaluate ~oracle keys (m : t) : t =
+    let update _k s acc =
+      let merge prev =
+        Extlib.merge_opt (fun () -> S.unify (M.smash ~oracle)) () (Some s) prev
+      in
+      Map.update (S.evaluate keys s) merge acc
+    in
+    Map.fold update m Map.empty
+
+  let smash ?(oracle=no_oracle) (m : t) =
+    let _k,s,m = pick m in
+    Map.fold (fun _k -> S.unify (M.smash ~oracle)) m s
+
+  let of_struct (ci : Cil_types.compinfo) (s : S.t) =
+    let keys = S.keys_candidates ci s in
+    Map.singleton (S.evaluate keys s) s
+
+  let to_struct ~oracle (m : t) =
+    smash ~oracle m
+
+  let raw (m : t) : bit =
+    let f _ x acc =
+      Bottom.join Bit.join acc (`Value (S.raw x))
+    in
+    Bottom.non_bottom (Map.fold f m `Bottom) (* The map should not be empty *)
+
+  let of_raw (ci : Cil_types.compinfo) (b : bit) : t =
+    let s = S.of_raw b in
+    of_struct ci s
+
+  let weak_erase (ci : Cil_types.compinfo) (b : bit) (m : t) : t =
+    (* if b is Zero, more could be done as all scalar fields might be a key *)
+    (* weak_erase is probably linear in the number of join operators, so we
+       weak_erase befoire joining as the join on erased memory is faster. *)
+    let m = Map.map (fun m -> S.weak_erase b m) m in
+    let s = smash m in
+    of_struct ci s
+
+  let is_included (m1 : t) (m2 : t) : bool  =
+    (* Imprecise inclusion: we only consider inclusion of disjunctions where
+       the set of field keys is the same *)
+    let aux k s1 =
+      match Map.find_opt k m2 with
+      | None -> false
+      | Some s2 -> S.is_included s1 s2
+    in
+    Map.for_all aux m1
+
+  let unify ~oracle f (m1 : t) (m2 : t) =
+    let keys = Valuation.FSet.inter (keys m1) (keys m2) in
+    let m1 = reevaluate ~oracle:(oracle Left) keys m1
+    and m2 = reevaluate ~oracle:(oracle Right) keys m2 in
+    Map.union (fun _k m1 m2 -> Some (S.unify f m1 m2)) m1 m2
+
+  let read map reduce (m : t) (fi : Cil_types.fieldinfo) =
+    let aux _k s acc =
+      map (S.read s fi) |>
+      Option.fold ~none:Fun.id ~some:reduce acc |>
+      Option.some
+    in
+    Option.get (Map.fold aux m None) (* If the map is not empty, result is Some *)
+
+  let update ~oracle f (m : t) (fi : Cil_types.fieldinfo) =
+    let m = Map.filter_map (fun _k s -> Bottom.to_option (S.update f s fi)) m in
+    if Map.is_empty m then
+      `Bottom
+    else
+      let is_key = Map.for_all (fun _k s -> S.suitable_key fi s) m in
+      let old_keys = keys m in
+      if is_key || Valuation.FSet.mem fi old_keys then
+        let new_keys =
+          if is_key
+          then Valuation.FSet.add fi old_keys
+          else Valuation.FSet.remove fi old_keys
+        in
+        `Value (reevaluate ~oracle new_keys m)
+      else
+        `Value (m)
+
+  let map f m =
+    Map.map (S.map f) m
+end
diff --git a/src/plugins/value/domains/multidim/abstract_structure.mli b/src/plugins/value/domains/multidim/abstract_structure.mli
new file mode 100644
index 00000000000..71a81ee103f
--- /dev/null
+++ b/src/plugins/value/domains/multidim/abstract_structure.mli
@@ -0,0 +1,81 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    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 Lattice_extrema
+open Abstract_memory
+
+module type Config =
+sig
+  (* Dependencies of the hash-consing table. The table will be cleared
+     whenever one of those dependencies is cleared. *)
+  val deps : State.t list
+end
+
+module type Structure =
+sig
+  type t
+  type submemory
+  val pretty : Format.formatter -> t -> unit
+  val hash : t -> int
+  val equal : t -> t -> bool
+  val compare : t -> t -> int
+  val raw : t -> Bit.t
+  val of_raw : Bit.t -> t
+  val weak_erase : Bit.t -> t -> t
+  val is_included : t -> t -> bool
+  val unify : (submemory -> submemory -> submemory) -> t -> t -> t
+  val read : t -> Cil_types.fieldinfo -> submemory
+  val update : (submemory -> submemory or_bottom) -> t -> Cil_types.fieldinfo ->
+    t or_bottom
+  val map : (submemory -> submemory) -> t -> t
+end
+
+module Make (Config : Config) (M : ProtoMemory) :
+  Structure with type submemory = M.t
+
+module type Disjunction =
+sig
+  type t
+  type submemory
+  type structure
+  val pretty : Format.formatter -> t -> unit
+  val hash : t -> int
+  val equal : t -> t -> bool
+  val compare : t -> t -> int
+  val raw : t -> Bit.t
+  val of_raw : Cil_types.compinfo -> Bit.t -> t
+  val of_struct : Cil_types.compinfo -> structure -> t
+  val to_struct : oracle:oracle -> t -> structure
+  val weak_erase : Cil_types.compinfo -> Bit.t -> t -> t
+  val is_included : t -> t -> bool
+  val unify : oracle:bioracle -> (submemory -> submemory -> submemory) ->
+    t -> t -> t
+  val read : (submemory -> 'a) -> ('a -> 'a -> 'a)  ->
+    t -> Cil_types.fieldinfo -> 'a
+  val update : oracle:oracle -> (submemory -> submemory or_bottom) ->
+    t -> Cil_types.fieldinfo -> t or_bottom
+  val map : (submemory -> submemory) -> t -> t
+end
+
+module Disjunction (Config : Config) (M : ProtoMemory)
+    (S : Structure with type submemory = M.t) :
+  Disjunction with type submemory = M.t and type structure = S.t
diff --git a/src/plugins/value/domains/multidim/lattice_extrema.ml b/src/plugins/value/domains/multidim/lattice_extrema.ml
new file mode 100644
index 00000000000..523a679f08b
--- /dev/null
+++ b/src/plugins/value/domains/multidim/lattice_extrema.ml
@@ -0,0 +1,81 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+type 'a or_bottom = [`Bottom | `Value of 'a]
+type 'a or_top = [`Top | `Value of 'a]
+type 'a or_top_bottom = [`Top | `Bottom | `Value of 'a]
+
+module Bot =
+struct
+  include Bottom.Type
+  let zip x y =
+    match x, y with
+    | `Bottom, _ | _, `Bottom -> `Bottom
+    | `Value x, `Value y -> `Value (x,y)
+
+  (* Applicative syntax *)
+  let ( let+ ) = (>>-:)
+  let ( and+ ) = zip
+  (* Monad syntax *)
+  let ( let* ) = (>>-)
+  let ( and* ) = zip
+end
+
+module Top =
+struct
+  let zip x y =
+    match x, y with
+    | `Top, _ | _, `Top -> `Top
+    | `Value x, `Value y -> `Value (x,y)
+
+  let (>>-) t f = match t with
+    | `Top  -> `Top
+    | `Value t -> f t
+
+  let (>>-:) t f = match t with
+    | `Top  -> `Top
+    | `Value t -> `Value (f t)
+
+  let (let+) = (>>-:)
+  let (and+) = zip
+  let (let*) = (>>-)
+  let (and*) = zip
+  let of_option = function
+    | None -> `Top
+    | Some v -> `Value v
+end
+
+module TopBot =
+struct
+  let (>>-) t f = match t with
+    | `Top  -> `Top
+    | `Bottom -> `Bottom
+    | `Value t -> f t
+
+  let (>>-:) t f = match t with
+    | `Top  -> `Top
+    | `Bottom -> `Bottom
+    | `Value t -> `Value (f t)
+
+  let (let+) = (>>-:)
+  let (let*) = (>>-)
+end
diff --git a/src/kernel_services/abstract_interp/multidim.ml b/src/plugins/value/domains/multidim/multidim.ml
similarity index 100%
rename from src/kernel_services/abstract_interp/multidim.ml
rename to src/plugins/value/domains/multidim/multidim.ml
diff --git a/src/kernel_services/abstract_interp/multidim.mli b/src/plugins/value/domains/multidim/multidim.mli
similarity index 100%
rename from src/kernel_services/abstract_interp/multidim.mli
rename to src/plugins/value/domains/multidim/multidim.mli
diff --git a/src/plugins/value/domains/multidim_domain.ml b/src/plugins/value/domains/multidim/multidim_domain.ml
similarity index 99%
rename from src/plugins/value/domains/multidim_domain.ml
rename to src/plugins/value/domains/multidim/multidim_domain.ml
index f5e11817f41..d19806d7728 100644
--- a/src/plugins/value/domains/multidim_domain.ml
+++ b/src/plugins/value/domains/multidim/multidim_domain.ml
@@ -280,7 +280,7 @@ struct
     let disjunctive_invariants =
       Parameters.MultidimDisjunctiveInvariants.get
   end
-  module Memory = Abstract_memory.TypedMemory (Config) (Value_or_Uninitialized)
+  module Memory = Typed_memory.Make (Config) (Value_or_Uninitialized)
 
   module Prototype =
   (* Datatype *)
diff --git a/src/plugins/value/domains/multidim_domain.mli b/src/plugins/value/domains/multidim/multidim_domain.mli
similarity index 100%
rename from src/plugins/value/domains/multidim_domain.mli
rename to src/plugins/value/domains/multidim/multidim_domain.mli
diff --git a/src/plugins/value/domains/multidim/pretty_memory.ml b/src/plugins/value/domains/multidim/pretty_memory.ml
new file mode 100644
index 00000000000..3bfed3b65d6
--- /dev/null
+++ b/src/plugins/value/domains/multidim/pretty_memory.ml
@@ -0,0 +1,46 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+type 'a sformat = ('a,Format.formatter,unit) format
+type 'a formatter = Format.formatter -> 'a -> unit
+
+(* inspired by Pretty_utils.pp_iter *)
+
+let pp_iter
+    ?(pre=format_of_string "{@;<1 2>")
+    ?(sep=format_of_string ",@;<1 2>")
+    ?(suf=format_of_string "@ }")
+    ?(format=format_of_string "@[<hv>%a@]")
+    iter pp fmt v =
+  let need_sep = ref false in
+  Format.fprintf fmt pre;
+  iter (fun v ->
+      if !need_sep then Format.fprintf fmt sep else need_sep := true;
+      Format.fprintf fmt format pp v;
+    ) v;
+  Format.fprintf fmt suf
+
+let pp_iter2 ?pre ?sep ?suf ?(format=format_of_string "@[<hv>%a%a@]")
+    iter2 pp_key pp_val fmt v =
+  let iter f = iter2 (fun k v -> f (k,v)) in
+  let pp fmt (k,v) = Format.fprintf fmt format pp_key k pp_val v in
+  pp_iter ?pre ?sep ?suf ~format:"%a" iter pp fmt v
diff --git a/src/plugins/value/domains/multidim/pretty_memory.mli b/src/plugins/value/domains/multidim/pretty_memory.mli
new file mode 100644
index 00000000000..26ecb1b94f1
--- /dev/null
+++ b/src/plugins/value/domains/multidim/pretty_memory.mli
@@ -0,0 +1,42 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+type 'a sformat = ('a,Format.formatter,unit) format
+type 'a formatter = Format.formatter -> 'a -> unit
+
+(* Pretty printing for iterators *)
+
+val pp_iter :
+  ?pre:unit sformat ->
+  ?sep:unit sformat ->
+  ?suf:unit sformat ->
+  ?format:('a formatter -> 'a -> unit) sformat ->
+  (('a -> unit) -> 'b -> unit) ->
+  'a formatter -> 'b formatter
+
+val pp_iter2 :
+  ?pre:(unit sformat) ->
+  ?sep:(unit sformat) ->
+  ?suf:(unit sformat) ->
+  ?format:('a formatter -> 'a -> 'b formatter -> 'b -> unit) sformat ->
+  (('a -> 'b -> unit) -> 'c -> unit) ->
+  'a formatter -> 'b formatter -> 'c formatter
diff --git a/src/plugins/value/domains/multidim/segmentation.ml b/src/plugins/value/domains/multidim/segmentation.ml
new file mode 100644
index 00000000000..21251f31aec
--- /dev/null
+++ b/src/plugins/value/domains/multidim/segmentation.ml
@@ -0,0 +1,827 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    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 Lattice_extrema
+open Abstract_memory
+open Pretty_memory
+
+exception Not_implemented
+
+let no_oracle = fun _exp -> Int_val.top
+
+(* Composition operator for compare function *)
+
+let (<?>) c lcmp =
+  if c = 0 then 0 else Lazy.force lcmp
+
+
+(* ------------------------------------------------------------------------ *)
+(* --- Comparison operators                                             --- *)
+(* ------------------------------------------------------------------------ *)
+
+type comparison =
+  | Equal
+  | Lower
+  | Greater
+  | LowerOrEqual
+  | GreaterOrEqual
+  | Uncomparable
+
+module type Operators =
+sig
+  [@@@warning "-32"] (* unused operators... for now*)
+
+  type t
+  val (<) : t -> t -> bool
+  val (>) : t -> t -> bool
+  val (<=) : t -> t -> bool
+  val (>=) : t -> t -> bool
+  val (===) : t -> t -> bool
+end
+
+let operators :
+  type a . (a -> a -> comparison) -> (module Operators with type t = a) =
+  fun f ->
+  (module struct
+    type t = a
+
+    let (<) b1 b2 = match f b1 b2 with
+      | Lower -> true
+      | LowerOrEqual | Equal | Greater | GreaterOrEqual | Uncomparable -> false
+
+    let (<=) b1 b2 = match f b1 b2 with
+      | Lower | Equal | LowerOrEqual -> true
+      | Greater | GreaterOrEqual | Uncomparable -> false
+
+    let (>) b1 b2 = match f b1 b2 with
+      | Greater -> true
+      | Equal | LowerOrEqual | Lower | GreaterOrEqual | Uncomparable -> false
+
+    let (>=) b1 b2 = match f b1 b2 with
+      | Greater | GreaterOrEqual | Equal -> true
+      | Lower | LowerOrEqual | Uncomparable -> false
+
+    let (===) b1 b2 = match f b1 b2 with
+      | Equal -> true
+      | GreaterOrEqual | Greater | Lower | LowerOrEqual | Uncomparable -> false
+  end)
+
+
+(* ------------------------------------------------------------------------ *)
+(* --- Bounds of the segmentation                                       --- *)
+(* ------------------------------------------------------------------------ *)
+
+module Bound =
+struct
+  module Var = Cil_datatype.Varinfo
+  module Exp =
+  struct
+    include Cil_datatype.ExpStructEq
+    let equal e1 e2 =
+      if e1 == e2 then true else equal e1 e2
+  end
+
+  type t =
+    | Const of Integer.t
+    | Exp of Cil_types.exp * Integer.t (* x + c *)
+    | Ptroffset of Cil_types.exp * Cil_types.offset * Integer.t (* (x - &b.offset) + c *)
+
+  let pretty fmt : t -> unit = function
+    | Const i -> Integer.pretty fmt i
+    | Exp (e,i) when Integer.is_zero i -> Exp.pretty fmt e
+    | Exp (e,i) when Integer.(lt i zero) ->
+      Format.fprintf fmt "%a - %a" Exp.pretty e Integer.pretty (Integer.neg i)
+    | Exp (e,i) ->
+      Format.fprintf fmt "%a + %a" Exp.pretty e Integer.pretty i
+    | _ -> raise Not_implemented
+
+  let hash : t -> int = function
+    | Const i -> Hashtbl.hash (1, Integer.hash i)
+    | Exp (e, i) -> Hashtbl.hash (2, Exp.hash e, Integer.hash i)
+    | Ptroffset _ -> raise Not_implemented
+
+  let compare (b1 : t) (b2 : t) : int =
+    match b1, b2 with
+    | Const i1, Const i2 -> Integer.compare i1 i2
+    | Exp (e1, i1), Exp (e2, i2) ->
+      Exp.compare e1 e2 <?> lazy (Integer.compare i1 i2)
+    | Ptroffset _, Ptroffset _ -> raise Not_implemented
+    | Const _, _ -> 1
+    | _, Const _-> -1
+    | Exp _, _ -> 1
+    | _, Exp _ -> -1
+
+  let equal (b1 : t) (b2 : t) : bool =
+    match b1, b2 with
+    | Const i1, Const i2 -> Integer.equal i1 i2
+    | Exp (e1, i1), Exp (e2, i2) ->
+      Exp.equal e1 e2 && Integer.equal i1 i2
+    | Ptroffset _, Ptroffset _ -> raise Not_implemented
+    | _, _ -> false
+
+  let of_integer (i : Integer.t) : t =
+    Const i
+
+  exception UnsupportedBoundExpression
+  exception NonLinear
+
+  (* Find a coefficient before vi in exp *)
+  let rec linearity vi exp =
+    match exp.Cil_types.enode with
+    | Const _
+    | SizeOf _ | SizeOfE _ | SizeOfStr _ | AlignOf _ | AlignOfE _
+    | AddrOf _ | StartOf _ -> Integer.zero
+    | Lval (Var vi', NoOffset) ->
+      if Var.equal  vi' vi
+      then Integer.one
+      else Integer.zero
+    | Lval _ -> raise UnsupportedBoundExpression
+    | UnOp (Neg, e, _typ) ->
+      Integer.neg (linearity vi e)
+    | UnOp (_, e, _typ) | CastE (_typ, e) ->
+      if Integer.is_zero (linearity vi e)
+      then Integer.zero
+      else raise NonLinear
+    | BinOp (op, e1, e2, _typ) ->
+      let l1 = linearity vi e1 and l2 = linearity vi e2 in
+      match op with
+      | PlusA|PlusPI -> Integer.add l1 l2
+      | MinusA|MinusPI -> Integer.sub l1 l2
+      | _ ->
+        if Integer.(is_zero l1 && is_zero l2)
+        then Integer.zero
+        else raise NonLinear
+
+  let check_support exp =
+    (* Check that the linearity of any variable is not hidden into a mem access *)
+    ignore (linearity Var.dummy exp)
+
+  let of_exp exp =
+    check_support exp;
+    (* Normalizes x + c, c + x and x - c *)
+    match Cil.constFoldToInt exp with
+    | Some i -> Const i
+    | None ->
+      match exp.Cil_types.enode with
+      | BinOp ((PlusA|PlusPI), e1, e2, _typ) ->
+        begin match Cil.constFoldToInt e1, Cil.constFoldToInt e2 with
+          | None, Some i -> Exp (e1, i)
+          | Some i, None -> Exp (e2, i)
+          | _ -> Exp (exp, Integer.zero)
+        end
+      | BinOp ((MinusA|MinusPI), e1, e2, _typ) ->
+        begin match Cil.constFoldToInt e2 with
+          | Some i -> Exp (e1, Integer.neg i)
+          | None -> Exp (exp, Integer.zero)
+        end
+      | _ -> Exp (exp, Integer.zero)
+
+  let _of_ptr ~base_offset e =
+    (* TODO: verify type compatibility between e and base_offset *)
+    match of_exp e with
+    | Exp (e, c) -> Ptroffset (e, base_offset, c)
+    | Const _ -> assert false (* should not happen ? even with absolute adresses ? *)
+    | Ptroffset _ -> assert false (* Not produced by of_exp *)
+
+  (* Convert bound to interval using oracle *)
+  let to_int_val ~oracle = function
+    | Const i -> Int_val.inject_singleton i
+    | Exp (e, i) -> Int_val.add_singleton i (oracle e)
+    | Ptroffset _ -> raise Not_implemented
+
+  let to_integer ~oracle = function
+    | Const i -> Some i
+    | Exp (e, i) ->
+      begin try
+          Some (Integer.add (Int_val.project_int (oracle e)) i)
+        with Ival.Not_Singleton_Int ->
+          None
+      end
+    | Ptroffset _ -> raise Not_implemented
+
+  let succ = function
+    | Const i -> Const (Integer.succ i)
+    | Exp (e, i) -> Exp (e, Integer.succ i)
+    | Ptroffset _ -> raise Not_implemented
+
+  let pred = function
+    | Const i -> Const (Integer.pred i)
+    | Exp (e, i) -> Exp (e, Integer.pred i)
+    | Ptroffset _ -> raise Not_implemented
+
+  let incr vi i b =
+    try
+      match b with
+      | Const _ -> Some b
+      | Exp (e, j) ->
+        let l = linearity vi e in
+        if Integer.is_zero l
+        then Some b
+        else i |> Option.map (fun i -> Exp (e, Integer.(sub j (mul l i))))
+      | Ptroffset (e, base, j) ->
+        let l = linearity vi e in
+        if Integer.is_zero l
+        then Some b
+        else
+          i |> Option.map (fun i ->
+              Ptroffset (e, base, Integer.(sub j (mul l i))))
+    with NonLinear -> None
+
+  let incr_or_constantify ~oracle vi i b =
+    match incr vi i b with
+    | Some v -> Some v
+    | None -> Option.map (fun c -> Const c) (to_integer ~oracle b)
+
+  let cmp_int i1 i2 =
+    let r = Integer.sub i1 i2 in
+    if Integer.is_zero r
+    then Equal
+    else if Integer.(lt r zero) then Lower else Greater
+
+  let cmp ~oracle b1 b2 =
+    if b1 == b2
+    then Equal
+    else
+      match b1, b2 with
+      | Const i1, Const i2 -> cmp_int i1 i2
+      | Exp (e1, i1), Exp (e2, i2) when Exp.equal e1 e2 -> cmp_int i1 i2
+      | Ptroffset _, _ | _, Ptroffset _ -> raise Not_implemented
+      | _, _ ->
+        let i1 = to_int_val ~oracle b1 and i2 = to_int_val ~oracle b2 in
+        let r = Int_val.(add i1 (neg i2)) in
+        match Int_val.min_and_max r with
+        | Some min, Some max when Integer.is_zero min && Integer.is_zero max ->
+          Equal
+        | Some l, _ when Integer.(ge l zero) ->
+          if Integer.(gt l zero) then Greater else GreaterOrEqual
+        | _, Some u when Integer.(le u zero) ->
+          if Integer.(lt u zero) then Lower else LowerOrEqual
+        | _ -> Uncomparable
+
+  let eq ?(oracle=no_oracle) b1 b2 =
+    cmp ~oracle b1 b2 = Equal
+
+  let lower_integer ~oracle b =
+    match Int_val.min_int (to_int_val ~oracle b) with
+    | Some l -> `Value l
+    | None ->
+      Kernel.warning ~current:true "cannot retrieve a lower bound for %a"
+        pretty b;
+      `Top
+
+  let upper_integer ~oracle b =
+    match Int_val.max_int (to_int_val ~oracle b) with
+    | Some u -> `Value u
+    | None ->
+      Kernel.warning ~current:true "cannot retrieve an upper bound for %a"
+        pretty b;
+      `Top
+
+  let lower_bound ~oracle b1 b2 =
+    if b1 == b2 || eq b1 b2 then `Value b1 else
+      let open Top in
+      let+ i1,i2 = Top.zip
+          (lower_integer ~oracle:(oracle Left) b1)
+          (lower_integer ~oracle:(oracle Right) b2) in
+      Const (Integer.min i1 i2)
+
+  let upper_bound ~oracle b1 b2 =
+    if b1 == b2 || eq b1 b2 then `Value b1 else
+      let open Top in
+      let+ i1,i2 = Top.zip
+          (upper_integer ~oracle:(oracle Left) b1)
+          (upper_integer ~oracle:(oracle Right) b2) in
+      Const (Integer.max i1 i2)
+
+  let lower_const ~oracle b =
+    let open Top in
+    lower_integer ~oracle b >>-: of_integer
+
+  let upper_const ~oracle b =
+    let open Top in
+    upper_integer ~oracle b >>-: of_integer
+end
+
+module AgingBound =
+struct
+  type age = Integer.t
+  type t = Bound.t * age
+
+  let pretty fmt (b,_) = Bound.pretty fmt b
+  let hash (b,_a) = Bound.hash b
+  let compare (b1,_a1) (b2,_a2) =
+    Bound.compare b1 b2
+  let equal_regardless_age (b1,_a1) (b2,_a2) =
+    Bound.equal b1 b2
+  let equal = equal_regardless_age
+  let pred (b,a) = (Bound.pred b, a)
+  let incr_or_constantify ~oracle vi i (b,a) =
+    Bound.incr_or_constantify ~oracle vi i b |> Option.map (fun b -> b,a)
+  let cmp ~oracle (b1,_a1) (b2,_a2) = Bound.cmp ~oracle b1 b2
+  let eq ?oracle (b1,_a1) (b2,_a2) = Bound.eq ?oracle b1 b2
+  let lower_bound ~oracle (b1,a1) (b2,a2) =
+    let open Top in
+    Bound.lower_bound ~oracle b1 b2 >>-: fun b -> b, Integer.min a1 a2
+  let upper_bound ~oracle (b1,a1) (b2,a2) =
+    let open Top in
+    Bound.upper_bound ~oracle b1 b2 >>-: fun b -> b, Integer.min a1 a2
+  let lower_const ~oracle (b,_a) = Bound.lower_const ~oracle b
+  let upper_const ~oracle (b,_a) = Bound.upper_const ~oracle b
+  let birth b = b, Integer.zero
+  let aging (b,a) = b, Integer.succ a
+  let age (_,a) = a
+  let unify_age ~other:(_,a') (b,a) = (b, Integer.min a' a)
+  let operators oracle : (module Operators with type t = t) =
+    operators (cmp ~oracle)
+end
+
+
+(* ------------------------------------------------------------------------ *)
+(* --- Segmentation                                                     --- *)
+(* ------------------------------------------------------------------------ *)
+
+module type Config =
+sig
+  val slice_limit : unit -> int
+end
+
+module type Segmentation =
+sig
+  type bound = Bound.t
+  type submemory
+  type t
+  val pretty : Format.formatter -> t -> unit
+  val hash : t -> int
+  val equal : t -> t -> bool
+  val compare : t -> t -> int
+  val hull : oracle:oracle -> t -> (bound * bound) or_top
+  val raw : t -> Bit.t
+  val weak_erase : Bit.t -> t -> t
+  val is_included : t -> t -> bool
+  val unify : oracle:bioracle -> (submemory -> submemory -> submemory) ->
+    t -> t -> t or_top
+  val single : bit -> bound -> bound -> submemory -> t
+  val read : oracle:oracle ->
+    (submemory -> 'a) -> ('a -> 'a -> 'a) -> t -> bound -> bound -> 'a
+  val update : oracle:oracle -> (submemory -> submemory or_bottom) ->
+    t -> bound -> bound -> t or_top_bottom
+  val incr_bound :
+    oracle:oracle -> Cil_types.varinfo -> Integer.t option -> t -> t or_top
+  val map : (submemory -> submemory) -> t -> t
+  val fold : (submemory -> 'a -> 'a) -> (bit -> 'b -> 'a) -> t -> 'b -> 'a
+  val add_segmentation_bounds : oracle:oracle -> bound list -> t -> t
+end
+
+
+module Make (Config : Config) (M : ProtoMemory) =
+struct
+  module B = AgingBound
+
+  type bound = Bound.t
+  type submemory = M.t
+
+  type t = {
+    start: B.t;
+    segments: (M.t * B.t) list; (* should not be empty *)
+    padding: bit; (* padding at the left and right of the segmentation *)
+  }
+
+  type segments = (M.t * B.t) list
+
+  let _pretty_debug fmt (l,s) : unit =
+    Format.fprintf fmt " {%a} " B.pretty l;
+    let pp fmt (v,u) =
+      Format.fprintf fmt "%a {%a} " M.pretty v B.pretty u
+    in
+    List.iter (pp fmt) s
+
+  let pretty_segments fmt ((l : B.t), (s : (M.t * B.t) list)) : unit =
+    let pp fmt (l,v,u) =
+      let u = B.pred u in (* Upper bound is not included *)
+      (* Less strict than B.equal even with no oracles *)
+      if B.eq l u then
+        Format.fprintf fmt "[%a]%a" B.pretty l M.pretty v
+      else
+        Format.fprintf fmt "[%a .. %a]%a" B.pretty l B.pretty u M.pretty v
+    in
+    match s with
+    | [] -> Format.fprintf fmt "[]" (* should not happen *)
+    | [(v,u)] -> pp fmt (l,v,u)
+    | _ :: _ ->
+      let iter l f segments =
+        (* fold the previous upper bound = the current lower bound *)
+        ignore (List.fold_left (fun l (v,u) -> f (l,v,u) ; u) l segments)
+      in
+      pp_iter (iter l) pp fmt s
+
+  let pretty fmt (m : t) : unit =
+    pretty_segments fmt (m.start,m.segments)
+
+  let hash (m : t) : int =
+    Hashtbl.hash (
+      B.hash m.start,
+      List.map (fun (v,u) -> Hashtbl.hash (M.hash v, B.hash u)) m.segments,
+      Bit.hash m.padding)
+
+  let compare (m1 : t) (m2 : t) : int =
+    let compare_segments (v1,u1) (v2,u2) =
+      M.compare v1 v2 <?> lazy (B.compare u1 u2)
+    in
+    B.compare m1.start m2.start <?>
+    lazy (Transitioning.List.compare compare_segments m1.segments m2.segments) <?>
+    lazy (Bit.compare m1.padding m2.padding)
+
+  let equal (m1 : t) (m2 : t) : bool =
+    let equal_segments (v1,u1) (v2,u2) =
+      M.equal v1 v2 && B.equal u1 u2
+    in
+    B.equal m1.start m2.start &&
+    Transitioning.List.equal equal_segments m1.segments m2.segments &&
+    Bit.equal m1.padding m2.padding
+
+  let raw (m : t) : bit =
+    (* Perhaps some segments are empty, but we are not going to test it for now *)
+    List.fold_left
+      (fun acc (v,_u) -> Bit.join acc (M.raw v))
+      m.padding m.segments
+
+  let weak_erase (b : bit) (m : t) : t =
+    {
+      m with
+      segments = List.map (fun (v,u) -> M.weak_erase b v, u) m.segments ;
+      padding = Bit.join b m.padding ;
+    }
+
+  let is_included (m1 : t) (m2 : t) : bool =
+    let included_segments (v1,u1) (v2,u2) =
+      M.is_included v1 v2 && B.eq u1 u2
+    in
+    B.eq m1.start m2.start &&
+    Bit.is_included m1.padding m2.padding &&
+    try
+      List.for_all2 included_segments m1.segments m2.segments
+    with Invalid_argument _ -> false (* Segmentations have different sizes *)
+
+  let rec last_bound = function
+    | [] -> assert false
+    | [(_v,u)] -> u
+    | _ :: t -> last_bound t
+
+  let hull ~oracle (m : t) : (bound * bound) or_top =
+    let l = m.start and u = last_bound m.segments in
+    Top.zip (B.lower_const ~oracle l) (B.upper_const ~oracle u)
+
+  let is_empty_segment ~oracle l u =
+    let open (val (B.operators oracle)) in
+    l >= u
+
+  let check m = (* temporarily checks type invariant; TODO: remove *)
+    match m.segments with
+    | [] -> assert false
+    | _ :: _  -> m
+
+  (* Merge the two first slices of a segmentation *)
+  exception NothingToMerge
+  let smash_head ~oracle l = function
+    | [] | [_] -> raise NothingToMerge
+    | (v1,m) :: (v2,u) :: tail ->
+      let v1' = if is_empty_segment ~oracle l m then `Bottom else `Value v1
+      and v2' = if is_empty_segment ~oracle m u then `Bottom else `Value v2
+      in
+      match Bottom.join (M.smash ~oracle) v1' v2' with
+      | `Bottom -> tail
+      | `Value v -> (v,u) :: tail
+
+  let rec smash_all ~oracle l = function
+    | [] -> `Bottom, l
+    | [(v,u)] -> `Value v, u
+    | t -> smash_all ~oracle l (smash_head ~oracle l t)
+
+  (* Unify two arrays m1 and m2 *)
+  let unify ~oracle f (m1 (*Left*): t) (m2 (*Right*) : t) : t or_top =
+    let open Top in
+    (* Shortcuts *)
+    let left = oracle Left and right = oracle Right in
+    let equals side b1 b2 = B.cmp ~oracle:(oracle side) b1 b2 = Equal in
+    let smash side = Bottom.join (M.smash ~oracle:(oracle side)) in
+    let {start=l1 ; segments=s1 ; padding=p1 } = m1
+    and {start=l2 ; segments=s2 ; padding=p2 } = m2 in
+    (* Unify the segmentation start *)
+    let* start = B.lower_bound ~oracle l1 l2 in
+    let s1 = if equals Left start l1 then s1 else (M.of_raw p1, l1) :: s1
+    and s2 = if equals Right start l2 then s2 else (M.of_raw p2, l2) :: s2 in
+    (* Unify the segmentation end *)
+    let merge_first side = smash_head ~oracle:(oracle side) in
+    let unify_end l s1 s2 acc =
+      let v1, u1 = smash_all ~oracle:left l s1
+      and v2, u2 = smash_all ~oracle:right l s2 in
+      let+ u = B.upper_bound ~oracle u1 u2 in
+      let w1 =
+        if equals Left u u1 then v1 else smash Left (`Value (M.of_raw p1)) v1
+      and w2 =
+        if equals Right u u2 then v2 else smash Right (`Value (M.of_raw p2)) v2
+      in
+      match Bottom.join f w1 w2 with
+      | `Bottom -> acc (* should not happen, but acc is still correct *)
+      | `Value w -> ((w,u) :: acc)
+    in
+    (* +----+-------+-----
+       | v1 | v1'   |
+       +----+-------+-----
+       l    u1
+       +------+-------+---
+       | v2   | v2'   |
+       +------+-------+---
+       l      u2 *)
+    let rec aux l s1 s2 acc =
+      (* Look for emerging slices *)
+      let left_slice_emerges = match s1 with
+        | (v1,u1) :: t1 when equals Right l u1 -> Some (v1,u1,t1)
+        | _ -> None
+      and right_slice_emerges = match s2 with
+        | (v2,u2) :: t2 when equals Left l u2 -> Some (v2,u2,t2)
+        | _ -> None
+      in
+      match left_slice_emerges, right_slice_emerges with
+      | Some (v1,u1,t1), None -> (* left slice emerges *)
+        if equals Left l u1 then (* actually empty both sides*)
+          aux l t1 s2 acc
+        else
+          aux u1 t1 s2 ((v1,B.unify_age ~other:l u1) :: acc)
+      | None, Some (v2,u2,t2) -> (* right slice emerges *)
+        if equals Right l u2 then (* actually empty both sides *)
+          aux l s1 t2 acc
+        else
+          aux u2 s1 t2 ((v2,B.unify_age ~other:l u2) :: acc)
+      | Some _, Some _ (* both emerges, can't choose *)
+      | None, None -> (* none emerges *)
+        match s1, s2 with (* Are we done yet ? *)
+        | [], [] -> `Value acc
+        | _ :: _, [] | [], _ :: _-> unify_end l s1 s2 acc
+        | (v1,u1) :: t1, (v2,u2) :: t2 ->
+          try
+            match B.cmp ~oracle:left u1 u2, B.cmp ~oracle:right u1 u2 with (* Compare bounds *)
+            | _, Equal ->
+              (* u1 and u2 can be indeferently used right side
+                 -> use u1 as next bound
+                 Note: Asymetric choice, u2 may also be a good choice *)
+              aux u1 t1 t2 ((f v1 v2, B.unify_age ~other:u2 u1) :: acc)
+            | Equal, _ ->
+              (* u1 and u2 can be indeferently used left side
+                 -> use u2 as next bound *)
+              aux u2 t1 t2 ((f v1 v2, B.unify_age ~other:u1 u2) :: acc)
+            | (Greater | GreaterOrEqual),
+              (Greater | GreaterOrEqual | Uncomparable) ->
+              (* u1 > u2 on the left side, we are sure u2 doesn't appear left
+                 -> remove u2, merge slices *)
+              aux l s1 (merge_first Right l s2) acc
+            | (Lower | LowerOrEqual | Uncomparable),
+              (Lower | LowerOrEqual) ->
+              (* u1 < u2 on the right side, we are sure u1 doesn't appear right
+                 -> remove u1, merge slices *)
+              aux l (merge_first Left l s1) s2 acc
+            | (Greater | GreaterOrEqual), (Lower | LowerOrEqual) (* Can't choose which bound to remove first *)
+            | (Lower | LowerOrEqual | Uncomparable),
+              (Greater | GreaterOrEqual | Uncomparable) ->
+              aux l (merge_first Left l s1) (merge_first Right l s2) acc
+          with NothingToMerge -> (* There is nothing left to merge *)
+            unify_end l s1 s2 acc
+    in
+    let+ rev_segments = aux start s1 s2 [] in
+    let segments = List.rev rev_segments in
+    (* Iterate through segmentations *)
+    check { start ; segments ; padding = Bit.join p1 p2 }
+
+  let single padding lindex (* included *) uindex (* excluded *) value =
+    check {
+      padding ;
+      start = B.birth lindex ;
+      segments = [(value,B.birth uindex)] ;
+    }
+
+  let read ~oracle map reduce m lindex uindex =
+    let open (val (B.operators oracle)) in
+    let lindex = B.birth lindex and uindex = B.birth uindex in
+    let fold acc x =
+      Bottom.join reduce acc (`Value (map x))
+    in
+    let aux (l,acc) (v,u) =
+      u, if l > uindex || u <= lindex then acc else fold acc v
+    in
+    let acc = `Bottom in
+    let acc = if m.start <= lindex then acc else fold acc (M.of_raw m.padding) in
+    let last,acc = List.fold_left aux (m.start,acc) m.segments in
+    let acc = if last > uindex then acc else fold acc (M.of_raw m.padding) in
+    match acc with
+    | `Bottom -> assert false (* TODO: ensure that with typing *)
+    | `Value v -> v
+
+  let aging m = (* Extremities do not age *)
+    let rec aux acc = function
+      | [] -> acc
+      | [x] -> x :: acc
+      | (v,b) :: t -> aux ((v,B.aging b) :: acc) t
+    in
+    { m with segments = List.rev (aux [] m.segments) }
+
+  let age m = (* Age of the segmentation / age of the oldest bound *)
+    match m.segments with (* ignore m.start bound *)
+    | [] -> None
+    | (_,b) :: t ->
+      let rec aux acc = function
+        | [] -> None
+        | [_] -> Some acc (* ignore last bound *)
+        | (_,b) :: t -> aux (max acc (B.age b)) t
+      in
+      aux (B.age b) t
+
+  let remove_elderlies ~oracle m =
+    match age m with
+    | None -> m (* no inner bounds, should not happen if segments_limit > 2 *)
+    | Some oldest_age ->
+      (* Remove all bounds of this age *)
+      let rec aux acc l = function
+        | ([] | [_]) as t -> List.rev (t @ acc)
+        | ((v,u) :: t) as s ->
+          if B.age u = oldest_age then
+            aux acc l (smash_head ~oracle l s)
+          else
+            aux ((v,u) :: acc) u t
+      in
+      { m with segments = aux [] m.start m.segments }
+
+  let limit_size ~oracle m =
+    let limit = max 1 (Config.slice_limit ()) in
+    let rec aux m =
+      if List.length m.segments <= limit
+      then m
+      else aux (remove_elderlies ~oracle m)
+    in
+    aux m
+
+
+  (* TODO: partitioning strategies
+     1. reinforcement without loss
+     2. weak update without singularization
+     3. update reduces the number of segments to 3 *)
+  let update ~oracle f m lindex (* included *) uindex (* excluded *) = (* lindex < uindex *)
+    let open TopBot in
+    let open (val (B.operators oracle)) in
+    let same_bounds = lindex == uindex in (* happens when adding partitioning hints *)
+    let lindex = B.birth lindex and uindex = B.birth uindex in
+    (* (start,head) : segmentation kept identical below the update indexes,
+                      head is a list in reverse order
+       (l,v,u) : the segment (l,u) beeing overwriten with previous value v
+
+       head = (_,l) :: _
+    *)
+    let rec aux_before l s =
+      if lindex >= l
+      then aux_below l [] l s
+      else
+        let* l = B.lower_bound ~oracle:(fun _ -> oracle) lindex l in
+        aux_over l [] l (M.of_raw m.padding) l s
+    and aux_below start head l = fun t ->
+      match t with (* l <= lindex *)
+      | [] ->
+        aux_end start head l (M.of_raw m.padding) uindex []
+      | (v,u) :: t ->
+        if lindex >= u
+        then aux_below start ((v,u) :: head) u t
+        else aux_over start head l v u t
+    and aux_over start head l v u s = (* l <= lindex *)
+      if uindex <= u then
+        aux_end start head l v u s
+      else
+        match s with
+        | [] ->
+          let* u = B.upper_bound ~oracle:(fun _ -> oracle) u uindex in
+          let v = M.smash ~oracle v (M.of_raw m.padding) in
+          aux_end start head l v u []
+        | (v',u') :: t ->
+          (* TODO: do not smash for overwrites if the slices are covered by the write *)
+          aux_over start head l (M.smash ~oracle v v') u' t
+    and aux_end start head l v u tail = (* l <= lindex < uindex <= u*)
+      let+ new_v = f v in
+      let previous_is_empty = is_empty_segment ~oracle l lindex
+      and next_is_empty = is_empty_segment ~oracle uindex u in
+      let tail' =
+        (if previous_is_empty then [] else [(v,lindex)]) @
+        (if same_bounds then [] else [(new_v,uindex)]) @
+        (if next_is_empty then [] else [(v,u)]) @
+        tail
+      and head',start' = match head with (* change last bound to match lindex *)
+        | (v',_u) :: t when previous_is_empty -> (v',lindex) :: t, start
+        | [] when previous_is_empty -> [], lindex
+        | head -> head, start
+      in
+      check {
+        padding = m.padding;
+        segments = List.rev_append head' tail';
+        start = start';
+      }
+    in
+    aux_before m.start m.segments >>-:
+    aging >>-:
+    limit_size ~oracle
+
+  let remove_empty_segments m =
+    let unify_head_age (b' : B.t) : segments -> segments = function
+      | [] -> [] (* Start of segmentation, age is 0, do nothing *)
+      | (v,b) :: t -> (v, B.unify_age ~other:b' b) :: t
+    in
+    let rec aux l acc = function
+      | [] -> List.rev acc
+      | (v,u) :: t ->
+        (* Oracle cannot be used in case of incr here as it represents the
+           value of the bounds before the instruction, while we iterate
+           here over a segmentation interpreted after the instruction. *)
+        if B.equal_regardless_age l u then (* empty segment, remove v *)
+          aux l (unify_head_age u acc) t
+        else
+          aux u ((v,u) :: acc) t
+    in
+    check { m with segments = aux m.start [] m.segments }
+
+  let incr_bound ~oracle vi x m =
+    let incr = B.incr_or_constantify ~oracle vi x in
+    let rec incr_start p l = function
+      | [] -> `Top (* No more segments, top segmentation *)
+      | (v,u) :: t as s ->
+        match incr l with
+        | Some l' -> incr_end p l' (List.rev s)
+        | None -> (* No replacement, try to find a lower bound instead *)
+          match B.lower_const ~oracle l with
+          | `Top -> (* No lower bound, completely remove the segment *)
+            let p' = Bit.join p (M.raw v) in
+            incr_start p' u t
+          | `Value l' ->
+            let v' = M.smash ~oracle (M.of_raw p) v in
+            incr_end p (B.birth l') (List.rev ((v',u) :: t))
+    and incr_end p l = function (* In reverse order *)
+      | [] -> `Top (* No more segments, top segmentation *)
+      | (v,u) :: t ->
+        match incr u with
+        | Some u' -> incr_inner p l [] ((v,u') :: t)
+        | None -> (* No replacement, try to find an upper bound instead *)
+          match B.upper_const ~oracle u with
+          | `Top -> (* No upper bound, completely remove the segment *)
+            let p' = Bit.join p (M.raw v) in
+            incr_end p' l t
+          | `Value u' ->
+            let v' = M.smash ~oracle (M.of_raw p) v in
+            incr_inner p l [] ((v',B.birth u') :: t)
+    and incr_inner p l acc (* In right order *) = function (* In reverse order *)
+      | [] -> assert false
+      | [s] ->
+        check { start=l ; padding=p ; segments=s :: acc } |>
+        remove_empty_segments |>
+        fun m -> `Value m
+      | (v1,u) :: (v2,m) :: t -> (* u is already increased *)
+        match incr m with
+        | Some m' ->
+          incr_inner p l ((v1,u) :: acc) ((v2,m') :: t)
+        | None ->
+          let v' = M.smash ~oracle v1 v2 in
+          incr_inner p l acc ((v',u) :: t)
+    in
+    incr_start m.padding m.start m.segments
+
+  let map f m =
+    check { m with segments=List.map (fun (v,u) -> f v, u) m.segments }
+
+  let fold fs fp m acc =
+    List.fold_left (fun acc (v,_) -> fs v acc) (fp m.padding acc) m.segments
+
+  let add_segmentation_bounds ~oracle bounds m =
+    let add_bound m b =
+      match update ~oracle (fun x -> `Value x) m b b with
+      | `Value m -> m
+      | `Bottom -> assert false
+      | `Top ->
+        Kernel.warning ~current:true
+          "failed to introduce %a inside the array segmentation"
+          Bound.pretty b;
+        m
+    in
+    List.fold_left add_bound m bounds
+end
diff --git a/src/plugins/value/domains/multidim/segmentation.mli b/src/plugins/value/domains/multidim/segmentation.mli
new file mode 100644
index 00000000000..6eb1aeb54cd
--- /dev/null
+++ b/src/plugins/value/domains/multidim/segmentation.mli
@@ -0,0 +1,70 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    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 Lattice_extrema
+open Abstract_memory
+
+module type Config =
+sig
+  (* Limit on the number of slice after a write for array segmentations.
+     Makes sense above or equal to 1, though below 3 is counter-productive. *)
+  val slice_limit : unit -> int
+end
+
+module Bound :
+sig
+  type t
+  exception UnsupportedBoundExpression
+  val of_exp : Cil_types.exp -> t
+  val of_integer : Integer.t -> t
+  val succ : t -> t
+end
+
+module type Segmentation =
+sig
+  type bound = Bound.t
+  type submemory
+  type t
+  val pretty : Format.formatter -> t -> unit
+  val hash : t -> int
+  val equal : t -> t -> bool
+  val compare : t -> t -> int
+  val hull : oracle:oracle -> t -> (bound * bound) or_top
+  val raw : t -> Bit.t
+  val weak_erase : Bit.t -> t -> t
+  val is_included : t -> t -> bool
+  val unify : oracle:bioracle -> (submemory -> submemory -> submemory) ->
+    t -> t -> t or_top
+  val single : bit -> bound -> bound -> submemory -> t
+  val read : oracle:oracle ->
+    (submemory -> 'a) -> ('a -> 'a -> 'a) -> t -> bound -> bound -> 'a
+  val update : oracle:oracle -> (submemory -> submemory or_bottom) ->
+    t -> bound -> bound -> t or_top_bottom
+  val incr_bound :
+    oracle:oracle -> Cil_types.varinfo -> Integer.t option -> t -> t or_top
+  val map : (submemory -> submemory) -> t -> t
+  val fold : (submemory -> 'a -> 'a) -> (bit -> 'b -> 'a) -> t -> 'b -> 'a
+  val add_segmentation_bounds : oracle:oracle -> bound list -> t -> t
+end
+
+module Make (Config : Config) (M : ProtoMemory) :
+  Segmentation with type submemory = M.t
diff --git a/src/plugins/value/domains/multidim/typed_memory.ml b/src/plugins/value/domains/multidim/typed_memory.ml
new file mode 100644
index 00000000000..255de2e1965
--- /dev/null
+++ b/src/plugins/value/domains/multidim/typed_memory.ml
@@ -0,0 +1,596 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(* Ocaml compiler incorrectly considers that module MemorySafe is unused and
+   emits a warning *)
+[@@@warning "-60"]
+
+open Lattice_extrema
+open Abstract_offset
+open Abstract_memory
+
+module Bound = Segmentation.Bound
+
+(* Composition operator for compare function *)
+
+let (<?>) c lcmp =
+  if c = 0 then 0 else Lazy.force lcmp
+
+(* Types compatibility *)
+
+let typ_size t =
+  Integer.of_int (Cil.bitsSizeOf t)
+
+let are_typ_compatible t1 t2 =
+  Integer.equal (typ_size t1) (typ_size t2)
+
+(* Input modules *)
+
+module type Config =
+sig
+  val deps : State.t list
+  val slice_limit : unit -> int
+  val disjunctive_invariants : unit -> bool
+end
+
+module type Value =
+sig
+  type t
+
+  val name : string
+
+  val hash : t -> int
+  val equal : t -> t -> bool
+  val compare : t -> t -> int
+
+  val pretty : Format.formatter -> t -> unit
+  val of_bit : typ:Cil_types.typ -> bit -> t
+  val to_bit : t -> bit
+  val to_integer : t -> Integer.t option
+  val is_included : t -> t -> bool
+  val join : t -> t -> t
+end
+
+
+module Make (Config : Config) (V : Value) =
+struct
+  (* Recursively instanciate the typed memory *)
+  module rec ProtoMemory : ProtoMemory with type value = V.t =
+  struct
+    type value = V.t
+
+    type t =
+      | Raw of bit
+      | Scalar of memory_scalar
+      | Struct of memory_struct
+      | Disjunct of memory_disjunct
+      | Union of memory_union
+      | Array of memory_array
+    and memory_scalar = {
+      scalar_value: V.t;
+      scalar_type: Cil_types.typ;
+    }
+    and memory_struct = {
+      struct_value: S.t;
+      struct_type: Cil_types.compinfo;
+    }
+    and memory_disjunct = {
+      disj_value: D.t;
+      disj_type: Cil_types.compinfo;
+    }
+    (* unions are handled separately from struct to avoid confusion and error *)
+    and memory_union = {
+      union_value: t;
+      union_field: Cil_types.fieldinfo;
+      union_padding: bit;
+    }
+    and memory_array = {
+      array_value: A.t;
+      array_cell_type: Cil_types.typ;
+    }
+
+
+    let are_scalar_compatible s1 s2 =
+      are_typ_compatible s1.scalar_type s2.scalar_type
+
+    let are_aray_compatible a1 a2 =
+      are_typ_compatible a1.array_cell_type a2.array_cell_type
+
+    let are_compinfo_compatible ci1 ci2 =
+      ci1.Cil_types.ckey = ci2.Cil_types.ckey
+
+    let are_structs_compatible s1 s2 =
+      s1.struct_type.ckey = s2.struct_type.ckey
+
+    let are_disjuncts_compatible d1 d2 =
+      d1.disj_type.ckey = d2.disj_type.ckey
+
+    let are_union_compatible u1 u2 =
+      Cil_datatype.Fieldinfo.equal u1.union_field u2.union_field
+
+    let rec pp ~root fmt =
+      let prefix fmt =
+        if not root then Format.fprintf fmt " = "
+      in
+      function
+      | Raw b ->
+        Format.fprintf fmt "%t%a" prefix Bit.pretty b
+      | Scalar { scalar_value } ->
+        Format.fprintf fmt "%t%a" prefix V.pretty scalar_value
+      | Struct s ->
+        Format.fprintf fmt "%t%a" prefix S.pretty s.struct_value
+      | Disjunct d ->
+        Format.fprintf fmt "%t%a" prefix D.pretty d.disj_value
+      | Union u ->
+        Format.fprintf fmt ".%s%a"
+          u.union_field.Cil_types.fname
+          (pp ~root:false) u.union_value
+      | Array a ->
+        Format.fprintf fmt "%a" A.pretty a.array_value
+
+    let pretty fmt m = pp ~root:false fmt m
+    let pretty_root fmt m =
+      Format.fprintf fmt "@[<hv>%a@]" (pp ~root:true) m
+
+    let rec hash m = match m with
+      | Raw b -> Hashtbl.hash (
+          1,
+          Bit.hash b)
+      | Scalar s -> Hashtbl.hash (
+          2,
+          V.hash s.scalar_value,
+          Cil_datatype.Typ.hash s.scalar_type)
+      | Struct s -> Hashtbl.hash (
+          3,
+          S.hash s.struct_value,
+          Cil_datatype.Compinfo.hash s.struct_type)
+      | Disjunct d -> Hashtbl.hash (
+          4,
+          D.hash d.disj_value,
+          Cil_datatype.Compinfo.hash d.disj_type
+        )
+      | Union u -> Hashtbl.hash (
+          5,
+          hash u.union_value,
+          Cil_datatype.Fieldinfo.hash u.union_field,
+          Bit.hash u.union_padding)
+      | Array a -> Hashtbl.hash (
+          6,
+          A.hash a.array_value,
+          Cil_datatype.Typ.hash a.array_cell_type)
+
+    let rec equal m1 m2 =
+      match m1, m2 with
+      | Raw b1, Raw b2 -> Bit.equal b1 b2
+      | Scalar s1, Scalar s2 ->
+        V.equal s1.scalar_value s2.scalar_value &&
+        Cil_datatype.Typ.equal s1.scalar_type s2.scalar_type
+      | Struct s1, Struct s2 ->
+        S.equal s1.struct_value s2.struct_value &&
+        Cil_datatype.Compinfo.equal s1.struct_type s2.struct_type
+      | Disjunct d1, Disjunct d2 ->
+        D.equal d1.disj_value d2.disj_value &&
+        Cil_datatype.Compinfo.equal d1.disj_type d2.disj_type
+      | Union u1, Union u2 ->
+        equal u1.union_value u2.union_value &&
+        Bit.equal u1.union_padding u2.union_padding &&
+        Cil_datatype.Fieldinfo.equal u1.union_field u2.union_field
+      | Array a1, Array a2 ->
+        A.equal a1.array_value a2.array_value &&
+        Cil_datatype.Typ.equal a1.array_cell_type a2.array_cell_type
+      | (Raw _ | Scalar _ | Struct _ | Disjunct _ |  Union _ | Array _), _ ->
+        false
+
+    let rec compare m1 m2 =
+      match m1, m2 with
+      | Raw b1, Raw b2 -> Bit.compare b1 b2
+      | Scalar s1, Scalar s2 ->
+        V.compare s1.scalar_value s2.scalar_value <?>
+        lazy (Cil_datatype.Typ.compare s1.scalar_type s2.scalar_type)
+      | Struct s1, Struct s2 ->
+        S.compare s1.struct_value s2.struct_value <?>
+        lazy (Cil_datatype.Compinfo.compare s1.struct_type s2.struct_type)
+      | Disjunct d1, Disjunct d2 ->
+        D.compare d1.disj_value d2.disj_value <?>
+        lazy (Cil_datatype.Compinfo.compare d1.disj_type d2.disj_type)
+      | Union u1, Union u2 ->
+        compare u1.union_value u2.union_value <?>
+        lazy (Bit.compare u1.union_padding u2.union_padding) <?>
+        lazy (Cil_datatype.Fieldinfo.compare u1.union_field u2.union_field)
+      | Array a1, Array a2 ->
+        A.compare a1.array_value a2.array_value <?>
+        lazy (Cil_datatype.Typ.compare a1.array_cell_type a2.array_cell_type)
+      | Raw _, _ -> 1
+      | _, Raw _ -> -1
+      | Scalar _, _ -> 1
+      | _, Scalar _ -> -1
+      | Struct _, _ -> 1
+      | _, Struct _ -> -1
+      | Disjunct _, _ -> 1
+      | _, Disjunct _ -> -1
+      | Union _, _ -> 1
+      | _, Union _ -> -1
+
+    let of_raw b = Raw b
+
+    let rec raw m = match m with
+      | Raw b -> b
+      | Scalar s -> V.to_bit s.scalar_value
+      | Struct s -> S.raw s.struct_value
+      | Disjunct d -> D.raw d.disj_value
+      | Union u -> raw u.union_value
+      | Array a -> A.raw a.array_value
+
+    let of_value scalar_type scalar_value =
+      Scalar { scalar_type ; scalar_value }
+
+    let to_value typ = function
+      | Scalar s when are_typ_compatible s.scalar_type typ -> s.scalar_value
+      | m -> V.of_bit ~typ (raw m)
+
+    let rec to_singleton_int = function
+      | Scalar s -> V.to_integer s.scalar_value
+      | Raw (Zero _) -> Some Integer.zero
+      | Union u -> to_singleton_int u.union_value
+      | _ -> None
+
+    let rec weak_erase b = function
+      | Raw b' ->
+        Raw (Bit.join b' b)
+      | Scalar s when Bit.is_any b ->
+        Raw (Bit.join (V.to_bit s.scalar_value) b)
+      | Scalar s ->
+        let typ = s.scalar_type in
+        Scalar { s with scalar_value = V.(join (of_bit ~typ b) s.scalar_value) }
+      | Array a ->
+        Array { a with array_value = A.weak_erase b a.array_value }
+      | Struct s ->
+        Struct { s with struct_value = S.weak_erase b s.struct_value }
+      | Disjunct d ->
+        Disjunct { d with disj_value = D.weak_erase d.disj_type b d.disj_value }
+      | Union u -> Union {
+          u with
+          union_padding = Bit.join u.union_padding b;
+          union_value = weak_erase b u.union_value;
+        }
+
+    let rec is_included m1 m2 = match m1, m2 with
+      | _, Raw r -> Bit.is_included (raw m1) r
+      | Scalar s1, Scalar s2 ->
+        are_scalar_compatible s1 s2 &&
+        V.is_included s1.scalar_value s2.scalar_value
+      | Array a1, Array a2 ->
+        are_aray_compatible a1 a2 &&
+        A.is_included a1.array_value a2.array_value
+      | Struct s1, Struct s2 ->
+        are_structs_compatible s1 s2 &&
+        S.is_included s1.struct_value s2.struct_value
+      | Disjunct d1, Disjunct d2 ->
+        are_disjuncts_compatible d1 d2 &&
+        D.is_included d1.disj_value d2.disj_value
+      | Union u1, Union u2 ->
+        are_union_compatible u1 u2 &&
+        Bit.is_included u1.union_padding u2.union_padding &&
+        is_included u1.union_value u2.union_value
+      | (Raw _ | Scalar _ | Array _ | Struct _ | Disjunct _ | Union _), _ ->
+        false
+
+    let to_struct ~oracle ci = function
+      | Struct s when are_compinfo_compatible s.struct_type ci ->
+        s.struct_value
+      | Disjunct d when are_compinfo_compatible d.disj_type ci ->
+        D.to_struct ~oracle d.disj_value
+      | m -> S.of_raw (raw m)
+
+    let to_disj ci = function
+      | Struct s when are_compinfo_compatible s.struct_type ci ->
+        D.of_struct ci s.struct_value
+      | Disjunct d when are_compinfo_compatible d.disj_type ci ->
+        d.disj_value
+      | m -> D.of_raw ci (raw m)
+
+    let unify ~oracle f =
+      let open Top in
+      let raw_to_array side prototype b =
+        A.hull ~oracle:(oracle side) prototype >>-:
+        fun (l,u) -> A.single b l u (Raw b)
+      in
+      let rec aux m1 m2 =
+        match m1, m2 with
+        | Raw b1, Raw b2 -> Raw (Bit.join b1 b2)
+        | Scalar s1, Scalar s2
+          when are_typ_compatible s1.scalar_type s2.scalar_type ->
+          let size = typ_size s1.scalar_type in
+          let scalar_value = f ~size s1.scalar_value s2.scalar_value in
+          Scalar { s1 with scalar_value }
+        | Array a1, Array a2 when are_aray_compatible a1 a2 ->
+          begin match A.unify ~oracle aux a1.array_value a2.array_value with
+            | `Top -> Raw (Bit.join (raw m1) (raw m2))
+            | `Value array_value -> Array { a1 with array_value }
+          end
+        | Array a1, Raw b2 ->
+          let m =
+            let* array_value2 = raw_to_array Left a1.array_value b2 in
+            let+ array_value = A.unify ~oracle aux a1.array_value array_value2 in
+            Array { a1 with array_value }
+          in
+          begin match m with
+            | `Top -> weak_erase b2 (Array a1) (* Should not happen unless oracle is very unprecise *)
+            | `Value m -> m
+          end
+        | Raw b1, Array a2 ->
+          let m =
+            let* array_value1 = raw_to_array Right a2.array_value b1 in
+            let+ array_value = A.unify ~oracle aux array_value1 a2.array_value in
+            Array { a2 with array_value }
+          in
+          begin match m with
+            | `Top -> weak_erase b1 (Array a2) (* Should not happen unless oracle is very unprecise *)
+            | `Value m -> m
+          end
+        | Struct s1, Struct s2 when are_structs_compatible s1 s2 ->
+          let struct_value = S.unify aux s1.struct_value s2.struct_value in
+          Struct { s1 with struct_value }
+        | Struct s, Raw _ | Raw _, Struct s ->
+          (* Previously implemented as weak_erase, it is useful to continue
+             the recursive unification as there can be an emerging array segment
+             in the sub structure. *)
+          let v1 = to_struct ~oracle:(oracle Left) s.struct_type m1
+          and v2 = to_struct ~oracle:(oracle Right) s.struct_type m2 in
+          let struct_value = S.unify aux v1 v2 in
+          Struct { s with struct_value }
+        | Disjunct d1, Disjunct d2 when are_disjuncts_compatible d1 d2 ->
+          let disj_value = D.unify ~oracle aux d1.disj_value d2.disj_value in
+          Disjunct { d1 with disj_value }
+        | Disjunct d, Raw _ | Raw _, Disjunct d ->
+          let v1 = to_disj d.disj_type m1
+          and v2 = to_disj d.disj_type m2 in
+          let disj_value = D.unify ~oracle aux v1 v2 in
+          Disjunct { d with disj_value }
+        | Union u1, Union u2 when are_union_compatible u1 u2 ->
+          Union {
+            u1 with
+            union_value = aux u1.union_value u2.union_value;
+            union_padding = Bit.join u1.union_padding u2.union_padding;
+          }
+        | m, Raw b | Raw b, m -> weak_erase b m
+        | _,_ -> Raw (Bit.join (raw m1) (raw m2))
+      in
+      aux
+
+    let join ~oracle = unify ~oracle (fun ~size:_ -> V.join)
+
+    let smash ~oracle = join ~oracle:(fun _ -> oracle)
+
+    let read ~oracle (map : Cil_types.typ -> t -> 'a) (reduce : 'a -> 'a -> 'a) =
+      let rec aux offset m =
+        match offset, m with
+        | NoOffset t, m ->
+          map t m
+        | Field (fi, offset'), Struct s
+          when s.struct_type.ckey = fi.fcomp.ckey ->
+          aux offset' (S.read s.struct_value fi)
+        | Field (fi, offset'), Disjunct d
+          when d.disj_type.ckey = fi.fcomp.ckey ->
+          D.read (aux offset') reduce d.disj_value fi
+        | Field (fi, offset'), Union u
+          when Cil_datatype.Fieldinfo.equal u.union_field fi ->
+          aux offset' u.union_value
+        | Index (exp, index, elem_type, offset'), Array a
+          when are_typ_compatible a.array_cell_type elem_type ->
+          let open Top in
+          let m =
+            let+ lindex, uindex = match Option.map Bound.of_exp exp with
+              | Some b -> `Value (b, b)
+              | None | exception Bound.UnsupportedBoundExpression ->
+                let l, u = Int_val.min_and_max index in
+                let+ l = Top.of_option l
+                and+ u = Top.of_option u in
+                Bound.of_integer l, Bound.of_integer u
+            in
+            A.read ~oracle (aux offset') reduce a.array_value lindex uindex
+          in
+          begin match m with
+            | `Value v -> v
+            | `Top ->
+              A.fold
+                (fun m' acc -> reduce (aux offset' m') acc)
+                (fun p () -> aux offset' (Raw p))
+                a.array_value ()
+          end
+        | _, m -> (* structure mismatch *)
+          let r = Raw (raw m) in
+          match offset with
+          | NoOffset t -> map t r
+          | Field (_, offset') | Index (_, _, _, offset') -> aux offset' r
+      in
+      aux
+
+    let update ~oracle (f : weak:bool -> Cil_types.typ -> t -> t or_bottom) =
+      let open Bot in
+      let rec aux ~weak offset m =
+        match offset with
+        | NoOffset t ->
+          f ~weak t m
+        | Field (fi, offset') ->
+          if fi.fcomp.cstruct then (* Structures *)
+            if Config.disjunctive_invariants () then
+              let old = to_disj fi.fcomp m in
+              let+ disj_value = D.update ~oracle (aux ~weak offset') old fi in
+              Disjunct { disj_type = fi.fcomp ; disj_value }
+            else
+              let old = to_struct ~oracle fi.fcomp m in
+              let+ struct_value = S.update (aux ~weak offset') old fi in
+              Struct { struct_type = fi.fcomp; struct_value }
+          else (* Unions *)
+            let old = match m with
+              | Union u when Cil_datatype.Fieldinfo.equal u.union_field fi -> u
+              | _ ->
+                let b = raw m in
+                { union_value = Raw b ; union_field = fi ; union_padding = b }
+            in
+            let+ union_value = aux ~weak offset' old.union_value in
+            Union { old with union_value }
+        | Index (exp, index, elem_type, offset') ->
+          let m' =
+            let open TopBot in
+            let* lindex, uindex, weak =
+              match Option.map Bound.of_exp exp with
+              | Some b -> `Value (b, Bound.succ b, weak)
+              | None | exception Bound.UnsupportedBoundExpression ->
+                begin match Int_val.min_and_max index with
+                  | None, _ | _, None -> `Top
+                  | Some l, Some u ->
+                    let l' = Bound.of_integer l
+                    and u' = Bound.(succ (of_integer u)) in
+                    `Value (l', u', weak || Integer.equal l u)
+                end
+            in
+            match m with
+            | Array a when are_typ_compatible a.array_cell_type elem_type ->
+              let+ array_value = A.update ~oracle (aux ~weak offset')
+                  a.array_value lindex uindex in
+              Array { a with array_value }
+            | _ ->
+              let b = raw m in
+              let+ new_value = aux ~weak offset' (Raw b) in
+              let array_value = A.single b lindex uindex new_value in
+              Array { array_cell_type = elem_type ; array_value }
+          in
+          match m' with
+          | `Top -> (* No suitable bound for the partition *)
+            let+ v = f ~weak:true Cil.voidType m in v
+          | `Bottom | `Value _ as m -> m
+      in aux
+
+    let incr_bound ~oracle vi x m = (* TODO: keep subtree when nothing changes *)
+      let rec aux = function
+        | (Raw _ | Scalar _) as m -> m
+        | Struct s -> Struct { s with struct_value = S.map aux s.struct_value }
+        | Disjunct d -> Disjunct { d with disj_value = D.map aux d.disj_value }
+        | Union u -> Union { u with union_value = aux u.union_value }
+        | Array a ->
+          match  A.incr_bound ~oracle vi x a.array_value with
+          | `Top -> Raw (A.raw a.array_value)
+          | `Value array_value ->
+            Array { a with array_value=A.map aux array_value }
+      in
+      aux m
+
+    let add_segmentation_bounds ~oracle ~typ bounds =
+      let convert_bound exp =
+        try
+          Some (Bound.of_exp exp)
+        with Bound.UnsupportedBoundExpression -> None
+      in
+      let bounds = List.filter_map convert_bound bounds in
+      function
+      | (Raw b as m) ->
+        begin match bounds with
+          | l :: u :: t ->
+            let array_value = A.single b l u m in
+            let array_value = A.add_segmentation_bounds ~oracle t array_value in
+            Array { array_cell_type = typ ; array_value }
+          | _ -> m (* Can't build a segmentation with only one bound *)
+        end
+      | Array a ->
+        let array_value = A.add_segmentation_bounds ~oracle bounds a.array_value
+        in
+        Array { a with array_value }
+      | m -> m (* Ignore segmentation hints on non-array *)
+  end
+  and S : Abstract_structure.Structure with type submemory = ProtoMemory.t =
+    Abstract_structure.Make (Config) (ProtoMemory)
+  and A : Segmentation.Segmentation with type submemory = ProtoMemory.t =
+    Segmentation.Make (Config) (ProtoMemory)
+  and D : Abstract_structure.Disjunction with type submemory = ProtoMemory.t and type structure = S.t =
+    Abstract_structure.Disjunction (Config) (ProtoMemory) (S)
+
+  include ProtoMemory
+
+  type location = Abstract_offset.t
+
+  let pretty = pretty_root
+
+  (* Constuctors *)
+
+  let top = of_raw Bit.top
+  let zero = of_raw Bit.zero
+  let is_top m = m = top
+
+  (* Widening *)
+
+  let widen = unify
+
+  (* Read/Write accesses *)
+
+  let get ~oracle (m : t) (loc : location) : value =
+    read ~oracle to_value V.join loc m
+
+  let extract ~oracle (m : t) (loc : location) : t =
+    read ~oracle (fun _typ x -> x) (smash ~oracle) loc m
+
+  let update' ~oracle ~weak f offset m =
+    let f' ~weak typ m =
+      `Value (f ~weak typ m)
+    in
+    Bottom.non_bottom (update ~oracle ~weak f' offset m)
+
+  let set ~oracle ~weak m offset new_v =
+    let f ~weak typ m =
+      of_value typ (if weak then V.join (to_value typ m) new_v else new_v)
+    in
+    update' ~oracle ~weak f offset m
+
+  let reinforce ~oracle f m offset =
+    let open Bottom in
+    let f' ~weak typ m =
+      if weak
+      then `Value m
+      else f (to_value typ m) >>-: of_value typ
+    in
+    update ~oracle ~weak:false f' offset m
+
+  let erase ~oracle ~weak m offset b =
+    let f ~weak _typ m =
+      if weak then
+        weak_erase b m
+      else
+        of_raw b
+    in
+    update' ~oracle ~weak f offset m
+
+  let overwrite ~oracle ~weak dst offset src =
+    let f ~weak _typ m =
+      if weak then
+        smash ~oracle m src
+      else
+        src
+    in
+    update' ~oracle ~weak f offset dst
+
+  let segmentation_hint ~oracle m offset bounds =
+    let f ~weak:_ typ m =
+      add_segmentation_bounds ~oracle ~typ bounds m
+    in
+    update' ~oracle ~weak:false f offset m
+end
diff --git a/src/kernel_services/abstract_interp/abstract_memory.mli b/src/plugins/value/domains/multidim/typed_memory.mli
similarity index 84%
rename from src/kernel_services/abstract_interp/abstract_memory.mli
rename to src/plugins/value/domains/multidim/typed_memory.mli
index d9be5b2f0de..d47e52af53c 100644
--- a/src/kernel_services/abstract_interp/abstract_memory.mli
+++ b/src/plugins/value/domains/multidim/typed_memory.mli
@@ -20,29 +20,23 @@
 (*                                                                        *)
 (**************************************************************************)
 
-type size = Integer.t
+open Lattice_extrema
+open Abstract_memory
 
-type initialization =
-  | SurelyInitialized
-  | MaybeUninitialized
-
-type bit =
-  | Uninitialized (* Uninitialized everywhere *)
-  | Zero of initialization (* Zero or uninitialized everywhere *)
-  | Any of Base.SetLattice.t * initialization
-  (* Undetermined anywhere, and can contain bits
-     of pointers. If the base set is empty,
-     the bit can only come from numerical values. *)
-
-
-module Bit :
+(* Configuration of the Abstract Memory model *)
+module type Config =
 sig
-  type t = bit
+  (* Dependencies of the hash-consing table. The table will be cleared
+     whenever one of those dependencies is cleared. *)
+  val deps : State.t list
 
-  val uninitialized : t
-  val zero : t
-  val numerical : t
-  val top : t
+  (* Limit on the number of slice after a write for array segmentations.
+     Makes sense above or equal to 1, though below 3 is counter-productive. *)
+  val slice_limit : unit -> int
+
+  (* Whether the memory model try to infer some structure disjunctive
+     invariants. *)
+  val disjunctive_invariants : unit -> bool
 end
 
 (* Values the memory is mapped to *)
@@ -64,28 +58,7 @@ sig
   val join : t -> t -> t
 end
 
-module type Config =
-sig
-  (* Dependencies of the hash-consing table. The table will be cleared
-     whenever one of those dependencies is cleared. *)
-  val deps : State.t list
-
-  (* Limit on the number of slice after a write for array segmentations.
-     Makes sense above or equal to 1, though below 3 is counter-productive. *)
-  val slice_limit : unit -> int
-
-  (* Whether the memory model try to infer some structure disjunctive
-     invariants. *)
-  val disjunctive_invariants : unit -> bool
-end
-
-
-type side = Left | Right
-type oracle = Cil_types.exp -> Int_val.t
-type bioracle = side -> oracle
-type strength = Strong | Weak | Reinforce (* update strength *)
-
-module TypedMemory (Config : Config) (Value : Value) :
+module Make (Config : Config) (Value : Value) :
 sig
   type location = Abstract_offset.t
   type value = Value.t
@@ -123,7 +96,7 @@ sig
   (* Reinforce values on a set of locations when the locations match the
      memory structure ; does nothing on locations that cannot be matched *)
   val reinforce : oracle:oracle ->
-    (value -> value Bottom.or_bottom) ->  t -> location -> t Bottom.or_bottom
+    (value -> value or_bottom) ->  t -> location -> t or_bottom
 
   (* Test inclusion of one memory map into another *)
   val is_included : t -> t -> bool
-- 
GitLab