diff --git a/src/plugins/eva/types/assigns.ml b/src/plugins/eva/types/assigns.ml
new file mode 100644
index 0000000000000000000000000000000000000000..643e847c4112fa2c98e8e9c4324258658e3ff91a
--- /dev/null
+++ b/src/plugins/eva/types/assigns.ml
@@ -0,0 +1,232 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2023                                               *)
+(*    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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+module DepsOrUnassigned = struct
+  module Datatype_Input = struct
+    include Datatype.Serializable_undefined
+
+    type t =
+      | DepsBottom
+      | Unassigned
+      | AssignedFrom of Deps.t
+      | MaybeAssignedFrom of Deps.t
+    [@@deriving eq,ord]
+    let name = "Eva.Froms.DepsOrUnassigned"
+
+    let pretty fmt = function
+      | DepsBottom -> Format.pp_print_string fmt "DEPS_BOTTOM"
+      | Unassigned -> Format.pp_print_string fmt "UNASSIGNED"
+      | AssignedFrom fd -> Deps.pretty_precise fmt fd
+      | MaybeAssignedFrom fd ->
+        Format.fprintf fmt "%a (or UNASSIGNED)" Deps.pretty_precise fd
+
+    let hash = function
+      | DepsBottom -> 1
+      | Unassigned -> 2
+      | AssignedFrom fd -> Hashtbl.hash (3, Deps.hash fd)
+      | MaybeAssignedFrom fd -> Hashtbl.hash (4, Deps.hash fd)
+
+    let reprs = Unassigned :: List.map (fun r -> AssignedFrom r) Deps.reprs
+  end
+
+  include Datatype.Make (Datatype_Input)
+  include Datatype_Input
+
+  let join d1 d2 = match d1, d2 with
+    | DepsBottom, d | d, DepsBottom -> d
+    | Unassigned, Unassigned -> Unassigned
+    | Unassigned, AssignedFrom fd | AssignedFrom fd, Unassigned ->
+      MaybeAssignedFrom fd
+    | Unassigned, (MaybeAssignedFrom _ as d)
+    | (MaybeAssignedFrom _ as d), Unassigned ->
+      d
+    | AssignedFrom fd1, AssignedFrom fd2 ->
+      AssignedFrom (Deps.join fd1 fd2)
+    | AssignedFrom fd1, MaybeAssignedFrom fd2
+    | MaybeAssignedFrom fd1, AssignedFrom fd2
+    | MaybeAssignedFrom fd1, MaybeAssignedFrom fd2 ->
+      MaybeAssignedFrom (Deps.join fd1 fd2)
+
+  let narrow _ _ = assert false (* not used yet *)
+
+  let is_included d1 d2 = match d1, d2 with
+    | DepsBottom, (DepsBottom | Unassigned | AssignedFrom _ |
+                   MaybeAssignedFrom _)
+    | Unassigned, (Unassigned | AssignedFrom _ | MaybeAssignedFrom _) ->
+      true
+    | MaybeAssignedFrom fd1, (AssignedFrom fd2 | MaybeAssignedFrom fd2)
+    | AssignedFrom fd1, AssignedFrom fd2 ->
+      Deps.is_included fd1 fd2
+    | (Unassigned | AssignedFrom _ | MaybeAssignedFrom _), DepsBottom
+    | (AssignedFrom _ | MaybeAssignedFrom _), Unassigned
+    | AssignedFrom _, MaybeAssignedFrom _ ->
+      false
+
+  let bottom = DepsBottom
+  let top = MaybeAssignedFrom Deps.top
+  let default = Unassigned
+
+  let to_zone = function
+    | DepsBottom | Unassigned -> Locations.Zone.bottom
+    | AssignedFrom fd | MaybeAssignedFrom fd -> Deps.to_zone fd
+
+  let may_be_unassigned = function
+    | DepsBottom | AssignedFrom _ -> false
+    | Unassigned | MaybeAssignedFrom _ -> true
+end
+
+module Memory = struct
+  (* A From table is internally represented as a Lmap of [DepsOrUnassigned].
+     However, the API mostly hides this fact, and exports access functions
+     that take or return [Deps.t] values. This way, the user needs not
+     understand the subtleties of DepsBottom/Unassigned/MaybeAssigned. *)
+
+  include Lmap_bitwise.Make_bitwise(DepsOrUnassigned)
+
+  let add_binding_precise_loc ~exact access m loc v =
+    let aux_one_loc loc m =
+      let loc = Locations.valid_part access loc in
+      add_binding_loc ~exact m loc (DepsOrUnassigned.AssignedFrom v)
+    in
+    Precise_locs.fold aux_one_loc loc m
+
+  let add_binding ~exact m z v =
+    add_binding ~exact m z (DepsOrUnassigned.AssignedFrom v)
+
+  let add_binding_loc ~exact m loc v =
+    add_binding_loc ~exact m loc (DepsOrUnassigned.AssignedFrom v)
+
+  (* This is the auxiliary datastructure used to write the function [find].
+     When we iterate over a offsetmap of value [DepsOrUnassigned], we obtain
+     two things: (1) some dependencies; (2) some intervals that may have not
+     been assigned, and that will appear as data dependencies (once we know
+     the base we are iterating on). *)
+  type find_offsm = {
+    fo_itvs: Int_Intervals.t;
+    fo_deps: Deps.t;
+  }
+
+  (* Once the base is known, we can obtain something of type [Deps.t] *)
+  let convert_find_offsm base fp =
+    let z = Locations.Zone.inject base fp.fo_itvs in
+    Deps.add_data fp.fo_deps z
+
+  let empty_find_offsm = {
+    fo_itvs = Int_Intervals.bottom;
+    fo_deps = Deps.bottom;
+  }
+
+  let join_find_offsm fp1 fp2 =
+    if fp1 == empty_find_offsm then fp2
+    else if fp2 == empty_find_offsm then fp1
+    else {
+      fo_itvs = Int_Intervals.join fp1.fo_itvs fp2.fo_itvs;
+      fo_deps = Deps.join fp1.fo_deps fp2.fo_deps;
+    }
+
+  (* Auxiliary function that collects the dependencies on some intervals of
+     an offsetmap. *)
+  let find_precise_offsetmap : Int_Intervals.t -> LOffset.t -> find_offsm =
+    let cache = Hptmap_sig.PersistentCache "Eva.Froms.Memory.find_precise" in
+    let aux_find_offsm ib ie v =
+      (* If the interval can be unassigned, we collect its bound. We also
+         return the dependencies stored at this interval. *)
+      let default, v = match v with
+        | DepsOrUnassigned.DepsBottom -> false, Deps.bottom
+        | DepsOrUnassigned.Unassigned -> true, Deps.bottom
+        | DepsOrUnassigned.MaybeAssignedFrom v -> true, v
+        | DepsOrUnassigned.AssignedFrom v -> false, v
+      in
+      { fo_itvs =
+          if default
+          then Int_Intervals.inject_bounds ib ie
+          else Int_Intervals.bottom;
+        fo_deps = v }
+    in
+    (* Partial application is important *)
+    LOffset.fold_join_itvs
+      ~cache aux_find_offsm join_find_offsm empty_find_offsm
+
+  (* Collecting dependencies on a given zone. *)
+  let find_precise : t -> Locations.Zone.t -> Deps.t =
+    let both = find_precise_offsetmap in
+    let conv = convert_find_offsm in
+    (* We are querying a zone for which no dependency is stored. Hence, every
+       base is implicitly bound to [Unassigned]. *)
+    let empty_map z = Deps.data z in
+    let join = Deps.join in
+    let empty = Deps.bottom in
+    (* Partial application is important *)
+    let f = fold_join_zone ~both ~conv ~empty_map ~join ~empty in
+    fun m z ->
+      match m with
+      | Top -> Deps.top
+      | Bottom -> Deps.bottom
+      | Map m -> try f z m with Abstract_interp.Error_Top -> Deps.top
+
+  let find_precise_loffset loffset base itv =
+    let fo = find_precise_offsetmap itv loffset in
+    convert_find_offsm base fo
+
+  let find z m = Deps.to_zone (find_precise z m)
+end
+
+module Datatype_Input = struct
+  include Datatype.Serializable_undefined
+  type t = {
+    deps_return : Deps.t;
+    deps_table : Memory.t
+  }
+  [@@deriving eq,ord]
+  let name = "Eva.Froms"
+  let top = {
+    deps_return = Deps.top;
+    deps_table = Memory.top;
+  }
+  let reprs = [ top ]
+  let hash froms =
+    Hashtbl.hash (Memory.hash froms.deps_table, Deps.hash froms.deps_return)
+  let pretty fmt { deps_return = r ; deps_table = t } =
+    Format.fprintf fmt "%a@\n\\result FROM @[%a@]@\n"
+      Memory.pretty t
+      Deps.pretty r
+end
+
+include Datatype.Make (Datatype_Input)
+include Datatype_Input
+
+let join x y =
+  { deps_return = Deps.join x.deps_return y.deps_return ;
+    deps_table = Memory.join x.deps_table y.deps_table }
+
+let outputs { deps_table = t } =
+  match t with
+  | Memory.Top -> Locations.Zone.top
+  | Memory.Bottom -> Locations.Zone.bottom
+  | Memory.Map m ->
+    Memory.fold
+      (fun z v acc ->
+         let open DepsOrUnassigned in
+         match v with
+         | DepsBottom | Unassigned -> acc
+         | AssignedFrom _ | MaybeAssignedFrom _ -> Locations.Zone.join z acc)
+      m Locations.Zone.bottom
diff --git a/src/plugins/eva/types/froms.ml b/src/plugins/eva/types/froms.ml
index c083e1b45b4ead45b42557a6a0f3e72667bae84c..a79c103c59894e2626a152cf3e583135d44c1782 100644
--- a/src/plugins/eva/types/froms.ml
+++ b/src/plugins/eva/types/froms.ml
@@ -21,17 +21,15 @@
 (**************************************************************************)
 
 module DepsOrUnassigned = struct
-
-  type t =
-    | DepsBottom
-    | Unassigned
-    | AssignedFrom of Deps.t
-    | MaybeAssignedFrom of Deps.t
-
   module Datatype_Input = struct
     include Datatype.Serializable_undefined
 
-    type nonrec t = t
+    type t =
+      | DepsBottom
+      | Unassigned
+      | AssignedFrom of Deps.t
+      | MaybeAssignedFrom of Deps.t
+    [@@deriving eq,ord]
     let name = "Eva.Froms.DepsOrUnassigned"
 
     let pretty fmt = function
@@ -42,36 +40,16 @@ module DepsOrUnassigned = struct
         Format.fprintf fmt "%a (or UNASSIGNED)" Deps.pretty_precise fd
 
     let hash = function
-      | DepsBottom -> 3
-      | Unassigned -> 17
-      | AssignedFrom fd -> 37 + 13 * Deps.hash fd
-      | MaybeAssignedFrom fd -> 57 + 123 * Deps.hash fd
-
-    let compare d1 d2 = match d1, d2 with
-      | DepsBottom, DepsBottom
-      | Unassigned, Unassigned -> 0
-      | AssignedFrom fd1, AssignedFrom fd2
-      | MaybeAssignedFrom fd1, MaybeAssignedFrom fd2 ->
-        Deps.compare fd1 fd2
-      | DepsBottom, (Unassigned | AssignedFrom _ | MaybeAssignedFrom _)
-      | Unassigned, (AssignedFrom _ | MaybeAssignedFrom _)
-      | AssignedFrom _, MaybeAssignedFrom _ ->
-        -1
-      | (Unassigned | AssignedFrom _ | MaybeAssignedFrom _), DepsBottom
-      | (AssignedFrom _ | MaybeAssignedFrom _), Unassigned
-      | MaybeAssignedFrom _, AssignedFrom _ ->
-        1
-
-    let equal = Datatype.from_compare
+      | DepsBottom -> 1
+      | Unassigned -> 2
+      | AssignedFrom fd -> Hashtbl.hash (3, Deps.hash fd)
+      | MaybeAssignedFrom fd -> Hashtbl.hash (4, Deps.hash fd)
 
     let reprs = Unassigned :: List.map (fun r -> AssignedFrom r) Deps.reprs
-
-    let structural_descr =
-      let d = Deps.packed_descr in
-      Structural_descr.t_sum [| [| d |]; [| d |] |]
   end
 
-  include (Datatype.Make (Datatype_Input) : Datatype.S with type t := t)
+  include Datatype.Make (Datatype_Input)
+  include Datatype_Input
 
   let join d1 d2 = match d1, d2 with
     | DepsBottom, d | d, DepsBottom -> d