diff --git a/src/kernel_services/abstract_interp/function_Froms.ml b/src/kernel_services/abstract_interp/function_Froms.ml
deleted file mode 100644
index f6f2f5e8a931121cbb58b2d66caf6c5fdaa4e3e5..0000000000000000000000000000000000000000
--- a/src/kernel_services/abstract_interp/function_Froms.ml
+++ /dev/null
@@ -1,633 +0,0 @@
-(**************************************************************************)
-(*                                                                        *)
-(*  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).            *)
-(*                                                                        *)
-(**************************************************************************)
-
-open Locations
-
-module Deps =
-struct
-
-  type deps = {
-    data: Zone.t;
-    indirect: Zone.t;
-  }
-
-  let to_zone {data; indirect} = Zone.join data indirect
-
-  module DatatypeFromDeps = Datatype.Make(struct
-      type t = deps
-
-      let name = "Function_Froms.Deps.from_deps"
-
-      let hash fd =
-        Zone.hash fd.data + 37 * Zone.hash fd.indirect
-
-      let compare fd1 fd2 =
-        let c = Zone.compare fd1.data fd2.data in
-        if c <> 0 then c
-        else Zone.compare fd1.indirect fd2.indirect
-
-      let equal = Datatype.from_compare
-
-      let pretty fmt d = Zone.pretty fmt (to_zone d)
-
-      let reprs =
-        List.map (fun z -> {data = z; indirect = z}) Zone.reprs
-
-      let structural_descr =
-        Structural_descr.t_record [| Zone.packed_descr; Zone.packed_descr; |]
-      let rehash = Datatype.identity
-
-      let mem_project = Datatype.never_any_project
-
-      let copy = Datatype.undefined
-    end)
-
-  include DatatypeFromDeps
-
-  let pretty_precise fmt {data; indirect} =
-    let bottom_data = Zone.is_bottom data in
-    let bottom_indirect = Zone.is_bottom indirect in
-    match bottom_indirect, bottom_data with
-    | true, true ->
-      Format.fprintf fmt "\\nothing"
-    | true, false ->
-      Format.fprintf fmt "direct: %a"
-        Zone.pretty data
-    | false, true ->
-      Format.fprintf fmt "indirect: %a"
-        Zone.pretty indirect
-    | false, false ->
-      Format.fprintf fmt "indirect: %a; direct: %a"
-        Zone.pretty indirect
-        Zone.pretty data
-
-  let from_data_deps z = { data = z; indirect = Zone.bottom }
-  let from_indirect_deps z = { data = Zone.bottom; indirect = z }
-
-  let bottom = {
-    data = Zone.bottom;
-    indirect = Zone.bottom;
-  }
-
-  let top = {
-    data = Zone.top;
-    indirect = Zone.top;
-  }
-
-  let is_included fd1 fd2 =
-    Zone.is_included fd1.data fd2.data &&
-    Zone.is_included fd1.indirect fd2.indirect
-
-  let join fd1 fd2 =
-    if fd1 == bottom then fd2
-    else if fd2 == bottom then fd1
-    else {
-      data = Zone.join fd1.data fd2.data;
-      indirect = Zone.join fd1.indirect fd2.indirect
-    }
-
-  let narrow fd1 fd2 = {
-    data = Zone.narrow fd1.data fd2.data;
-    indirect = Zone.narrow fd1.indirect fd2.indirect
-  }
-
-  let add_data_dep fd data =
-    { fd with data = Zone.join fd.data data }
-
-  let add_indirect_dep fd indirect =
-    { fd with indirect = Zone.join fd.indirect indirect }
-
-  let map f fd = {
-    data = f fd.data;
-    indirect = f fd.indirect;
-  }
-
-end
-
-module DepsOrUnassigned = struct
-
-  type deps_or_unassigned =
-    | DepsBottom
-    | Unassigned
-    | AssignedFrom of Deps.t
-    | MaybeAssignedFrom of Deps.t
-
-  module DatatypeDeps = Datatype.Make(struct
-      type t = deps_or_unassigned
-
-      let name = "Function_Froms.Deps.deps"
-
-      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 ->
-          (* '(or UNASSIGNED)' would be a better pretty-printer, we use
-             '(and SELF)' only for compatibility reasons *)
-          Format.fprintf fmt "%a (and SELF)" 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
-
-      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 |] |]
-      let rehash = Datatype.identity
-
-      let mem_project = Datatype.never_any_project
-
-      let copy = Datatype.undefined
-
-    end)
-
-  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
-
-  include DatatypeDeps
-
-  let subst f d = match d with
-    | DepsBottom -> DepsBottom
-    | Unassigned -> Unassigned
-    | AssignedFrom fd ->
-      let fd' = f fd in
-      if fd == fd' then d else AssignedFrom fd'
-    | MaybeAssignedFrom fd ->
-      let fd' = f fd in
-      if fd == fd' then d else MaybeAssignedFrom fd'
-
-  let pretty_precise = pretty
-
-  let to_zone = function
-    | DepsBottom | Unassigned -> Zone.bottom
-    | AssignedFrom fd | MaybeAssignedFrom fd -> Deps.to_zone fd
-
-  let to_deps = function
-    | DepsBottom | Unassigned -> Deps.bottom
-    | AssignedFrom fd | MaybeAssignedFrom fd -> fd
-
-  let extract_data = function
-    | DepsBottom | Unassigned -> Zone.bottom
-    | AssignedFrom fd | MaybeAssignedFrom fd -> fd.Deps.data
-
-  let extract_indirect = function
-    | DepsBottom | Unassigned -> Zone.bottom
-    | AssignedFrom fd | MaybeAssignedFrom fd -> fd.Deps.indirect
-
-  let may_be_unassigned = function
-    | DepsBottom | AssignedFrom _ -> false
-    | Unassigned | MaybeAssignedFrom _ -> true
-
-  let compose d1 d2 =
-    match d1, d2 with
-    | DepsBottom, _ | _, DepsBottom ->
-      DepsBottom (* could indicate dead code. Not used in practice anyway *)
-    | Unassigned, _ -> d2
-    | AssignedFrom _, _ -> d1
-    | MaybeAssignedFrom _, Unassigned -> d1
-    | MaybeAssignedFrom d1, MaybeAssignedFrom d2 ->
-      MaybeAssignedFrom (Deps.join d1 d2)
-    | MaybeAssignedFrom d1, AssignedFrom d2 ->
-      AssignedFrom (Deps.join d1 d2)
-
-  (* for backwards compatibility *)
-  let pretty fmt fd =
-    match fd with
-    | DepsBottom -> Format.pp_print_string fmt "DEPS_BOTTOM"
-    | Unassigned -> Format.pp_print_string fmt "(SELF)"
-    | AssignedFrom d -> Zone.pretty fmt (Deps.to_zone d)
-    | MaybeAssignedFrom d ->
-      Format.fprintf fmt "%a (and SELF)" Zone.pretty (Deps.to_zone d)
-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 () = imprecise_write_msg := "dependencies to update"
-
-  let pretty_skip = function
-    | DepsOrUnassigned.DepsBottom -> true
-    | DepsOrUnassigned.Unassigned -> true
-    | DepsOrUnassigned.AssignedFrom _ -> false
-    | DepsOrUnassigned.MaybeAssignedFrom _ -> false
-
-  let pretty =
-    pretty_generic_printer
-      ~skip_v:pretty_skip ~pretty_v:DepsOrUnassigned.pretty ~sep:"FROM" ()
-
-  let pretty_ind_data =
-    pretty_generic_printer
-      ~skip_v:pretty_skip ~pretty_v:DepsOrUnassigned.pretty_precise ~sep:"FROM"
-      ()
-
-
-  (** 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 = Zone.inject base fp.fo_itvs in
-    Deps.add_data_dep 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 "Function_Froms.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 -> 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.from_data_deps 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 z m =
-    Deps.to_zone (find_precise z m)
-
-  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 bind_var vi v m =
-    let z = Locations.zone_of_varinfo vi in
-    add_binding ~exact:true m z (DepsOrUnassigned.AssignedFrom v)
-
-  let unbind_var vi m =
-    remove_base (Base.of_varinfo vi) 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)
-
-  let is_unassigned m =
-    LOffset.is_same_value m DepsOrUnassigned.Unassigned
-
-  (* Unassigned is a neutral value for compose, on both sides *)
-  let decide_compose m1 m2 =
-    if m1 == m2 || is_unassigned m1 then LOffset.ReturnRight
-    else if is_unassigned m2 then LOffset.ReturnLeft
-    else LOffset.Recurse
-
-  let compose_map =
-    let cache = Hptmap_sig.PersistentCache "Function_Froms.Memory.compose" in
-    (* Partial application is important because of the cache. Idempotent,
-       because [compose x x] is always equal to [x]. *)
-    map2 ~cache ~symmetric:false ~idempotent:true ~empty_neutral:true
-      decide_compose DepsOrUnassigned.compose
-
-  let compose m1 m2 = match m1, m2 with
-    | Top, _ | _, Top -> Top
-    | Map m1, Map m2 -> Map (compose_map m1 m2)
-    | Bottom, (Map _ | Bottom) | Map _, Bottom -> Bottom
-
-  (** Auxiliary function that substitutes the data right-hand part of a
-      dependency by a pre-existing From state. The returned result is a Deps.t:
-      the data part will be the data part of the complete result, the indirect
-      part will be added to the indirect part of the final result. *)
-  (* This function iterates simultaneously on a From memory, and on a zone.
-     It is cached. The definitions below are used to call the function that
-     does the recursive descent. *)
-  let substitute_data_deps =
-    (* Nothing left to substitute, return z unchanged *)
-    let empty_right z = Deps.from_data_deps z in
-    (* Zone to substitute is empty *)
-    let empty_left _ = Deps.bottom in
-    (* [b] is in the zone and substituted. Rewrite appropriately *)
-    let both b itvs offsm =
-      let fp = find_precise_offsetmap itvs offsm in
-      convert_find_offsm b fp
-    in
-    let join = Deps.join in
-    let empty = Deps.bottom in
-    let cache = Hptmap_sig.PersistentCache "From_compute.subst_data" in
-    let f_map =
-      Zone.fold2_join_heterogeneous
-        ~cache ~empty_left ~empty_right ~both ~join ~empty
-    in
-    fun call_site_froms z ->
-      match call_site_froms with
-      | Bottom -> Deps.bottom
-      | Top -> Deps.top
-      | Map m ->
-        try f_map z (shape m)
-        with Abstract_interp.Error_Top -> Deps.top
-
-  (** Auxiliary function that substitutes the indirect right-hand part of a
-      dependency by a pre-existing From state. The returned result is a zone,
-      which will be added to the indirect part of the final result. *)
-  let substitute_indirect_deps =
-    (* Nothing left to substitute, z is directly an indirect dependency *)
-    let empty_right z = z in
-    (* Zone to substitute is empty *)
-    let empty_left _ = Zone.bottom in
-    let both b itvs offsm =
-      (* Both the found data and indirect dependencies are computed for indirect
-         dependencies: merge to a single zone *)
-      let fp = find_precise_offsetmap itvs offsm in
-      Deps.to_zone (convert_find_offsm b fp)
-    in
-    let join = Zone.join in
-    let empty = Zone.bottom in
-    let cache = Hptmap_sig.PersistentCache "From_compute.subst_indirect" in
-    let f_map =
-      Zone.fold2_join_heterogeneous
-        ~cache ~empty_left ~empty_right ~both ~join ~empty
-    in
-    fun call_site_froms z ->
-      match call_site_froms with
-      | Bottom -> Zone.bottom
-      | Top -> Zone.top
-      | Map m ->
-        try f_map z (shape m)
-        with Abstract_interp.Error_Top -> Zone.top
-
-  let substitute call_site_froms deps =
-    let open Deps in
-    let { data; indirect } = deps in
-    (* depending directly on an indirect dependency -> indirect,
-       depending indirectly on a direct dependency  -> indirect *)
-    let dirdeps = substitute_data_deps call_site_froms data in
-    let inddeps = substitute_indirect_deps call_site_froms indirect in
-    let dir = dirdeps.data in
-    let ind = Zone.(join dirdeps.indirect inddeps) in
-    { data = dir; indirect = ind }
-
-
-  type return = Deps.t
-
-  let default_return = Deps.bottom
-
-  let top_return = Deps.top
-
-  let add_to_return ?start:(_start=0) ~size:_size ?(m=default_return) v =
-    Deps.join m v
-(*
-    let start = Ival.of_int start in
-    let itvs = Int_Intervals.from_ival_size start size in
-    LOffset.add_iset ~exact:true itvs (DepsOrUnassigned.AssignedFrom v) m
-*)
-
-  let top_return_size size =
-    add_to_return ~size Deps.top
-
-  let join_return = Deps.join
-
-  let collapse_return x = x
-
-end
-
-type froms =
-  { deps_return : Memory.return;
-    deps_table : Memory.t }
-
-let top = {
-  deps_return = Memory.top_return;
-  deps_table = Memory.top;
-}
-
-let join x y =
-  { deps_return = Memory.join_return 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
-
-let inputs ?(include_self=false) t =
-  let aux b offm acc =
-    Memory.LOffset.fold
-      (fun itvs deps acc ->
-         let z = DepsOrUnassigned.to_zone deps in
-         let self = DepsOrUnassigned.may_be_unassigned deps in
-         let acc = Zone.join z acc in
-         match include_self, self, b with
-         | true, true, Some b ->
-           Zone.join acc (Zone.inject b itvs)
-         | _ -> acc
-      )
-      offm
-      acc
-  in
-  let return = Deps.to_zone t.deps_return in
-  let aux_table b = aux (Some b) in
-  match t.deps_table with
-  | Memory.Top -> Zone.top
-  | Memory.Bottom -> Zone.bottom
-  | Memory.Map m -> Memory.fold_base aux_table m 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
-
-(** same as pretty, but uses the type of the function to output more
-    precise information.
-    @raise Error if the given type is not a function type
-*)
-let pretty_with_type ~indirect typ fmt { deps_return = r; deps_table = t } =
-  let (rt_typ,_,_,_) = Cil.splitFunctionType typ in
-  if Memory.is_bottom t
-  then Format.fprintf fmt
-      "@[NON TERMINATING - NO EFFECTS@]"
-  else
-    let map_pretty =
-      if indirect
-      then Memory.pretty_ind_data
-      else Memory.pretty
-    in
-    if Cil.isVoidType rt_typ
-    then begin
-      if Memory.is_empty t
-      then Format.fprintf fmt "@[NO EFFECTS@]"
-      else map_pretty fmt t
-    end
-    else
-      let pp_space fmt =
-        if not (Memory.is_empty t) then
-          Format.fprintf fmt "@ "
-      in
-      Format.fprintf fmt "@[<v>%a%t@[\\result FROM @[%a@]@]@]"
-        map_pretty t pp_space
-        (if indirect then Deps.pretty_precise else Deps.pretty) r
-
-let pretty_with_type_indirect = pretty_with_type ~indirect:true
-let pretty_with_type = pretty_with_type ~indirect:false
-
-let hash { deps_return = dr ; deps_table = dt } =
-  Memory.hash dt + 197 * Deps.hash dr
-
-let equal
-    { deps_return = dr ; deps_table = dt }
-    { deps_return = dr' ; deps_table = dt' } =
-  Memory.equal dt dt'&& Deps.equal dr dr'
-
-include Datatype.Make
-    (struct
-      type t = froms
-      let reprs =
-        List.fold_left
-          (fun acc o ->
-             List.fold_left
-               (fun acc m -> { deps_return = o; deps_table = m } :: acc)
-               acc
-               Memory.reprs)
-          []
-          Deps.reprs
-      let structural_descr =
-        Structural_descr.t_record
-          [| Deps.packed_descr;
-             Memory.packed_descr |]
-      let name = "Function_Froms"
-      let hash = hash
-      let compare = Datatype.undefined
-      let equal = equal
-      let pretty = pretty
-      let rehash = Datatype.identity
-      let copy = Datatype.undefined
-      let mem_project = Datatype.never_any_project
-    end)
-
-(*
-Local Variables:
-compile-command: "make -C ../../.."
-End:
-*)
diff --git a/src/kernel_services/abstract_interp/function_Froms.mli b/src/kernel_services/abstract_interp/function_Froms.mli
deleted file mode 100644
index 141bfc5f9a5266c16d87476242410a5f7f679f83..0000000000000000000000000000000000000000
--- a/src/kernel_services/abstract_interp/function_Froms.mli
+++ /dev/null
@@ -1,186 +0,0 @@
-(**************************************************************************)
-(*                                                                        *)
-(*  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).            *)
-(*                                                                        *)
-(**************************************************************************)
-
-(** Datastructures and common operations for the results of the From plugin. *)
-
-module Deps : sig
-
-  type deps = {
-    data: Locations.Zone.t;
-    indirect: Locations.Zone.t;
-  }
-
-  val bottom: deps
-  val top: deps
-  val is_included: deps -> deps -> bool
-  val join: deps -> deps -> deps
-  val narrow: deps -> deps -> deps
-  val to_zone: deps -> Locations.Zone.t
-
-  val add_data_dep: deps -> Locations.Zone.t -> deps
-  val add_indirect_dep: deps -> Locations.Zone.t -> deps
-
-
-  val from_data_deps: Locations.Zone.t -> deps
-  val from_indirect_deps: Locations.Zone.t -> deps
-
-  val map: (Locations.Zone.t -> Locations.Zone.t) -> deps -> deps
-
-  include Datatype.S with type t = deps
-
-  val pretty_precise : Format.formatter -> t -> unit
-end
-
-
-module DepsOrUnassigned : sig
-
-  type deps_or_unassigned =
-    | DepsBottom (** Bottom of the lattice, never bound inside a memory state
-                     at a valid location. (May appear for bases for which the
-                     validity does not start at 0, currently only NULL.) *)
-    | Unassigned (** Location has never been assigned *)
-    | AssignedFrom of Deps.t (** Location guaranteed to have been overwritten,
-                                 its contents depend on the [Deps.t] value *)
-    | MaybeAssignedFrom of Deps.t  (** Location may or may not have been
-                                       overwritten *)
-  (** The lattice is [DepsBottom <= Unassigned], [DepsBottom <= AssignedFrom z],
-      [Unassigned <= MaybeAssignedFrom] and
-      [AssignedFrom z <= MaybeAssignedFrom z]. *)
-
-  include Lmap_bitwise.With_default with type t = deps_or_unassigned
-
-  val subst: (Deps.t -> Deps.t) -> t -> t
-
-  val extract_data: t -> Locations.Zone.t
-  val extract_indirect: t -> Locations.Zone.t
-
-  val may_be_unassigned: t -> bool
-
-  val compose: t -> t -> t
-  (** [compose d1 d2] is the sequential composition of [d1] after [d2], ie.
-      the dependencies needed to execute [d1] after having executed [d2].
-      It is computed as [d1] if [d1 = AssignedFrom _] (as executing [d1]
-      completely overwrites what [d2] wrote), and as a partial join between
-      [d1] and [d2] in the other cases. *)
-
-  val pretty_precise : Format.formatter -> t -> unit
-
-  val to_zone: t -> Locations.Zone.t
-  val to_deps: t -> Deps.deps
-end
-
-module Memory : sig
-  include Lmap_bitwise.Location_map_bitwise with type v = DepsOrUnassigned.t
-
-  (** Prints the detail of address and data dependencies, as opposed to [pretty]
-      that prints the backwards-compatible union of them *)
-  val pretty_ind_data : Format.formatter -> t -> unit
-
-  val find: t -> Locations.Zone.t -> Locations.Zone.t
-  (** Imprecise version of find, in which data and indirect dependencies are
-      not distinguished *)
-
-  val find_precise: t -> Locations.Zone.t -> Deps.t
-  (** Precise version of find *)
-
-  val add_binding: exact:bool -> t -> Locations.Zone.t -> Deps.t -> t
-  val add_binding_loc: exact:bool -> t -> Locations.location -> Deps.t -> t
-  val add_binding_precise_loc:
-    exact:bool -> Locations.access -> t ->
-    Precise_locs.precise_location -> Deps.t -> t
-  val bind_var: Cil_types.varinfo -> Deps.t -> t -> t
-  val unbind_var: Cil_types.varinfo -> t -> t
-
-  val map: (DepsOrUnassigned.t -> DepsOrUnassigned.t) -> t -> t
-
-  val compose: t -> t -> t
-  (** Sequential composition. See {!DepsOrUnassigned.compose}. *)
-
-  val substitute: t -> Deps.t -> Deps.t
-  (** [substitute m d] applies [m] to [d] so that any dependency in [d] is
-      expressed using the dependencies already present in [m]. For example,
-      [substitute 'x From y' 'x'] returns ['y']. *)
-
-
-  (** Dependencies for [\result]. *)
-
-  type return = Deps.t
-  (* Currently, this type is equal to [Deps.t]. However, some of the functions
-     below are more precise, and will be more useful when 'return' are
-     represented by a precise offsetmap. *)
-
-  (** Default value to use for storing the dependencies of [\result] *)
-  val default_return: return
-
-  (** Completely imprecise return *)
-  val top_return: return
-
-  (** Completely imprecise return of the given size *)
-  val top_return_size: Int_Base.t -> return
-
-  (** Add some dependencies to [\result], between bits [start] and
-      [start+size-1], to the [Deps.t] value; default value for [start] is 0.
-      If [m] is specified, the dependencies are added to it. Otherwise,
-      {!default_return} is used. *)
-  val add_to_return:
-    ?start:int -> size:Int_Base.t -> ?m:return -> Deps.t -> return
-
-  val collapse_return: return -> Deps.t
-end
-
-
-
-type froms = {
-  deps_return : Memory.return
-(** Dependencies for the returned value *);
-  deps_table : Memory.t
-(** Dependencies on all the zones modified by the function *);
-}
-
-include Datatype.S with type t = froms
-
-val join: froms -> froms -> froms
-
-val top: froms
-
-(** Display dependencies of a function, using the function's type to improve
-    readability *)
-val pretty_with_type: Cil_types.typ -> froms Pretty_utils.formatter
-
-(** Display dependencies of a function, using the function's type to improve
-    readability, separating direct and indirect dependencies *)
-val pretty_with_type_indirect: Cil_types.typ -> froms Pretty_utils.formatter
-
-(** Extract the left part of a from result, ie. the zones that are written *)
-val outputs: froms -> Locations.Zone.t
-
-(** Extract the right part of a from result, ie. the zones on which the
-    written zones depend. If [include_self] is true, and the from is
-    of the form [x FROM y (and SELF)], [x] is added to the result;
-    default value is [false]. *)
-val inputs: ?include_self:bool -> froms -> Locations.Zone.t
-
-(*
-Local Variables:
-compile-command: "make -C ../../.."
-End:
-*)
diff --git a/src/plugins/eva/Eva.mli b/src/plugins/eva/Eva.mli
index 7ee21c551d305b60cadca7ec6460b7d37f67b24b..1a8fe1b2101e763421ef6889a479e982fe6eb818 100644
--- a/src/plugins/eva/Eva.mli
+++ b/src/plugins/eva/Eva.mli
@@ -178,6 +178,48 @@ module Callstack: sig
 
 end
 
+module Deps: sig
+
+  (** Memory dependencies of an expression. *)
+  type t = {
+    data: Locations.Zone.t;
+    (** Memory zone directly required to evaluate the given expression. *)
+    indirect: Locations.Zone.t;
+    (** Memory zone read to compute data addresses. *)
+  }
+
+  include Datatype.S with type t := t
+
+  val pretty_precise: Format.formatter -> t -> unit
+  val pretty_debug: Format.formatter -> t -> unit
+
+  (* Constructors *)
+
+  val top : t
+  val bottom : t
+  val data : Locations.Zone.t -> t
+  val indirect : Locations.Zone.t -> t
+
+  (* Conversion *)
+
+  val to_zone : t -> Locations.Zone.t
+
+  (* Mutators *)
+
+  val add_data : t -> Locations.Zone.t -> t
+  val add_indirect : t -> Locations.Zone.t -> t
+
+  (* Map *)
+
+  val map : (Locations.Zone.t -> Locations.Zone.t) -> t -> t
+
+  (* Lattice operators *)
+
+  val is_included : t -> t -> bool
+  val join : t -> t -> t
+  val narrow : t -> t -> t
+end
+
 module Results: sig
 
   (** Eva's result API is a new interface to access the results of an analysis,
@@ -335,14 +377,6 @@ module Results: sig
       evaluate the given lvalue, excluding the lvalue zone itself. *)
   val address_deps : Cil_types.lval -> request -> Locations.Zone.t
 
-  (** Memory dependencies of an expression. *)
-  type deps = Function_Froms.Deps.deps = {
-    data: Locations.Zone.t;
-    (** Memory zone directly required to evaluate the given expression. *)
-    indirect: Locations.Zone.t;
-    (** Memory zone read to compute data addresses. *)
-  }
-
   (** Taint of a memory zone, according to the taint domain. *)
   type taint =
     | Direct
@@ -361,7 +395,7 @@ module Results: sig
 
   (** Computes (an overapproximation of) the memory dependencies of an
       expression. *)
-  val expr_dependencies : Cil_types.exp -> request -> deps
+  val expr_dependencies : Cil_types.exp -> request -> Deps.t
 
   (** Evaluation *)
 
@@ -600,6 +634,63 @@ module Eval: sig
                          callers, can be cached. *)
 end
 
+module Assigns: sig
+
+  module DepsOrUnassigned : sig
+
+    type t =
+      | DepsBottom (** Bottom of the lattice, never bound inside a memory state
+                       at a valid location. (May appear for bases for which the
+                       validity does not start at 0, currently only NULL.) *)
+      | Unassigned (** Location has never been assigned *)
+      | AssignedFrom of Deps.t (** Location guaranteed to have been overwritten,
+                                   its contents depend on the [Deps.t] value *)
+      | MaybeAssignedFrom of Deps.t  (** Location may or may not have been
+                                         overwritten *)
+
+    (** The lattice is [DepsBottom <= Unassigned], [DepsBottom <= AssignedFrom z],
+        [Unassigned <= MaybeAssignedFrom] and
+        [AssignedFrom z <= MaybeAssignedFrom z]. *)
+
+    val top : t
+    val equal : t -> t -> bool
+    val may_be_unassigned : t -> bool
+    val to_zone : t -> Locations.Zone.t
+  end
+
+  module Memory : sig
+    include Lmap_bitwise.Location_map_bitwise with type v = DepsOrUnassigned.t
+
+    val find : t -> Locations.Zone.t -> Locations.Zone.t
+    (** Imprecise version of find, in which data and indirect dependencies are
+        not distinguished *)
+
+    val find_precise : t -> Locations.Zone.t -> Deps.t
+    (** Precise version of find *)
+
+    val find_precise_loffset : LOffset.t -> Base.t -> Int_Intervals.t -> Deps.t
+
+    val add_binding : exact:bool -> t -> Locations.Zone.t -> Deps.t -> t
+    val add_binding_loc : exact:bool -> t -> Locations.location -> Deps.t -> t
+    val add_binding_precise_loc :
+      exact:bool -> Locations.access -> t ->
+      Precise_locs.precise_location -> Deps.t -> t
+  end
+
+  type t = {
+    return : Deps.t
+  (** Dependencies for the returned value *);
+    memory : Memory.t
+  (** Dependencies on all the zones modified by the function *);
+  }
+
+  include Datatype.S with type t := t
+
+  val top : t
+  val join : t -> t -> t
+
+end
+
 module Builtins: sig
 
   (** Eva analysis builtins for the cvalue domain, more efficient than their
@@ -624,9 +715,11 @@ module Builtins: sig
     c_clobbered: Base.SetLattice.t;
     (** An over-approximation of the bases in which addresses of local variables
         might have been written *)
-    c_from: (Function_Froms.froms * Locations.Zone.t) option;
-    (** If not None, the froms of the function, and its sure outputs;
-        i.e. the dependencies of the result and of each zone written to. *)
+    c_assigns: (Assigns.t * Locations.Zone.t) option;
+    (** If not None:
+        - the assigns of the function, i.e. the dependencies of the result
+          and of each zone written to.
+        - and its sure outputs, i.e. an under-approximation of written zones. *)
   }
 
   (** The result of a builtin can be given in different forms. *)
@@ -672,10 +765,11 @@ module Cvalue_callbacks: sig
 
   type state = Cvalue.Model.t
 
-  (** If not None, the froms of the function, and its sure outputs;
-      i.e. the dependencies of the result, and the dependencies
-      of each zone written to. *)
-  type call_froms = (Function_Froms.froms * Locations.Zone.t) option
+  (** If not None:
+      - the assigns of the function, i.e. the dependencies of the result
+        and the dependencies of each zone written to;
+      - and its sure outputs, i.e. an under-approximation of written zones. *)
+  type call_assigns = (Assigns.t * Locations.Zone.t) option
 
   type analysis_kind =
     [ `Builtin (** A cvalue builtin is used to interpret the function. *)
@@ -700,7 +794,7 @@ module Cvalue_callbacks: sig
 
   (** Results of a function call. *)
   type call_results =
-    [ `Builtin of state list * call_froms
+    [ `Builtin of state list * call_assigns
     (** List of cvalue states at the end of the builtin. *)
     | `Spec of state list
     (** List of cvalue states at the end of the call. *)
@@ -767,10 +861,10 @@ module Logic_inout: sig
     Cvalue.Model.t -> Locations.access -> Cil_types.term -> tlval_zones option
 
   (** Evaluate the assigns clauses of the given function in its given pre-state,
-      and compare them with the given froms (computed by the from plugin).
+      and compare them with the dependencies computed by the from plugin.
       Emits warnings if needed, and sets statuses to the assigns clauses. *)
   val verify_assigns:
-    Cil_types.kernel_function -> pre:Cvalue.Model.t -> Function_Froms.froms -> unit
+    Cil_types.kernel_function -> pre:Cvalue.Model.t -> Assigns.t -> unit
 
 
   (** [accept_base ~formals ~locals kf b] returns [true] if and only if [b] is:
diff --git a/src/plugins/eva/api/general_requests.ml b/src/plugins/eva/api/general_requests.ml
index 3015cbcf1564ee2332cac2973c08abf0289588dd..3666a0c4345da0dbe0554481a50459fd1dd98453 100644
--- a/src/plugins/eva/api/general_requests.ml
+++ b/src/plugins/eva/api/general_requests.ml
@@ -327,7 +327,7 @@ module EvaTaints = struct
 
   let evaluate expr request =
     let (let+) = Option.bind in
-    let { data ; indirect } = expr_dependencies expr request in
+    let Deps.{ data ; indirect } = expr_dependencies expr request in
     let+ data = is_tainted data request |> Result.to_option in
     let+ indirect = is_tainted indirect request |> Result.to_option in
     Some (data, indirect)
diff --git a/src/plugins/eva/domains/cvalue/builtins.ml b/src/plugins/eva/domains/cvalue/builtins.ml
index b128b35eed8387cf041c595f1438392a8f0bc3e9..493a62a052b953e553871f1d6d1dbdf50ca7bd05 100644
--- a/src/plugins/eva/domains/cvalue/builtins.ml
+++ b/src/plugins/eva/domains/cvalue/builtins.ml
@@ -31,7 +31,7 @@ type cacheable = Eval.cacheable = Cacheable | NoCache | NoCacheCallers
 type full_result = {
   c_values: (Cvalue.V.t option * Cvalue.Model.t) list;
   c_clobbered: Base.SetLattice.t;
-  c_from: (Function_Froms.froms * Locations.Zone.t) option;
+  c_assigns: (Assigns.t * Locations.Zone.t) option;
 }
 
 type call_result =
@@ -276,7 +276,7 @@ let apply_builtin (builtin:builtin) call ~pre ~post =
     let states = process_result call post call_result in
     let froms =
       match call_result with
-      | Full result -> result.c_from
+      | Full result -> result.c_assigns
       | States _ | Result _ -> None
     in
     let result = `Builtin (List.map fst states, froms) in
diff --git a/src/plugins/eva/domains/cvalue/builtins.mli b/src/plugins/eva/domains/cvalue/builtins.mli
index 1d25c32aff6cc28b5391b25368b0c3a52dce20dc..bf85aba9659e2bd3cac76f04b6af8814f283dac0 100644
--- a/src/plugins/eva/domains/cvalue/builtins.mli
+++ b/src/plugins/eva/domains/cvalue/builtins.mli
@@ -44,9 +44,11 @@ type full_result = {
   c_clobbered: Base.SetLattice.t;
   (** An over-approximation of the bases in which addresses of local variables
       might have been written *)
-  c_from: (Function_Froms.froms * Locations.Zone.t) option;
-  (** If not None, the froms of the function, and its sure outputs;
-      i.e. the dependencies of the result and of each zone written to. *)
+  c_assigns: (Assigns.t * Locations.Zone.t) option;
+  (** If not None:
+      - the assigns of the function, i.e. the dependencies of the result
+        and of each zone written to.
+      - and its sure outputs, i.e. an under-approximation of written zones. *)
 }
 
 (** The result of a builtin can be given in different forms. *)
diff --git a/src/plugins/eva/domains/cvalue/builtins_malloc.ml b/src/plugins/eva/domains/cvalue/builtins_malloc.ml
index 77b26cbe8cc5bcb6e6cd6812c283f612ecb56eed..14364066d2fa108a24be87f005c2e0a6bce40756 100644
--- a/src/plugins/eva/domains/cvalue/builtins_malloc.ml
+++ b/src/plugins/eva/domains/cvalue/builtins_malloc.ml
@@ -489,7 +489,7 @@ let register_malloc ?replace name ?returns_null prefix region =
     let ret = V.inject new_base Ival.zero in
     let c_values = wrap_fallible_alloc ?returns_null ret state new_state in
     let c_clobbered = Base.SetLattice.bottom in
-    Builtins.Full { c_values; c_clobbered; c_from = None; }
+    Builtins.Full { c_values; c_clobbered; c_assigns = None; }
   in
   let name = "Frama_C_" ^ name in
   let typ () = Cil.voidPtrType, [Cil.theMachine.Cil.typeOfSizeOf] in
@@ -541,7 +541,7 @@ let calloc_builtin state args =
       wrap_fallible_alloc ?returns_null ret state new_state
   in
   let c_clobbered = Base.SetLattice.bottom in
-  Builtins.Full { c_values; c_clobbered; c_from = None; }
+  Builtins.Full { c_values; c_clobbered; c_assigns = None; }
 
 let () =
   let name = "Frama_C_calloc" in
@@ -579,9 +579,8 @@ let free ~exact bases state =
     Locals_scoping.make_escaping ~exact ~escaping ~on_escaping ~within state
   in
   let from_changed =
-    let open Function_Froms in
-    let m = Memory.(add_binding ~exact empty !changed Deps.bottom) in
-    { deps_table = m; deps_return = Deps.bottom }
+    let m = Assigns.Memory.(add_binding ~exact empty !changed Deps.bottom) in
+    Assigns.{ memory = m; return = Deps.bottom }
   in
   state, (from_changed, if exact then !changed else Zone.bottom)
 
@@ -646,12 +645,12 @@ let frama_c_free state actuals =
     let bases_to_remove, card_to_remove, _null = resolve_bases_to_free arg in
     let c_clobbered = Base.SetLattice.bottom in
     if card_to_remove = 0 then
-      Builtins.Full { c_values = []; c_clobbered; c_from = None; }
+      Builtins.Full { c_values = []; c_clobbered; c_assigns = None; }
     else
       let strong = card_to_remove <= 1 in
       let state, changed = free_aux state ~strong bases_to_remove in
       let c_values = [None, state] in
-      Builtins.Full { c_values; c_clobbered; c_from = Some changed; }
+      Builtins.Full { c_values; c_clobbered; c_assigns = Some changed; }
   | _ -> raise (Builtins.Invalid_nb_of_args 1)
 
 let () =
@@ -667,7 +666,7 @@ let frama_c_vla_free state actuals =
     let state, changed = free_aux state ~strong:true bases_to_remove in
     let c_values = [None, state] in
     let c_clobbered = Base.SetLattice.bottom in
-    Builtins.Full { c_values; c_clobbered; c_from = Some changed; }
+    Builtins.Full { c_values; c_clobbered; c_assigns = Some changed; }
   | _ -> raise (Builtins.Invalid_nb_of_args 1)
 
 let () =
@@ -824,10 +823,10 @@ let realloc_builtin_aux state ptr size =
     let new_state, changed = free_aux new_state ~strong bases in
     let c_values = wrap_fallible_alloc ret state new_state in
     let c_clobbered = Builtins.clobbered_set_from_ret new_state ret in
-    Builtins.Full { c_values; c_clobbered; c_from = Some changed; }
+    Builtins.Full { c_values; c_clobbered; c_assigns = Some changed; }
   else (* Invalid call. *)
     let c_clobbered = Base.SetLattice.bottom in
-    Builtins.Full { c_values = []; c_clobbered; c_from = None; }
+    Builtins.Full { c_values = []; c_clobbered; c_assigns = None; }
 
 let realloc_builtin state args =
   let ptr, size =
@@ -915,7 +914,7 @@ let check_leaked_malloced_bases state _ =
           Base.pretty base)
     alloced_bases;
   let c_clobbered = Base.SetLattice.bottom in
-  Builtins.Full { c_values = [None,state]; c_clobbered; c_from = None; }
+  Builtins.Full { c_values = [None,state]; c_clobbered; c_assigns = None; }
 
 let () =
   Builtins.register_builtin "Frama_C_check_leak" NoCacheCallers
diff --git a/src/plugins/eva/domains/cvalue/builtins_memory.ml b/src/plugins/eva/domains/cvalue/builtins_memory.ml
index 073fc79acf09987e1fd82a7623e58bd1257f3e97..f466617f59030c0b0438385e1b94fa5fb7784450 100644
--- a/src/plugins/eva/domains/cvalue/builtins_memory.ml
+++ b/src/plugins/eva/domains/cvalue/builtins_memory.ml
@@ -74,7 +74,7 @@ let frama_c_offset _state = function
 
 let () = register_builtin "Frama_C_offset" frama_c_offset
 
-exception Memcpy_result of (Cvalue.Model.t * Function_Froms.froms * Zone.t)
+exception Memcpy_result of (Cvalue.Model.t * Assigns.t * Zone.t)
 
 exception Indeterminate of V_Or_Uninitialized.t
 
@@ -97,11 +97,10 @@ let memcpy_check_indeterminate_offsetmap offsm =
 (* Create a dependency [\from arg_n] where n is the nth argument of the
    currently called function. *)
 let deps_nth_arg n =
-  let open Function_Froms in
   let kf = Callstack.top_kf (Eva_utils.current_call_stack ()) in
   try
     let vi = List.nth (Kernel_function.get_formals kf) n in
-    Deps.add_data_dep Deps.bottom (Locations.zone_of_varinfo vi)
+    Deps.add_data Deps.bottom (Locations.zone_of_varinfo vi)
   with Failure _ -> Kernel.fatal "%d arguments expected" n
 
 
@@ -126,7 +125,7 @@ let frama_c_memcpy name state actuals =
     in
     let deps_return = deps_nth_arg 0 in
     let empty_cfrom =
-      Function_Froms.({ deps_table = Memory.empty; deps_return })
+      Assigns.{ memory = Memory.empty; return = deps_return }
     in
     let precise_copy state =
       (* First step: copy the bytes we are sure to copy *)
@@ -148,26 +147,25 @@ let frama_c_memcpy name state actuals =
           let (deps_table, sure_zone) =
             let zone_dst = enumerate_valid_bits Locations.Write  loc_dst in
             let zone_src = enumerate_valid_bits Locations.Read loc_src in
-            let deps =
-              Function_Froms.(Deps.add_data_dep Deps.bottom zone_src)
-            in
+            let deps = Deps.data zone_src in
             (* Note: actually a part may be written for sure (if the
                difference between the offsets in loc_dst is smaller
                than size), but keeping it imprecise reflects more the
                imprecision of the value analysis here. *)
             let exact = Location_Bits.cardinal_zero_or_one dst_bits in
             let deps_table =
-              Function_Froms.Memory.add_binding ~exact
-                Function_Froms.Memory.empty zone_dst deps in
+              Assigns.Memory.add_binding ~exact
+                Assigns.Memory.empty zone_dst deps
+            in
             let sure_zone = if exact then zone_dst else Zone.bottom in
             (deps_table, sure_zone)
           in
           new_state, deps_table, sure_zone
       end
       else (* Nothing certain can be copied *)
-        (state, Function_Froms.Memory.empty, Zone.bottom)
+        (state, Assigns.Memory.empty, Zone.bottom)
     in
-    let imprecise_copy new_state precise_deps_table sure_zone =
+    let imprecise_copy new_state precise_assigns sure_zone =
       (* Second step. Size is imprecise, we will now copy some bits
          that we are not sure to copy *)
       let size_min_ival = Ival.inject_singleton size_min in
@@ -192,14 +190,13 @@ let frama_c_memcpy name state actuals =
       let loc_src = make_loc (Location_Bits.shift range src) size_char in
       let loc_dst = make_loc (Location_Bits.shift range dst) size_char in
       let c_from =
-        let open Function_Froms in
         let zone_src = enumerate_valid_bits Locations.Read loc_src in
         let zone_dst = enumerate_valid_bits Locations.Write  loc_dst in
-        let deps = Deps.add_data_dep Deps.bottom zone_src in
-        let deps_table =
-          Memory.add_binding ~exact:false precise_deps_table zone_dst deps
+        let deps = Deps.data zone_src in
+        let memory =
+          Assigns.Memory.add_binding ~exact:false precise_assigns zone_dst deps
         in
-        { deps_table; deps_return }
+        Assigns.{ memory; return = deps_return }
       in
       try
         (* We try to iter on all the slices inside the value of slice.
@@ -274,26 +271,29 @@ let frama_c_memcpy name state actuals =
     try
       if Ival.is_zero size then
         raise (Memcpy_result (state, empty_cfrom, Zone.bottom));
-      let (precise_state,precise_deps_table,sure_zone) = precise_copy state in
+      let (precise_state,precise_assigns,sure_zone) = precise_copy state in
       if Option.fold ~none:false ~some:(Int.equal min) max then
-        (let open Function_Froms in
-         let c_from = { deps_table = precise_deps_table; deps_return } in
-         raise (Memcpy_result (precise_state, c_from, sure_zone)));
-      imprecise_copy precise_state precise_deps_table sure_zone
+        begin
+          let c_assigns =
+            Assigns.{ memory = precise_assigns; return = deps_return }
+          in
+          raise (Memcpy_result (precise_state, c_assigns, sure_zone))
+        end;
+      imprecise_copy precise_state precise_assigns sure_zone
     with
-    | Memcpy_result (new_state,c_from,sure_zone) ->
+    | Memcpy_result (new_state,c_assigns,sure_zone) ->
       if Model.is_reachable new_state then
         (* Copy at least partially succeeded (with perhaps an
            alarm for some of the sizes *)
         Builtins.Full
           { Builtins.c_values = [Some dst_bytes, new_state];
             c_clobbered = Builtins.clobbered_set_from_ret new_state dst_bytes;
-            c_from = Some (c_from,  sure_zone); }
+            c_assigns = Some (c_assigns,  sure_zone); }
       else
         Builtins.Full
           { Builtins.c_values = [ None, Cvalue.Model.bottom];
             c_clobbered = Base.SetLattice.bottom;
-            c_from = Some (c_from,  sure_zone); }
+            c_assigns = Some (c_assigns,  sure_zone); }
   in
   match actuals with
   | [dst; src; size] -> compute dst src size
@@ -362,22 +362,22 @@ let frama_c_memset_imprecise state dst_lval dst v size =
         (new_state,Zone.bottom)
     with Not_found -> (new_state,Zone.bottom) (* from find_lonely_key + explicit raise *)
   in
-  let c_from =
-    let open Function_Froms in
+  let c_assigns =
     let value_dep = deps_nth_arg 1 in
-    let deps_table =
-      Memory.add_binding ~exact:false Memory.empty over_zone value_dep
+    let memory = Assigns.Memory.empty in
+    let memory =
+      Assigns.Memory.add_binding ~exact:false memory over_zone value_dep
     in
-    let deps_table =
-      Memory.add_binding ~exact:true deps_table sure_zone value_dep
+    let memory =
+      Assigns.Memory.add_binding ~exact:true memory sure_zone value_dep
     in
     let deps_return = deps_nth_arg 0 in
-    { deps_table; deps_return }
+    Assigns.{ memory; return = deps_return }
   in
   Builtins.Full
     { Builtins.c_values = [Some dst, new_state'];
       c_clobbered = Base.SetLattice.bottom;
-      c_from = Some (c_from,sure_zone); }
+      c_assigns = Some (c_assigns,sure_zone); }
 (* let () = register_builtin "Frama_C_memset" frama_c_memset_imprecise *)
 
 (* Type that describes why the 'precise memset' builtin may fail. *)
@@ -575,15 +575,15 @@ let frama_c_memset_precise state dst_lval dst v (exp_size, size) =
     let dst_loc = Locations.loc_bytes_to_loc_bits dst in
     let (c_from,dst_zone) =
       let input = deps_nth_arg 1 in
-      let open Function_Froms in
       let size_bits = Integer.mul size (Bit_utils.sizeofchar ())in
       let dst_location = Locations.make_loc dst_loc (Int_Base.Value size_bits) in
       let dst_zone = Locations.(enumerate_valid_bits Write dst_location) in
-      let deps_table =
-        Function_Froms.Memory.add_binding ~exact:true
-          Function_Froms.Memory.empty dst_zone input in
-      let deps_return = deps_nth_arg 0 in
-      let c_from = { deps_table; deps_return  } in
+      let memory =
+        Assigns.Memory.add_binding ~exact:true
+          Assigns.Memory.empty dst_zone input
+      in
+      let return = deps_nth_arg 0 in
+      let c_from = Assigns.{ memory; return  } in
       c_from,dst_zone
     in
     let _ = c_from in
@@ -596,7 +596,7 @@ let frama_c_memset_precise state dst_lval dst v (exp_size, size) =
     Builtins.Full
       { Builtins.c_values = [Some dst, state'];
         c_clobbered = Base.SetLattice.bottom;
-        c_from = Some (c_from,dst_zone); }
+        c_assigns = Some (c_from,dst_zone); }
   with
   | Bit_utils.NoMatchingOffset -> raise (ImpreciseMemset SizeMismatch)
   | Base.Not_a_C_variable -> raise (ImpreciseMemset NoTypeForDest)
diff --git a/src/plugins/eva/domains/octagons.ml b/src/plugins/eva/domains/octagons.ml
index 6ba460b8da9fe1f45b14226d6403a4f8fad23116..2cbaa286e1443f43629111580cac105071bd3683 100644
--- a/src/plugins/eva/domains/octagons.ml
+++ b/src/plugins/eva/domains/octagons.ml
@@ -56,7 +56,7 @@ let typ_kind typ =
   | TFloat _ -> Float
   | _ -> assert false
 
-type dependencies = Eva_utils.deps = {
+type dependencies = Deps.t = {
   data: Locations.Zone.t;
   indirect: Locations.Zone.t;
 }
@@ -836,12 +836,6 @@ end
 
 module VariableToDeps =
 struct
-  module Deps =
-  struct
-    include Function_Froms.Deps
-    let pretty_debug = pretty_precise
-  end
-
   let cache_prefix = "Eva.Octagons.VariableToDeps"
 
   include Hptmap.Make (Variable) (Deps) (Hptmap.Comp_unused)
@@ -1040,7 +1034,7 @@ module Deps = struct
       BaseToVariables.add_deps var deps i
     in
     match VariableToDeps.find_opt var m with
-    | Some previous_deps when Function_Froms.Deps.equal previous_deps deps ->
+    | Some previous_deps when Deps.equal previous_deps deps ->
       m, i
     | Some previous_deps when not (are_bases_increasing previous_deps deps) ->
       (* If [var] already exists in the state and had bigger dependencies (a
@@ -1053,7 +1047,7 @@ module Deps = struct
      contain variables that do not appear in [m]. *)
   let add var deps (m, i: t): t =
     match VariableToDeps.find_opt var m with
-    | Some d when Function_Froms.Deps.equal d deps -> m, i
+    | Some d when Deps.equal d deps -> m, i
     | _ ->
       VariableToDeps.add var deps m,
       BaseToVariables.add_deps var deps i
diff --git a/src/plugins/eva/dune b/src/plugins/eva/dune
index 66e75d5fb6c5738a5fac9570a71e8c8c1743dce1..ee102c636e8dba81c0523c333d5ca81ca3984577 100644
--- a/src/plugins/eva/dune
+++ b/src/plugins/eva/dune
@@ -112,8 +112,9 @@
  (mode (promote (only Eva.mli)))
  (deps
   gen-api.sh Eva.header
-  engine/analysis.mli types/callstack.mli utils/results.mli parameters.mli
-  utils/eva_annotations.mli eval.mli domains/cvalue/builtins.mli
-  utils/cvalue_callbacks.mli legacy/logic_inout.mli utils/eva_results.mli
+  engine/analysis.mli types/callstack.mli types/deps.mli utils/results.mli parameters.mli
+  utils/eva_annotations.mli eval.mli types/assigns.mli
+  domains/cvalue/builtins.mli utils/cvalue_callbacks.mli legacy/logic_inout.mli
+  utils/eva_results.mli
   utils/unit_tests.mli)
  (action (run bash %{deps})))
diff --git a/src/plugins/eva/engine/compute_functions.ml b/src/plugins/eva/engine/compute_functions.ml
index 88b6280b2e75a83b72bf2a90b23921754ba17269..90a70b081ad5d1f6cfe4b9528a9c50647ef4c3f7 100644
--- a/src/plugins/eva/engine/compute_functions.ml
+++ b/src/plugins/eva/engine/compute_functions.ml
@@ -35,7 +35,7 @@ let clear_caches () =
   Cvalue.Model.clear_caches ();
   Locations.Location_Bytes.clear_caches ();
   Locations.Zone.clear_caches ();
-  Function_Froms.Memory.clear_caches ()
+  Assigns.Memory.clear_caches ()
 
 let () = State.add_hook_on_update Self.state clear_caches
 
diff --git a/src/plugins/eva/gui/gui_eval.ml b/src/plugins/eva/gui/gui_eval.ml
index 3205c66f1626cdcf9cfbafd72be6a11af0af5b2c..1aec39065f79ef1fcdf4165fe164b86098694e2d 100644
--- a/src/plugins/eva/gui/gui_eval.ml
+++ b/src/plugins/eva/gui/gui_eval.ml
@@ -86,7 +86,7 @@ let clear_caches () =
   Cvalue.Model.clear_caches ();
   Locations.Location_Bytes.clear_caches ();
   Locations.Zone.clear_caches ();
-  Function_Froms.Memory.clear_caches ()
+  Assigns.Memory.clear_caches ()
 
 module type S = sig
   module Analysis : Analysis.S
diff --git a/src/plugins/eva/legacy/logic_inout.ml b/src/plugins/eva/legacy/logic_inout.ml
index 03c6ee235bad35404a29e23fe25237a7c671e1b3..7f6db6d4f75c48e434e26205b43ee000b084f9e7 100644
--- a/src/plugins/eva/legacy/logic_inout.ml
+++ b/src/plugins/eva/legacy/logic_inout.ml
@@ -147,14 +147,13 @@ let eval_assigns_from pre_state it =
 (** Compute the validity status for [from] in [pre_state], assuming the
     entire clause is [assigns asgn \from from]. The inferred dependencies
     are [found_froms], while [asgn] evaluates to [assigns_zone]. *)
-let check_from pre_state asgn assigns_zone from found_froms =
+let check_from pre_state asgn assigns_zone from found_assigns =
   let open Locations in
   let found_deps =
-    let open Function_Froms in
     if Logic_utils.is_result asgn.it_content then
-      found_froms.deps_return
+      found_assigns.Assigns.return
     else
-      Memory.find_precise found_froms.deps_table assigns_zone
+      Assigns.Memory.find_precise found_assigns.memory assigns_zone
   in
   let (indirect_deps,direct_deps) =
     let filter x = List.mem "indirect" x.it_content.term_name in
@@ -165,8 +164,8 @@ let check_from pre_state asgn assigns_zone from found_froms =
   let eval = eval_assigns_from pre_state in
   let stated_indirect_deps = link (List.map eval indirect_deps) in
   let stated_direct_deps = link (List.map eval direct_deps) in
-  let found_direct_deps = found_deps.Function_Froms.Deps.data in
-  let found_indirect_deps = found_deps.Function_Froms.Deps.indirect in
+  let found_direct_deps = found_deps.Deps.data in
+  let found_indirect_deps = found_deps.Deps.indirect in
   let res_for_unknown txt =
     Self.debug "found_direct deps %a stated_direct_deps %a \
                 found_indirect_deps %a stated_indirect_deps %a"
@@ -214,7 +213,7 @@ let check_fct_assigns kf ab ~pre_state found_froms =
   let behaviors = Annotations.behaviors kf in
   (* Under-approximation of the union. *)
   let link zones = List.fold_left Zone.link Zone.bottom zones in
-  let outputs = Function_Froms.outputs found_froms in
+  let outputs = Assigns.outputs found_froms in
   let check_for_behavior b =
     let activity = Active_behaviors.is_active ab b in
     match activity with
diff --git a/src/plugins/eva/legacy/logic_inout.mli b/src/plugins/eva/legacy/logic_inout.mli
index ddec2c39239451d647383b453d55c3800a534a7c..f5f7aaf82950babc4a850f8e10ab2d7b3d177748 100644
--- a/src/plugins/eva/legacy/logic_inout.mli
+++ b/src/plugins/eva/legacy/logic_inout.mli
@@ -63,10 +63,10 @@ val assigns_tlval_to_zones:
   Cvalue.Model.t -> Locations.access -> Cil_types.term -> tlval_zones option
 
 (** Evaluate the assigns clauses of the given function in its given pre-state,
-    and compare them with the given froms (computed by the from plugin).
+    and compare them with the dependencies computed by the from plugin.
     Emits warnings if needed, and sets statuses to the assigns clauses. *)
 val verify_assigns:
-  Cil_types.kernel_function -> pre:Cvalue.Model.t -> Function_Froms.froms -> unit
+  Cil_types.kernel_function -> pre:Cvalue.Model.t -> Assigns.t -> unit
 
 
 (** [accept_base ~formals ~locals kf b] returns [true] if and only if [b] is:
diff --git a/src/plugins/eva/types/assigns.ml b/src/plugins/eva/types/assigns.ml
new file mode 100644
index 0000000000000000000000000000000000000000..cc34f73bdee90be68d279d7b10c517fd0554ab81
--- /dev/null
+++ b/src/plugins/eva/types/assigns.ml
@@ -0,0 +1,241 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  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 = {
+    return : Deps.t;
+    memory : Memory.t
+  }
+  [@@deriving eq,ord]
+
+  let name = "Eva.Froms"
+
+  let top = {
+    return = Deps.top;
+    memory = Memory.top;
+  }
+
+  let reprs = [ top ]
+
+  let hash assigns =
+    Hashtbl.hash (Memory.hash assigns.memory, Deps.hash assigns.return)
+
+  let pretty fmt assigns =
+    Format.fprintf fmt "%a@\n\\result FROM @[%a@]@\n"
+      Memory.pretty assigns.memory
+      Deps.pretty assigns.return
+end
+
+include Datatype.Make (Datatype_Input)
+include Datatype_Input
+
+let join x y =
+  {
+    return = Deps.join x.return y.return;
+    memory = Memory.join x.memory y.memory;
+  }
+
+let outputs assigns =
+  match assigns.memory 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/assigns.mli b/src/plugins/eva/types/assigns.mli
new file mode 100644
index 0000000000000000000000000000000000000000..2875fbf0dc3a54e01e7feab400950478fa4f82bb
--- /dev/null
+++ b/src/plugins/eva/types/assigns.mli
@@ -0,0 +1,83 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(** Datastructures and common operations for the results of the From plugin. *)
+
+[@@@ api_start]
+
+module DepsOrUnassigned : sig
+
+  type t =
+    | DepsBottom (** Bottom of the lattice, never bound inside a memory state
+                     at a valid location. (May appear for bases for which the
+                     validity does not start at 0, currently only NULL.) *)
+    | Unassigned (** Location has never been assigned *)
+    | AssignedFrom of Deps.t (** Location guaranteed to have been overwritten,
+                                 its contents depend on the [Deps.t] value *)
+    | MaybeAssignedFrom of Deps.t  (** Location may or may not have been
+                                       overwritten *)
+
+  (** The lattice is [DepsBottom <= Unassigned], [DepsBottom <= AssignedFrom z],
+      [Unassigned <= MaybeAssignedFrom] and
+      [AssignedFrom z <= MaybeAssignedFrom z]. *)
+
+  val top : t
+  val equal : t -> t -> bool
+  val may_be_unassigned : t -> bool
+  val to_zone : t -> Locations.Zone.t
+end
+
+module Memory : sig
+  include Lmap_bitwise.Location_map_bitwise with type v = DepsOrUnassigned.t
+
+  val find : t -> Locations.Zone.t -> Locations.Zone.t
+  (** Imprecise version of find, in which data and indirect dependencies are
+      not distinguished *)
+
+  val find_precise : t -> Locations.Zone.t -> Deps.t
+  (** Precise version of find *)
+
+  val find_precise_loffset : LOffset.t -> Base.t -> Int_Intervals.t -> Deps.t
+
+  val add_binding : exact:bool -> t -> Locations.Zone.t -> Deps.t -> t
+  val add_binding_loc : exact:bool -> t -> Locations.location -> Deps.t -> t
+  val add_binding_precise_loc :
+    exact:bool -> Locations.access -> t ->
+    Precise_locs.precise_location -> Deps.t -> t
+end
+
+type t = {
+  return : Deps.t
+(** Dependencies for the returned value *);
+  memory : Memory.t
+(** Dependencies on all the zones modified by the function *);
+}
+
+include Datatype.S with type t := t
+
+val top : t
+val join : t -> t -> t
+
+[@@@ api_end]
+
+(** Extract the left part of a from result, ie. the zones that are written *)
+val outputs : t -> Locations.Zone.t
diff --git a/src/plugins/eva/types/deps.ml b/src/plugins/eva/types/deps.ml
new file mode 100644
index 0000000000000000000000000000000000000000..2f6bad3f7a3fd2b6929de54743354bf3c206498f
--- /dev/null
+++ b/src/plugins/eva/types/deps.ml
@@ -0,0 +1,132 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  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 Zone = Locations.Zone
+
+type deps = {
+  data: Zone.t;
+  indirect: Zone.t;
+}
+
+(* Pretty printing of detailed internal representation *)
+let pretty_precise fmt {data; indirect} =
+  let bottom_data = Zone.is_bottom data in
+  let bottom_indirect = Zone.is_bottom indirect in
+  match bottom_indirect, bottom_data with
+  | true, true ->
+    Format.fprintf fmt "\\nothing"
+  | true, false ->
+    Format.fprintf fmt "direct: %a"
+      Zone.pretty data
+  | false, true ->
+    Format.fprintf fmt "indirect: %a"
+      Zone.pretty indirect
+  | false, false ->
+    Format.fprintf fmt "indirect: %a; direct: %a"
+      Zone.pretty indirect
+      Zone.pretty data
+
+let pretty_debug = pretty_precise
+
+(* Conversion to zone, used by default pretty printing *)
+let to_zone d = Locations.Zone.join d.data d.indirect
+
+
+(* Datatype *)
+
+module Prototype = struct
+  include Datatype.Serializable_undefined
+
+  type t = deps = {
+    data: Zone.t;
+    indirect: Zone.t;
+  }
+  [@@deriving eq,ord]
+
+  let name = "Deps"
+  let pretty fmt d = Zone.pretty fmt (to_zone d)
+  let hash fd = Hashtbl.hash (Zone.hash fd.data, Zone.hash fd.indirect)
+  let reprs = List.map (fun z -> {data = z; indirect = z}) Zone.reprs
+end
+
+include Datatype.Make (Prototype)
+include Prototype
+
+
+(* Constructors *)
+
+let bottom = {
+  data = Locations.Zone.bottom;
+  indirect = Locations.Zone.bottom;
+}
+
+let top = {
+  data = Zone.top;
+  indirect = Zone.top;
+}
+
+let data z = {
+  data = z;
+  indirect = Zone.bottom;
+}
+
+let indirect z = {
+  data = Zone.bottom;
+  indirect = z;
+}
+
+
+(* Mutators *)
+
+let add_data d data =
+  { d with data = Zone.join d.data data }
+
+let add_indirect d indirect =
+  { d with indirect = Zone.join d.indirect indirect }
+
+
+(* Map *)
+
+let map f d = {
+  data = f d.data;
+  indirect = f d.indirect;
+}
+
+
+(* Lattice *)
+
+let is_included fd1 fd2 =
+  Zone.is_included fd1.data fd2.data &&
+  Zone.is_included fd1.indirect fd2.indirect
+
+let join d1 d2 =
+  if d1 == bottom then d2
+  else if d2 == bottom then d1
+  else {
+    data = Zone.join d1.data d2.data;
+    indirect = Zone.join d1.indirect d2.indirect;
+  }
+
+let narrow d1 d2 = {
+  data = Zone.narrow d1.data d2.data;
+  indirect = Zone.narrow d1.indirect d2.indirect;
+}
diff --git a/src/plugins/eva/types/deps.mli b/src/plugins/eva/types/deps.mli
new file mode 100644
index 0000000000000000000000000000000000000000..a30399e8e540450f5be762e719d3ffd86db01176
--- /dev/null
+++ b/src/plugins/eva/types/deps.mli
@@ -0,0 +1,63 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+[@@@ api_start]
+
+(** Memory dependencies of an expression. *)
+type t = {
+  data: Locations.Zone.t;
+  (** Memory zone directly required to evaluate the given expression. *)
+  indirect: Locations.Zone.t;
+  (** Memory zone read to compute data addresses. *)
+}
+
+include Datatype.S with type t := t
+
+val pretty_precise: Format.formatter -> t -> unit
+val pretty_debug: Format.formatter -> t -> unit
+
+(* Constructors *)
+
+val top : t
+val bottom : t
+val data : Locations.Zone.t -> t
+val indirect : Locations.Zone.t -> t
+
+(* Conversion *)
+
+val to_zone : t -> Locations.Zone.t
+
+(* Mutators *)
+
+val add_data : t -> Locations.Zone.t -> t
+val add_indirect : t -> Locations.Zone.t -> t
+
+(* Map *)
+
+val map : (Locations.Zone.t -> Locations.Zone.t) -> t -> t
+
+(* Lattice operators *)
+
+val is_included : t -> t -> bool
+val join : t -> t -> t
+val narrow : t -> t -> t
+[@@@ api_end]
diff --git a/src/plugins/eva/utils/cvalue_callbacks.ml b/src/plugins/eva/utils/cvalue_callbacks.ml
index d4101a12b9c2de6f3a289deef62458ba3a3ef6c6..99461fbbeb8ba04c86f57163a4a90380d8f25a1b 100644
--- a/src/plugins/eva/utils/cvalue_callbacks.ml
+++ b/src/plugins/eva/utils/cvalue_callbacks.ml
@@ -41,13 +41,13 @@ let register_call_hook f =
 let apply_call_hooks callstack kf state kind =
   Call.apply (callstack, kf, state, kind)
 
-type call_froms = (Function_Froms.froms * Locations.Zone.t) option
+type call_assigns = (Assigns.t * Locations.Zone.t) option
 
 type state_by_stmt = (state Cil_datatype.Stmt.Hashtbl.t) Lazy.t
 type results = { before_stmts: state_by_stmt; after_stmts: state_by_stmt }
 
 type call_results =
-  [ `Builtin of state list * call_froms
+  [ `Builtin of state list * call_assigns
   | `Spec of state list
   | `Body of results * int
   | `Reuse of int
diff --git a/src/plugins/eva/utils/cvalue_callbacks.mli b/src/plugins/eva/utils/cvalue_callbacks.mli
index baa6dc0942ac19de3ec39a0ce11584b893626fe8..203dddeced30b3c8eb3a0604026075e42ca8837d 100644
--- a/src/plugins/eva/utils/cvalue_callbacks.mli
+++ b/src/plugins/eva/utils/cvalue_callbacks.mli
@@ -30,10 +30,11 @@
 
 type state = Cvalue.Model.t
 
-(** If not None, the froms of the function, and its sure outputs;
-    i.e. the dependencies of the result, and the dependencies
-    of each zone written to. *)
-type call_froms = (Function_Froms.froms * Locations.Zone.t) option
+(** If not None:
+    - the assigns of the function, i.e. the dependencies of the result
+      and the dependencies of each zone written to;
+    - and its sure outputs, i.e. an under-approximation of written zones. *)
+type call_assigns = (Assigns.t * Locations.Zone.t) option
 
 type analysis_kind =
   [ `Builtin (** A cvalue builtin is used to interpret the function. *)
@@ -58,7 +59,7 @@ type results = { before_stmts: state_by_stmt; after_stmts: state_by_stmt }
 
 (** Results of a function call. *)
 type call_results =
-  [ `Builtin of state list * call_froms
+  [ `Builtin of state list * call_assigns
   (** List of cvalue states at the end of the builtin. *)
   | `Spec of state list
   (** List of cvalue states at the end of the call. *)
diff --git a/src/plugins/eva/utils/eva_utils.ml b/src/plugins/eva/utils/eva_utils.ml
index e1a9d043fec882e18870b54ba124cdf87ce62943..8741fe7ea42754bfae1230ba6ab801d53dbb2c57 100644
--- a/src/plugins/eva/utils/eva_utils.ml
+++ b/src/plugins/eva/utils/eva_utils.ml
@@ -285,20 +285,6 @@ let lval_to_exp =
   MemoLvalToExp.memo
     (fun lv -> Cil.new_exp ~loc:Cil_datatype.Location.unknown (Lval lv))
 
-type deps = Function_Froms.Deps.deps = {
-  data: Locations.Zone.t;
-  indirect: Locations.Zone.t;
-}
-
-let bottom_deps =
-  { data = Locations.Zone.bottom; indirect = Locations.Zone.bottom }
-
-let join_deps a b =
-  { data = Locations.Zone.join a.data b.data;
-    indirect = Locations.Zone.join a.indirect b.indirect; }
-
-let deps_to_zone deps = Locations.Zone.join deps.data deps.indirect
-
 (* Computation of the inputs of an expression. *)
 let rec deps_of_expr find_loc expr =
   let rec process expr = match expr.enode with
@@ -310,7 +296,7 @@ let rec deps_of_expr find_loc expr =
       process e
     | BinOp (_, e1, e2, _) ->
       (* Binary operators. *)
-      join_deps (process e1) (process e2)
+      Deps.join (process e1) (process e2)
     | StartOf lv | AddrOf lv ->
       (* computation of an address: the inputs of the lvalue whose address
          is computed are read to compute said address. *)
@@ -318,11 +304,11 @@ let rec deps_of_expr find_loc expr =
         indirect = Locations.Zone.bottom; }
     | Const _ | SizeOf _ | AlignOf _ | SizeOfStr _ | SizeOfE _ | AlignOfE _ ->
       (* static constructs, nothing is read to evaluate them. *)
-      bottom_deps
+      Deps.bottom
   in
   process expr
 
-and zone_of_expr find_loc expr = deps_to_zone (deps_of_expr find_loc expr)
+and zone_of_expr find_loc expr = Deps.to_zone (deps_of_expr find_loc expr)
 
 (* dereference of an lvalue: first, its address must be computed,
    then its contents themselves are read *)
diff --git a/src/plugins/eva/utils/eva_utils.mli b/src/plugins/eva/utils/eva_utils.mli
index b7c7a84512a3e487546f175d94229b679fcb82b7..2e6380b1ba6aebca694803c7112c684743c46389 100644
--- a/src/plugins/eva/utils/eva_utils.mli
+++ b/src/plugins/eva/utils/eva_utils.mli
@@ -126,18 +126,12 @@ val indirect_zone_of_lval:
     on which the offset and the pointer expression (if any) of an lvalue depend.
 *)
 
-
-type deps = Function_Froms.Deps.deps = {
-  data: Locations.Zone.t;
-  indirect: Locations.Zone.t;
-}
-
 val deps_of_expr:
-  (lval -> Precise_locs.precise_location) -> exp -> deps
+  (lval -> Precise_locs.precise_location) -> exp -> Deps.t
 (** Given a function computing the location of lvalues, computes the memory
     dependencies of an expression. *)
 
-val deps_of_lval: (lval -> Precise_locs.precise_location) -> lval -> deps
+val deps_of_lval: (lval -> Precise_locs.precise_location) -> lval -> Deps.t
 (** Given a function computing the location of lvalues, computes the memory
     dependencies of an lvalue. *)
 
diff --git a/src/plugins/eva/utils/private.ml b/src/plugins/eva/utils/private.ml
index e4a368655a9c996dcafcb0369e4ba346cfa6549b..2a103740752b240773be661b721e6142729470cc 100644
--- a/src/plugins/eva/utils/private.ml
+++ b/src/plugins/eva/utils/private.ml
@@ -28,6 +28,7 @@ module Abstractions = Abstractions
 module Active_behaviors = Active_behaviors
 module Alarmset = Alarmset
 module Analysis = Analysis
+module Assigns = Assigns
 module Callstack = Callstack
 module Cvalue_domain = Cvalue_domain
 module Cvalue_results = Cvalue_results
diff --git a/src/plugins/eva/utils/private.mli b/src/plugins/eva/utils/private.mli
index f70377152869169e4763bef80aca6d553384f555..eb7491b3c9617d5c9f9e8029b9d5106d13bf2f5c 100644
--- a/src/plugins/eva/utils/private.mli
+++ b/src/plugins/eva/utils/private.mli
@@ -32,6 +32,7 @@ module Abstractions = Abstractions
 module Active_behaviors = Active_behaviors
 module Alarmset = Alarmset
 module Analysis = Analysis
+module Assigns = Assigns
 module Callstack = Callstack
 module Cvalue_domain = Cvalue_domain
 module Cvalue_results = Cvalue_results
diff --git a/src/plugins/eva/utils/results.ml b/src/plugins/eva/utils/results.ml
index 297a2b22d2edf20faaac55e7e7c4e5a6443e824d..0df27562e45c83d92c131f8e32b81caa27d8889e 100644
--- a/src/plugins/eva/utils/results.ml
+++ b/src/plugins/eva/utils/results.ml
@@ -733,11 +733,6 @@ let address_deps lval request =
   let lval_to_loc lv = eval_address lv request |> as_precise_loc in
   Eva_utils.indirect_zone_of_lval lval_to_loc lval
 
-type deps = Function_Froms.Deps.deps = {
-  data: Locations.Zone.t;
-  indirect: Locations.Zone.t;
-}
-
 let expr_dependencies expr request =
   let lval_to_loc lv = eval_address lv request |> as_precise_loc in
   Eva_utils.deps_of_expr lval_to_loc expr
diff --git a/src/plugins/eva/utils/results.mli b/src/plugins/eva/utils/results.mli
index b091f95830f1e2c6846970a94b84bbafc81e4072..c68373ebcb4157a8312dfefb59b6f0467863d320 100644
--- a/src/plugins/eva/utils/results.mli
+++ b/src/plugins/eva/utils/results.mli
@@ -177,14 +177,6 @@ val lval_deps : Cil_types.lval -> request -> Locations.Zone.t
     evaluate the given lvalue, excluding the lvalue zone itself. *)
 val address_deps : Cil_types.lval -> request -> Locations.Zone.t
 
-(** Memory dependencies of an expression. *)
-type deps = Function_Froms.Deps.deps = {
-  data: Locations.Zone.t;
-  (** Memory zone directly required to evaluate the given expression. *)
-  indirect: Locations.Zone.t;
-  (** Memory zone read to compute data addresses. *)
-}
-
 (** Taint of a memory zone, according to the taint domain. *)
 type taint =
   | Direct
@@ -203,7 +195,7 @@ val is_tainted : Locations.Zone.t -> request -> (taint, error) Result.t
 
 (** Computes (an overapproximation of) the memory dependencies of an
     expression. *)
-val expr_dependencies : Cil_types.exp -> request -> deps
+val expr_dependencies : Cil_types.exp -> request -> Deps.t
 
 (** Evaluation *)
 
diff --git a/src/plugins/from/From.ml b/src/plugins/from/From.ml
index 03c99df40462fd32d23c0c3d9794b283fc074559..a2bbb575037a34ebd51051c1406eee5d305a44b5 100644
--- a/src/plugins/from/From.ml
+++ b/src/plugins/from/From.ml
@@ -27,7 +27,7 @@ let is_computed = Functionwise.is_computed
 let get = Functionwise.get
 let pretty = Functionwise.pretty
 
-let access zone mem = Function_Froms.Memory.find mem zone
+let access zone mem = Eva.Assigns.Memory.find mem zone
 
 let display fmt = From_register.display (Some fmt)
 
diff --git a/src/plugins/from/From.mli b/src/plugins/from/From.mli
index ac8e62ad0c26771c22e8f1ccf023d03be617a62f..ac0db406d4db28374a7cea5b28c4486c54885a05 100644
--- a/src/plugins/from/From.mli
+++ b/src/plugins/from/From.mli
@@ -28,8 +28,8 @@ val is_computed : kernel_function -> bool
 val compute : kernel_function -> unit
 val compute_all : unit -> unit
 
-val get : Cil_types.kernel_function -> Function_Froms.froms
-val access : Locations.Zone.t -> Function_Froms.Memory.t -> Locations.Zone.t
+val get : Cil_types.kernel_function -> Eva.Assigns.t
+val access : Locations.Zone.t -> Eva.Assigns.Memory.t -> Locations.Zone.t
 
 val self : State.t
 
@@ -42,6 +42,6 @@ val display : Format.formatter -> unit
 
 val compute_all_calldeps : unit -> unit
 module Callwise : sig
-  val iter : (Cil_types.kinstr -> Function_Froms.froms -> unit) -> unit
-  val find : Cil_types.kinstr -> Function_Froms.froms
+  val iter : (Cil_types.kinstr -> Eva.Assigns.t -> unit) -> unit
+  val find : Cil_types.kinstr -> Eva.Assigns.t
 end
diff --git a/src/plugins/from/callwise.ml b/src/plugins/from/callwise.ml
index 0e5d37b4d35d0d8aaf6bd1613548ee7cd526d7d2..1d70d24bb671bb6e53819735614723cf564333ac 100644
--- a/src/plugins/from/callwise.ml
+++ b/src/plugins/from/callwise.ml
@@ -25,7 +25,7 @@ open Cil_datatype
 
 module Tbl =
   Cil_state_builder.Kinstr_hashtbl
-    (Function_Froms)
+    (Eva.Assigns)
     (struct
       let name = "Callwise dependencies"
       let size = 17
@@ -36,7 +36,7 @@ let () = From_parameters.ForceCallDeps.set_output_dependencies [Tbl.self]
 let merge_call_froms table callsite froms =
   try
     let current = Kinstr.Hashtbl.find table callsite in
-    let new_froms = Function_Froms.join froms current in
+    let new_froms = Eva.Assigns.join froms current in
     Kinstr.Hashtbl.replace table callsite new_froms
   with Not_found ->
     Kinstr.Hashtbl.add table callsite froms
@@ -44,7 +44,7 @@ let merge_call_froms table callsite froms =
 (** State for the analysis of one function call *)
 type from_state = {
   current_function: Kernel_function.t (** Function being analyzed *);
-  table_for_calls: Function_Froms.t Kinstr.Hashtbl.t
+  table_for_calls: Eva.Assigns.t Kinstr.Hashtbl.t
 (** State of the From plugin for each statement containing a function call
     in the body of [current_function]. Updated incrementally each time
     Value analyses such a statement *);
@@ -59,7 +59,7 @@ let call_froms_stack : from_state list ref = ref []
 let record_callwise_dependencies_in_db call_site froms =
   try
     let previous = Tbl.find call_site in
-    Tbl.replace call_site (Function_Froms.join previous froms)
+    Tbl.replace call_site (Eva.Assigns.join previous froms)
   with Not_found -> Tbl.add call_site froms
 
 let call_for_individual_froms _callstack current_function _state call_type =
@@ -97,7 +97,7 @@ let end_record callstack froms =
 module MemExec =
   State_builder.Hashtbl
     (Datatype.Int.Hashtbl)
-    (Function_Froms)
+    (Eva.Assigns)
     (struct
       let size = 17
       let dependencies = [Tbl.self]
@@ -135,7 +135,7 @@ let record_for_individual_froms callstack kf pre_state value_res =
         let froms =
           if Eva.Analysis.save_results kf
           then compute_call_from_value_states kf (Lazy.force before_stmts)
-          else Function_Froms.top
+          else Eva.Assigns.top
         in
         if From_parameters.VerifyAssigns.get () then
           Eva.Logic_inout.verify_assigns kf ~pre:pre_state froms;
diff --git a/src/plugins/from/callwise.mli b/src/plugins/from/callwise.mli
index 6fa25e4f741c7f6803d63cdfd1795f81423afdee..ad682a4d569ca6c75efea339479e358348a738aa 100644
--- a/src/plugins/from/callwise.mli
+++ b/src/plugins/from/callwise.mli
@@ -25,5 +25,5 @@
     precise than the functionwise results. *)
 
 val compute_all_calldeps : unit -> unit
-val iter : (Cil_types.kinstr -> Function_Froms.froms -> unit) -> unit
-val find : Cil_types.kinstr -> Function_Froms.froms
+val iter : (Cil_types.kinstr -> Eva.Assigns.t -> unit) -> unit
+val find : Cil_types.kinstr -> Eva.Assigns.t
diff --git a/src/plugins/from/from_compute.ml b/src/plugins/from/from_compute.ml
index 4c662872580ba2732f0583f403dda70ffe646c7a..9a063c21cea44ee5b54938be5b28d21a1fcf8f1b 100644
--- a/src/plugins/from/from_compute.ml
+++ b/src/plugins/from/from_compute.ml
@@ -33,16 +33,16 @@ module Record_From_Callbacks =
     (struct
       type t =
         (Kernel_function.t Stack.t) *
-        Function_Froms.Memory.t Stmt.Hashtbl.t *
-        (Kernel_function.t * Function_Froms.Memory.t) list Stmt.Hashtbl.t
+        Eva.Assigns.Memory.t Stmt.Hashtbl.t *
+        (Kernel_function.t * Eva.Assigns.Memory.t) list Stmt.Hashtbl.t
     end)
 
 module type To_Use =
 sig
-  val get_from_call : kernel_function -> stmt -> Function_Froms.t
+  val get_from_call : kernel_function -> stmt -> Eva.Assigns.t
   val stmt_request : stmt -> Eva.Results.request
   val keep_base : kernel_function -> Base.t -> bool
-  val cleanup_and_save : kernel_function -> Function_Froms.t -> Function_Froms.t
+  val cleanup_and_save : kernel_function -> Eva.Assigns.t -> Eva.Assigns.t
 end
 
 let compute_using_prototype_for_state state kf assigns =
@@ -53,7 +53,7 @@ let compute_using_prototype_for_state state kf assigns =
       From_parameters.warning "@[no assigns clauses@ for function %a.@]@ \
                                Results will be imprecise."
         Kernel_function.pretty kf;
-      Function_Froms.Memory.(top_return, top)
+      From_memory.(top_return, top)
     | Writes assigns ->
       let (rt_typ,_,_,_) = splitFunctionTypeVI varinfo in
       let input_zone out ins =
@@ -75,9 +75,9 @@ let compute_using_prototype_for_state state kf assigns =
           (* assign clauses do not let us specify address
              dependencies for now, so we assume it is all data
              dependencies *)
-          let input_deps = Function_Froms.Deps.from_data_deps input_zone in
+          let input_deps = Eva.Deps.data input_zone in
           (* Weak update of the over-approximation of the zones assigned *)
-          let acc = Function_Froms.Memory.add_binding ~exact:false
+          let acc = Eva.Assigns.Memory.add_binding ~exact:false
               acc output.over input_deps in
           (* Now, perform a strong update on the zones that are guaranteed
                    to be assigned (under-approximation) AND that do not depend
@@ -90,7 +90,7 @@ let compute_using_prototype_for_state state kf assigns =
             Zone.(if equal top input_zone then bottom
                   else diff output.under input_zone)
           in
-          let acc = Function_Froms.Memory.add_binding ~exact:true
+          let acc = Eva.Assigns.Memory.add_binding ~exact:true
               acc sure_out_zone input_deps in
           acc
 
@@ -99,15 +99,15 @@ let compute_using_prototype_for_state state kf assigns =
         let zone_from = input_zone out from in
         (* assign clauses do not let us specify address dependencies for
            now, so we assume it is all data dependencies *)
-        let inputs_deps = Function_Froms.Deps.from_data_deps zone_from in
+        let inputs_deps = Eva.Deps.data zone_from in
         try
           let coffs = Logic_to_c.loc_to_offset out.it_content in
           List.fold_left
             (fun acc coff ->
                let (base,width) = bitsOffset rt_typ coff in
                let size = Int_Base.inject (Int.of_int width) in
-               Function_Froms.Memory.(add_to_return
-                                        ~start:base ~size ~m:acc inputs_deps)
+               From_memory.add_to_return
+                 ~start:base ~size ~m:acc inputs_deps
             )
             acc coffs
         with Logic_to_c.No_conversion | SizeOfError _ ->
@@ -115,7 +115,7 @@ let compute_using_prototype_for_state state kf assigns =
             "Unable to extract a proper offset. \
              Using FROM for the whole \\result";
           let size = Bit_utils.sizeof rt_typ in
-          Function_Froms.(Memory.add_to_return ~size ~m:acc inputs_deps)
+          From_memory.add_to_return ~size ~m:acc inputs_deps
       in
       let return_assigns, other_assigns =
         List.fold_left
@@ -127,19 +127,19 @@ let compute_using_prototype_for_state state kf assigns =
       let return_assigns =
         match return_assigns with
         | [] when Cil.isVoidType rt_typ ->
-          Function_Froms.Memory.default_return
+          From_memory.default_return
         | [] -> (* \from unspecified. *)
           let size = Bit_utils.sizeof rt_typ in
-          Function_Froms.Memory.top_return_size size
+          From_memory.top_return_size size
         | _ ->
           List.fold_left treat_ret_assign
-            Function_Froms.Memory.default_return return_assigns
+            From_memory.default_return return_assigns
       in
       return_assigns,
       List.fold_left
-        treat_assign Function_Froms.Memory.empty other_assigns
+        treat_assign Eva.Assigns.Memory.empty other_assigns
   in
-  { deps_return = return_deps; Function_Froms.deps_table = deps }
+  Eva.Assigns.{ return = return_deps; memory = deps }
 
 module ZoneStmtMap = struct
   include
@@ -164,7 +164,7 @@ struct
           the statement that gave rise to the dependency. *)
       additional_deps : Zone.t;
       (** Union of the sets in {!additional_deps_table} *)
-      deps_table : Function_Froms.Memory.t
+      deps_table : Eva.Assigns.Memory.t
       (** dependency table *)
     }
 
@@ -181,7 +181,7 @@ struct
         depending directly on an indirect dependency -> indirect,
         depending indirectly on a direct dependency  -> indirect *)
   let merge_deps f deps =
-    let open Function_Froms.Deps in
+    let open Eva.Deps in
     let ind = f deps.indirect in
     let data = f deps.data in
     let ind = Zone.join data.indirect (to_zone ind) in
@@ -202,7 +202,7 @@ struct
     let aux_local acc vi =
       Cil.CurrentLoc.set vi.vdecl;
       (* Consider that local are initialized to a constant value *)
-      Function_Froms.Memory.bind_var vi Function_Froms.Deps.bottom acc
+      From_memory.bind_var vi Eva.Deps.bottom acc
     in
     let loc = Cil.CurrentLoc.get () in
 
@@ -212,7 +212,7 @@ struct
 
   let unbind_locals m b =
     let aux_local acc vi =
-      Function_Froms.Memory.unbind_var vi acc
+      From_memory.unbind_var vi acc
     in
     List.fold_left aux_local m b.blocals
 
@@ -221,7 +221,7 @@ struct
     let request = To_Use.stmt_request stmt in
     let pre_trans = Eva.Results.expr_dependencies expr request in
     merge_deps
-      (fun d -> Function_Froms.Memory.find_precise deps_tbl d) pre_trans
+      (fun d -> Eva.Assigns.Memory.find_precise deps_tbl d) pre_trans
 
   let lval_to_zone_with_deps stmt lv =
     let request = To_Use.stmt_request stmt in
@@ -238,12 +238,12 @@ struct
   let empty_from =
     { additional_deps_table = ZoneStmtMap.empty;
       additional_deps = Zone.bottom;
-      deps_table = Function_Froms.Memory.empty }
+      deps_table = Eva.Assigns.Memory.empty }
 
   let bottom_from =
     { additional_deps_table = ZoneStmtMap.empty;
       additional_deps = Zone.bottom;
-      deps_table = Function_Froms.Memory.bottom }
+      deps_table = Eva.Assigns.Memory.bottom }
 
   module Computer = struct
 
@@ -253,11 +253,11 @@ struct
     let callwise_states_with_formals = Stmt.Hashtbl.create 7
 
     let substitute call_site_froms extra_loc deps =
-      let subst_deps = Function_Froms.Memory.substitute call_site_froms deps in
-      Function_Froms.Deps.add_indirect_dep subst_deps extra_loc
+      let subst_deps = From_memory.substitute call_site_froms deps in
+      Eva.Deps.add_indirect subst_deps extra_loc
 
     let display_one_from fmt v =
-      Function_Froms.Memory.pretty fmt v.deps_table;
+      Eva.Assigns.Memory.pretty fmt v.deps_table;
       Format.fprintf fmt "Additional Variable Map : %a@\n"
         ZoneStmtMap.pretty v.additional_deps_table;
       Format.fprintf fmt
@@ -270,7 +270,7 @@ struct
 
     let transfer_conditional_exp s exp state =
       let additional = find s state.deps_table exp in
-      let additional = Function_Froms.Deps.to_zone additional in
+      let additional = Eva.Deps.to_zone additional in
       {state with
        additional_deps_table =
          ZoneStmtMap.add s additional state.additional_deps_table;
@@ -291,10 +291,10 @@ struct
           m, new_z, false
       in
       let map =
-        Function_Froms.Memory.join new_.deps_table old.deps_table
+        Eva.Assigns.Memory.join new_.deps_table old.deps_table
       in
       let included' =
-        Function_Froms.Memory.is_included new_.deps_table old.deps_table
+        Eva.Assigns.Memory.is_included new_.deps_table old.deps_table
       in
       { deps_table = map;
         additional_deps_table = additional_map;
@@ -314,12 +314,12 @@ struct
       let deps, loc, exact =
         lval_to_precise_loc_with_deps stmt ~for_writing:(not init) lv
       in
-      let deps_of_deps = Function_Froms.Memory.find state.deps_table deps in
+      let deps_of_deps = Eva.Assigns.Memory.find state.deps_table deps in
       let all_indirect = Zone.join state.additional_deps deps_of_deps in
-      let deps = Function_Froms.Deps.add_indirect_dep deps_right all_indirect in
+      let deps = Eva.Deps.add_indirect deps_right all_indirect in
       let access = if init then Read else Write in
       { state with deps_table =
-                     Function_Froms.Memory.add_binding_precise_loc
+                     Eva.Assigns.Memory.add_binding_precise_loc
                        ~exact access state.deps_table loc deps }
 
     let transfer_call stmt dest f args _loc state =
@@ -328,9 +328,7 @@ struct
       let called_vinfos = Eva.Results.(eval_callee f request |> default []) in
       let f_deps = Eva.Results.expr_deps f request in
       (* dependencies for the evaluation of [f] *)
-      let f_deps =
-        Function_Froms.Memory.find state.deps_table f_deps
-      in
+      let f_deps = Eva.Assigns.Memory.find state.deps_table f_deps in
       let additional_deps =
         Zone.join
           state.additional_deps
@@ -350,8 +348,8 @@ struct
           state
         else
           let froms_call = To_Use.get_from_call kf stmt in
-          let froms_call_table = froms_call.Function_Froms.deps_table in
-          if Function_Froms.Memory.is_bottom froms_call_table then
+          let froms_call_table = froms_call.Eva.Assigns.memory in
+          if Eva.Assigns.Memory.is_bottom froms_call_table then
             bottom_from
           else
             let formal_args = Kernel_function.get_formals kf in
@@ -360,7 +358,7 @@ struct
                 List.iter2
                   (fun vi from ->
                      state_with_formals :=
-                       Function_Froms.Memory.bind_var
+                       From_memory.bind_var
                          vi from !state_with_formals;
                   ) formal_args args_froms;
               with Invalid_argument _ ->
@@ -381,10 +379,10 @@ struct
                but before the result assignment *)
             let deps_after_call =
               let before_call = state.deps_table in
-              let open Function_Froms in
-              let subst d = DepsOrUnassigned.subst subst_before_call d in
-              let call_substituted = Memory.map subst froms_call_table in
-              Memory.compose call_substituted before_call
+              let call_substituted =
+                From_memory.map subst_before_call froms_call_table
+              in
+              From_memory.compose call_substituted before_call
             in
             let state = {state with deps_table = deps_after_call } in
             (* Treatement for the possible assignment
@@ -392,7 +390,7 @@ struct
             match dest with
             | None -> state
             | Some lv ->
-              let return_from = froms_call.Function_Froms.deps_return in
+              let return_from = froms_call.Eva.Assigns.return in
               let deps_ret = subst_before_call return_from in
               let init = Cil.is_mutable_or_initialized lv in
               transfer_assign stmt ~init lv deps_ret state
@@ -404,7 +402,7 @@ struct
         | Some acc_memory ->
           Some
             {state with
-             deps_table = Function_Froms.Memory.join
+             deps_table = Eva.Assigns.Memory.join
                  p.deps_table
                  acc_memory.deps_table}
       in
@@ -452,7 +450,7 @@ struct
               (* If implicit zero-initializers have been skipped, also mark
                  the entire array as initialized from no dependency (nothing
                  is read by the implicit zero-initializers). *)
-              transfer_assign stmt ~init:true lv Function_Froms.Deps.bottom r
+              transfer_assign stmt ~init:true lv Eva.Deps.bottom r
         in
         aux (Cil.var v) i state
       | Call (lvaloption,funcexp,argl,loc) ->
@@ -521,7 +519,7 @@ struct
     (* Filter out unreachable values. *)
     let transfer_stmt s d =
       if Eva.Results.is_reachable s &&
-         not (Function_Froms.Memory.is_bottom d.deps_table)
+         not (Eva.Assigns.Memory.is_bottom d.deps_table)
       then transfer_stmt s d
       else []
 
@@ -547,30 +545,27 @@ struct
 
 
   (* Remove all local variables and formals from table *)
-  let externalize return kf state =
-    let deps_return =
-      (match return.skind with
+  let externalize return_stmt kf state =
+    let return =
+      (match return_stmt.skind with
        | Return (Some ({enode = Lval v}),_) ->
-         let zone = lval_to_zone_with_deps return v in
-         let deps = Function_Froms.Memory.find_precise state.deps_table zone in
+         let zone = lval_to_zone_with_deps return_stmt v in
+         let deps = Eva.Assigns.Memory.find_precise state.deps_table zone in
          let size = Bit_utils.sizeof (Cil.typeOfLval v) in
-         Function_Froms.(Memory.add_to_return ~size deps)
+         From_memory.add_to_return ~size deps
        | Return (None,_) ->
-         Function_Froms.Memory.default_return
+         From_memory.default_return
        | _ -> assert false)
     in
     let accept = To_Use.keep_base kf in
-    let deps_table =
-      Function_Froms.Memory.filter_base accept state.deps_table
-    in
-    { deps_return = deps_return;
-      Function_Froms.deps_table = deps_table }
+    let memory = Eva.Assigns.Memory.filter_base accept state.deps_table in
+    Eva.Assigns.{ return; memory }
 
   let compute_using_cfg kf =
     match kf.fundec with
     | Declaration _ -> assert false
     | Definition (f,_) ->
-      if not (Eva.Analysis.save_results kf) then Function_Froms.top
+      if not (Eva.Analysis.save_results kf) then Eva.Assigns.top
       else
         try
           Stack.iter (fun g -> if kf == g then raise Exit) call_stack;
@@ -615,16 +610,19 @@ struct
                 From_parameters.result
                   "Non-terminating function %a (no dependencies)"
                   Kernel_function.pretty kf;
-                { Function_Froms.deps_return =
-                    Function_Froms.Memory.default_return;
-                  deps_table = Function_Froms.Memory.bottom }
+                Eva.Assigns.{
+                  return = From_memory.default_return;
+                  memory = Eva.Assigns.Memory.bottom
+                }
               end
           in
           last_from
 
         with Exit (* Recursive call *) ->
-          { Function_Froms.deps_return = Function_Froms.Memory.default_return;
-            deps_table = Function_Froms.Memory.empty }
+          {
+            return = From_memory.default_return;
+            memory = Eva.Assigns.Memory.empty
+          }
 
   let compute_using_prototype kf =
     let state = Eva.Results.(at_start_of kf |> get_cvalue_model) in
diff --git a/src/plugins/from/from_compute.mli b/src/plugins/from/from_compute.mli
index 9beabb6c488608a4c1efd485a7b0d775dbff2859..a48e9c38d2675bd00784dc2a7af4a1230328ccf9 100644
--- a/src/plugins/from/from_compute.mli
+++ b/src/plugins/from/from_compute.mli
@@ -30,7 +30,7 @@ open Cil_types
 module type To_Use =
 sig
   (** How to find the Froms for a given call during the analysis. *)
-  val get_from_call : kernel_function -> stmt -> Function_Froms.t
+  val get_from_call : kernel_function -> stmt -> Eva.Assigns.t
 
   (** The Eva request that can be used to evaluate expressions at a given
       statement through the Eva public API. *)
@@ -43,7 +43,7 @@ sig
 
   (** Clean the given from (that have been computed for the given function),
       optionally save them, and return the cleaned result. *)
-  val cleanup_and_save : kernel_function -> Function_Froms.t -> Function_Froms.t
+  val cleanup_and_save : kernel_function -> Eva.Assigns.t -> Eva.Assigns.t
 end
 
 (** Function that compute the Froms from a given prototype, called
@@ -52,14 +52,14 @@ val compute_using_prototype_for_state :
   Cvalue.Model.t ->
   Kernel_function.t ->
   assigns ->
-  Function_Froms.froms
+  Eva.Assigns.t
 
 
 (** Functor computing the functional dependencies, according to the three
     modules above. *)
 module Make (_: To_Use) : sig
   (** Compute the dependencies of the given function, and return them *)
-  val compute_and_return : Kernel_function.t -> Function_Froms.t
+  val compute_and_return : Kernel_function.t -> Eva.Assigns.t
 
   (** Compute the dependencies of the given function *)
   val compute : Kernel_function.t -> unit
diff --git a/src/plugins/from/from_memory.ml b/src/plugins/from/from_memory.ml
new file mode 100644
index 0000000000000000000000000000000000000000..57d9fde7a112b070d89182f35e8ac20107f5bc62
--- /dev/null
+++ b/src/plugins/from/from_memory.ml
@@ -0,0 +1,248 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Locations
+module Deps = Eva.Deps
+
+module DepsOrUnassigned = struct
+  include Eva.Assigns.DepsOrUnassigned
+
+  let subst f d =
+    match d with
+    | DepsBottom -> DepsBottom
+    | Unassigned -> Unassigned
+    | AssignedFrom fd ->
+      let fd' = f fd in
+      if fd == fd' then d else AssignedFrom fd'
+    | MaybeAssignedFrom fd ->
+      let fd' = f fd in
+      if fd == fd' then d else MaybeAssignedFrom fd'
+
+  let compose d1 d2 =
+    match d1, d2 with
+    | DepsBottom, _ | _, DepsBottom ->
+      DepsBottom (* could indicate dead code. Not used in practice anyway *)
+    | Unassigned, _ -> d2
+    | AssignedFrom _, _ -> d1
+    | MaybeAssignedFrom _, Unassigned -> d1
+    | MaybeAssignedFrom d1, MaybeAssignedFrom d2 ->
+      MaybeAssignedFrom (Deps.join d1 d2)
+    | MaybeAssignedFrom d1, AssignedFrom d2 ->
+      AssignedFrom (Deps.join d1 d2)
+
+  (* for backwards compatibility *)
+  let pretty fmt fd =
+    match fd with
+    | DepsBottom -> Format.pp_print_string fmt "DEPS_BOTTOM"
+    | Unassigned -> Format.pp_print_string fmt "(SELF)"
+    | AssignedFrom d -> Zone.pretty fmt (Deps.to_zone d)
+    | MaybeAssignedFrom d ->
+      Format.fprintf fmt "%a (and SELF)" Zone.pretty (Deps.to_zone d)
+
+  let pretty_precise 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 ->
+      (* '(or UNASSIGNED)' would be a better pretty-printer, we use
+         '(and SELF)' only for compatibility reasons *)
+      Format.fprintf fmt "%a (and SELF)" Deps.pretty_precise fd
+end
+
+include Eva.Assigns.Memory
+
+let bind_var vi v m =
+  let z = Locations.zone_of_varinfo vi in
+  add_binding ~exact:true m z v
+
+let unbind_var vi m =
+  remove_base (Base.of_varinfo vi) m
+
+let map f = map (DepsOrUnassigned.subst f)
+
+let is_unassigned m =
+  LOffset.is_same_value m DepsOrUnassigned.Unassigned
+
+(* Unassigned is a neutral value for compose, on both sides *)
+let decide_compose m1 m2 =
+  if m1 == m2 || is_unassigned m1 then LOffset.ReturnRight
+  else if is_unassigned m2 then LOffset.ReturnLeft
+  else LOffset.Recurse
+
+let compose_map =
+  let cache = Hptmap_sig.PersistentCache "From_memory.compose" in
+  (* Partial application is important because of the cache. Idempotent,
+     because [compose x x] is always equal to [x]. *)
+  map2 ~cache ~symmetric:false ~idempotent:true ~empty_neutral:true
+    decide_compose DepsOrUnassigned.compose
+
+let compose m1 m2 = match m1, m2 with
+  | Top, _ | _, Top -> Top
+  | Map m1, Map m2 -> Map (compose_map m1 m2)
+  | Bottom, (Map _ | Bottom) | Map _, Bottom -> Bottom
+
+
+(* ----- Substitute --------------------------------------------------------- *)
+
+(** Auxiliary function that substitutes the data right-hand part of a
+    dependency by a pre-existing From state. The returned result is a Deps.t:
+    the data part will be the data part of the complete result, the indirect
+    part will be added to the indirect part of the final result. *)
+(* This function iterates simultaneously on a From memory, and on a zone.
+   It is cached. The definitions below are used to call the function that
+   does the recursive descent. *)
+let substitute_data_deps =
+  (* Nothing left to substitute, return z unchanged *)
+  let empty_right z = Deps.data z in
+  (* Zone to substitute is empty *)
+  let empty_left _ = Deps.bottom in
+  (* [b] is in the zone and substituted. Rewrite appropriately *)
+  let both b itvs offsm = find_precise_loffset offsm b itvs in
+  let join = Deps.join in
+  let empty = Deps.bottom in
+  let cache = Hptmap_sig.PersistentCache "From_memory.substitute_data" in
+  let f_map =
+    Zone.fold2_join_heterogeneous
+      ~cache ~empty_left ~empty_right ~both ~join ~empty
+  in
+  fun call_site_froms z ->
+    match call_site_froms with
+    | Bottom -> Deps.bottom
+    | Top -> Deps.top
+    | Map m ->
+      try f_map z (shape m)
+      with Abstract_interp.Error_Top -> Deps.top
+
+(** Auxiliary function that substitutes the indirect right-hand part of a
+    dependency by a pre-existing From state. The returned result is a zone,
+    which will be added to the indirect part of the final result. *)
+let substitute_indirect_deps =
+  (* Nothing left to substitute, z is directly an indirect dependency *)
+  let empty_right z = z in
+  (* Zone to substitute is empty *)
+  let empty_left _ = Zone.bottom in
+  let both b itvs offsm =
+    (* Both the found data and indirect dependencies are computed for indirect
+       dependencies: merge to a single zone *)
+    Deps.to_zone (find_precise_loffset offsm b itvs)
+  in
+  let join = Zone.join in
+  let empty = Zone.bottom in
+  let cache = Hptmap_sig.PersistentCache "From_memory.substitute_indirect" in
+  let f_map =
+    Zone.fold2_join_heterogeneous
+      ~cache ~empty_left ~empty_right ~both ~join ~empty
+  in
+  fun call_site_froms z ->
+    match call_site_froms with
+    | Bottom -> Zone.bottom
+    | Top -> Zone.top
+    | Map m ->
+      try f_map z (shape m)
+      with Abstract_interp.Error_Top -> Zone.top
+
+let substitute call_site_froms deps =
+  let open Deps in
+  let { data; indirect } = deps in
+  (* depending directly on an indirect dependency -> indirect,
+     depending indirectly on a direct dependency  -> indirect *)
+  let dirdeps = substitute_data_deps call_site_froms data in
+  let inddeps = substitute_indirect_deps call_site_froms indirect in
+  let dir = dirdeps.data in
+  let ind = Zone.(join dirdeps.indirect inddeps) in
+  { data = dir; indirect = ind }
+
+
+(* ----- Dependency of returned values -------------------------------------- *)
+
+type return = Deps.t
+
+let default_return = Deps.bottom
+
+let top_return = Deps.top
+
+let add_to_return ?start:(_start=0) ~size:_size ?(m=default_return) v =
+  Deps.join m v
+(*
+    let start = Ival.of_int start in
+    let itvs = Int_Intervals.from_ival_size start size in
+    LOffset.add_iset ~exact:true itvs (DepsOrUnassigned.AssignedFrom v) m
+*)
+
+let top_return_size size =
+  add_to_return ~size Deps.top
+
+let collapse_return x = x
+
+
+(* ----- Pretty-printing ---------------------------------------------------- *)
+
+let () = imprecise_write_msg := "dependencies to update"
+
+let pretty_skip = function
+  | DepsOrUnassigned.DepsBottom -> true
+  | DepsOrUnassigned.Unassigned -> true
+  | DepsOrUnassigned.AssignedFrom _ -> false
+  | DepsOrUnassigned.MaybeAssignedFrom _ -> false
+
+let pretty =
+  pretty_generic_printer
+    ~skip_v:pretty_skip ~pretty_v:DepsOrUnassigned.pretty ~sep:"FROM" ()
+
+let pretty_ind_data =
+  pretty_generic_printer
+    ~skip_v:pretty_skip ~pretty_v:DepsOrUnassigned.pretty_precise ~sep:"FROM"
+    ()
+
+(** same as pretty, but uses the type of the function to output more
+    precise information.
+    @raise Error if the given type is not a function type
+*)
+let pretty_with_type ~indirect typ fmt assigns =
+  let Eva.Assigns.{ memory; return } = assigns in
+  let (rt_typ,_,_,_) = Cil.splitFunctionType typ in
+  if is_bottom memory
+  then Format.fprintf fmt
+      "@[NON TERMINATING - NO EFFECTS@]"
+  else
+    let map_pretty =
+      if indirect
+      then pretty_ind_data
+      else pretty
+    in
+    if Cil.isVoidType rt_typ
+    then begin
+      if is_empty memory
+      then Format.fprintf fmt "@[NO EFFECTS@]"
+      else map_pretty fmt memory
+    end
+    else
+      let pp_space fmt =
+        if not (is_empty memory) then
+          Format.fprintf fmt "@ "
+      in
+      Format.fprintf fmt "@[<v>%a%t@[\\result FROM @[%a@]@]@]"
+        map_pretty memory pp_space
+        (if indirect then Deps.pretty_precise else Deps.pretty) return
+
+let pretty_with_type_indirect = pretty_with_type ~indirect:true
+let pretty_with_type = pretty_with_type ~indirect:false
diff --git a/src/plugins/from/from_memory.mli b/src/plugins/from/from_memory.mli
new file mode 100644
index 0000000000000000000000000000000000000000..8e6f08e5905a93ea68fc7f20a0c284b27eaa0f02
--- /dev/null
+++ b/src/plugins/from/from_memory.mli
@@ -0,0 +1,78 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(** Utilitary functions on the {!Eva.Froms.Memory.t} type. *)
+
+type t = Eva.Assigns.Memory.t
+
+val top : t
+
+val bind_var: Cil_types.varinfo -> Eva.Deps.t -> t -> t
+val unbind_var: Cil_types.varinfo -> t -> t
+
+val map : (Eva.Deps.t -> Eva.Deps.t) -> t -> t
+
+val compose : t -> t -> t
+(** Sequential composition. See {!DepsOrUnassigned.compose}. *)
+
+val substitute : t -> Eva.Deps.t -> Eva.Deps.t
+(** [substitute m d] applies [m] to [d] so that any dependency in [d] is
+    expressed using the dependencies already present in [m]. For example,
+    [substitute 'x From y' 'x'] returns ['y']. *)
+
+(** {2 Dependencies for [\result]} *)
+
+type return = Eva.Deps.t
+(* Currently, this type is equal to [Eva.Deps.t]. However, some of the functions
+   below are more precise, and will be more useful when 'return' is
+   represented by a precise offsetmap. This would also require changing
+   the type of the [deps.return] of type Eva.Froms.t. *)
+
+(** Default value to use for storing the dependencies of [\result] *)
+val default_return: return
+
+(** Completely imprecise return *)
+val top_return: return
+
+(** Completely imprecise return of the given size *)
+val top_return_size: Int_Base.t -> return
+
+(** Add some dependencies to [\result], between bits [start] and
+    [start+size-1], to the [Deps.t] value; default value for [start] is 0.
+    If [m] is specified, the dependencies are added to it. Otherwise,
+    {!default_return} is used. *)
+val add_to_return:
+  ?start:int -> size:Int_Base.t -> ?m:return -> Eva.Deps.t -> return
+
+val collapse_return: return -> Eva.Deps.t
+
+(** {2 Pretty-printing} *)
+
+(** Display dependencies of a function, using the function's type to improve
+    readability *)
+val pretty_with_type: Cil_types.typ -> Eva.Assigns.t Pretty_utils.formatter
+
+(** Display dependencies of a function, using the function's type to improve
+    readability, separating direct and indirect dependencies *)
+val pretty_with_type_indirect:
+  Cil_types.typ ->
+  Eva.Assigns.t Pretty_utils.formatter
diff --git a/src/plugins/from/from_register.ml b/src/plugins/from/from_register.ml
index df9beea20f8bd31766c25136d413db7fb06a2b9f..680ad06894204d0f75bc0a4c0a372010ae9776f5 100644
--- a/src/plugins/from/from_register.ml
+++ b/src/plugins/from/from_register.ml
@@ -24,7 +24,7 @@ open Cil_types
 
 let pretty_with_indirect fmt v =
   let deps = Functionwise.get v in
-  Function_Froms.pretty_with_type_indirect (Kernel_function.get_type v) fmt deps
+  From_memory.pretty_with_type_indirect (Kernel_function.get_type v) fmt deps
 
 let display fmtopt =
   Option.iter (fun fmt -> Format.fprintf fmt "@[<v>") fmtopt;
@@ -138,8 +138,8 @@ let print_calldeps () =
        From_parameters.printf ~header
          "@[  %a@]"
          ((if From_parameters.ShowIndirectDeps.get ()
-           then Function_Froms.pretty_with_type_indirect
-           else Function_Froms.pretty_with_type) typ)
+           then From_memory.pretty_with_type_indirect
+           else From_memory.pretty_with_type) typ)
          d);
   From_parameters.feedback "====== END OF CALLWISE DEPENDENCIES ======"
 
diff --git a/src/plugins/from/functionwise.ml b/src/plugins/from/functionwise.ml
index 484fbdba5b54ddcd4643bbba1f7f7e62d396d3e6..972c2749f7f19c2ea85b81ed1339a71acf19e48b 100644
--- a/src/plugins/from/functionwise.ml
+++ b/src/plugins/from/functionwise.ml
@@ -25,7 +25,7 @@ open Locations
 
 module Tbl =
   Kernel_function.Make_Table
-    (Function_Froms)
+    (Eva.Assigns)
     (struct
       let name = "Functionwise dependencies"
       let size = 17
@@ -53,7 +53,7 @@ module To_Use = struct
     Eva.Logic_inout.accept_base ~formals:false ~locals:false kf
 
   let cleanup kf froms =
-    if Function_Froms.Memory.is_bottom froms.Function_Froms.deps_table
+    if Eva.Assigns.Memory.is_bottom froms.Eva.Assigns.memory
     then froms
     else
       let accept_base =
@@ -75,11 +75,9 @@ module To_Use = struct
           zone_substitution x
         with Abstract_interp.Error_Top -> Zone.top
       in
-      let map_zone = Function_Froms.Deps.map zone_substitution in
-      let subst = Function_Froms.DepsOrUnassigned.subst map_zone  in
-      let open Function_Froms in
-      { deps_table = Memory.map subst froms.deps_table;
-        deps_return = Deps.map zone_substitution froms.deps_return;
+      let map_zone = Eva.Deps.map zone_substitution in
+      { memory = From_memory.map map_zone froms.memory;
+        return = Eva.Deps.map zone_substitution froms.return;
       }
 
   let cleanup_and_save kf froms =
@@ -111,7 +109,7 @@ let is_computed = Tbl.mem
 let get = To_Use.memo
 
 let pretty fmt v =
-  Function_Froms.pretty_with_type (Kernel_function.get_type v) fmt (get v)
+  From_memory.pretty_with_type (Kernel_function.get_type v) fmt (get v)
 
 (*
 Local Variables:
diff --git a/src/plugins/from/functionwise.mli b/src/plugins/from/functionwise.mli
index 2f81e01120c45eb4fb58e1d242dd885c6f760136..5f0ba60bace3c7b9c29a4dfa3c4506482b3b25a7 100644
--- a/src/plugins/from/functionwise.mli
+++ b/src/plugins/from/functionwise.mli
@@ -29,5 +29,5 @@ val self : State.t
 val compute : kernel_function -> unit
 val compute_all : unit -> unit
 val is_computed : kernel_function -> bool
-val get : Cil_types.kernel_function -> Function_Froms.froms
+val get : Cil_types.kernel_function -> Eva.Assigns.t
 val pretty : Format.formatter -> kernel_function -> unit
diff --git a/src/plugins/inout/operational_inputs.ml b/src/plugins/inout/operational_inputs.ml
index d12bc75efab318688cbc438b023c28003b81d836..74d506662ca0d90cfad653097b3140edffa329d9 100644
--- a/src/plugins/inout/operational_inputs.ml
+++ b/src/plugins/inout/operational_inputs.ml
@@ -514,26 +514,24 @@ let get_external_aux ?stmt kf =
         r
       else !ref_get_external kf
 
-let extract_inout_from_froms froms =
-  let open Function_Froms in
-  let {deps_return; deps_table } = froms in
-  let in_return = Deps.to_zone deps_return in
+let extract_inout_from_froms assigns =
+  let Eva.Assigns.{ return = deps_return; memory = deps_table } = assigns in
+  let in_return = Eva.Deps.to_zone deps_return in
   let in_, out_ =
     match deps_table with
-    | Memory.Top -> Zone.top, Zone.top
-    | Memory.Bottom -> Zone.bottom, Zone.bottom
-    | Memory.Map m ->
+    | Top -> Zone.top, Zone.top
+    | Bottom -> Zone.bottom, Zone.bottom
+    | Map m ->
       let aux_from out in_ (acc_in,acc_out as acc) =
-        let open DepsOrUnassigned in
         (* Skip zones fully unassigned, they are not really port of the
            dependencies, but just present in the offsetmap to avoid "holes" *)
-        match in_ with
+        match (in_ : Eva.Assigns.DepsOrUnassigned.t) with
         | DepsBottom | Unassigned -> acc
         | AssignedFrom in_ | MaybeAssignedFrom in_ ->
-          Zone.join acc_in (Deps.to_zone in_),
+          Zone.join acc_in (Eva.Deps.to_zone in_),
           Zone.join acc_out out
       in
-      Memory.fold aux_from m (Zone.bottom, Zone.bottom)
+      Eva.Assigns.Memory.fold aux_from m (Zone.bottom, Zone.bottom)
   in
   (Zone.join in_return in_), out_
 
diff --git a/src/plugins/pdg/build.ml b/src/plugins/pdg/build.ml
index 6db271d345e2405a1761dc0f0ea9be74ad26a0d8..c2268d86d6f084325f3b5cf096c294eb2e2749d5 100644
--- a/src/plugins/pdg/build.ml
+++ b/src/plugins/pdg/build.ml
@@ -566,7 +566,7 @@ let finalize_pdg pdg from_opt =
      (* TODO : also add the nodes for the other from ! *)
      let state = match last_state with Some s -> s | None -> assert false in
      let process_out out deps s =
-       let open Function_Froms.DepsOrUnassigned in
+       let open Eva.Assigns.DepsOrUnassigned in
        if (equal Unassigned deps)
        then s
        else
@@ -574,24 +574,23 @@ let finalize_pdg pdg from_opt =
          let default = may_be_unassigned deps in
          add_from pdg state s out (default, from_out)
      in
-     let from_table = froms.Function_Froms.deps_table in
+     let from_table = froms.Eva.Assigns.memory in
      let new_state =
-       if Function_Froms.Memory.is_bottom from_table then
+       if Eva.Assigns.Memory.is_bottom from_table then
          Pdg_state.bottom
        else
          let new_state =
            match from_table with
-           | Function_Froms.Memory.Top ->
+           | Top ->
              process_out
-               Locations.Zone.top Function_Froms.DepsOrUnassigned.top state
-           | Function_Froms.Memory.Map m ->
-             Function_Froms.Memory.fold_fuse_same process_out m state
-           | Function_Froms.Memory.Bottom -> assert false (* checked above *)
+               Locations.Zone.top Eva.Assigns.DepsOrUnassigned.top state
+           | Map m ->
+             Eva.Assigns.Memory.fold_fuse_same process_out m state
+           | Bottom -> assert false (* checked above *)
          in
          if not (Kernel_function.returns_void pdg.fct) then begin
-           let from0 = froms.Function_Froms.deps_return in
-           let deps_ret = Function_Froms.Memory.collapse_return from0 in
-           let deps_ret = Function_Froms.Deps.to_zone deps_ret in
+           let deps_ret = froms.Eva.Assigns.return in
+           let deps_ret = Eva.Deps.to_zone deps_ret in
            ignore
              (create_fun_output_node pdg (Some new_state) deps_ret)
          end;
@@ -648,49 +647,44 @@ let process_args pdg st stmt argl =
     and [new_state] the state to modify.
  * Process call outputs (including returned value) *)
 let call_outputs  pdg state_before_call state_with_inputs stmt
-    lvaloption froms fct_dpds =
+    lvaloption assigns fct_dpds =
   (* obtain inputs from state_with_inputs
      to avoid mixing in and out *)
-  let froms_deps_return = froms.Function_Froms.deps_return in
-  let from_table = froms.Function_Froms.deps_table in
+  let Eva.Assigns.{ return = return_deps; memory = memory_deps } = assigns in
   let print_outputs fmt =
     Format.fprintf fmt "call outputs  : %a"
-      Function_Froms.Memory.pretty from_table;
+      Eva.Assigns.Memory.pretty memory_deps;
     if not (lvaloption = None) then
       Format.fprintf fmt "\t and \\result %a@."
-        Function_Froms.Deps.pretty froms_deps_return
+        Eva.Deps.pretty return_deps
   in
   debug "%t" print_outputs;
 
   let process_out out deps state =
-    if Function_Froms.DepsOrUnassigned.(equal Unassigned deps) then
+    if Eva.Assigns.DepsOrUnassigned.(equal Unassigned deps) then
       state
     else
-      let from_out = Function_Froms.DepsOrUnassigned.to_zone deps in
-      let default = Function_Froms.DepsOrUnassigned.may_be_unassigned deps in
+      let from_out = Eva.Assigns.DepsOrUnassigned.to_zone deps in
+      let default = Eva.Assigns.DepsOrUnassigned.may_be_unassigned deps in
       process_call_output
         pdg state_with_inputs state stmt out default from_out fct_dpds
   in
-  if Function_Froms.Memory.is_bottom from_table then
+  if Eva.Assigns.Memory.is_bottom memory_deps then
     Pdg_state.bottom
   else
     let state_with_outputs =
-      let open Function_Froms in
-      match from_table with
-      | Memory.Top ->
+      match memory_deps with
+      | Top ->
         process_out
-          Locations.Zone.top DepsOrUnassigned.top state_before_call
-      | Memory.Bottom -> assert false (* checked above *)
-      | Memory.Map m ->
-        Memory.fold_fuse_same process_out m state_before_call
+          Locations.Zone.top Eva.Assigns.DepsOrUnassigned.top state_before_call
+      | Bottom -> assert false (* checked above *)
+      | Map m ->
+        Eva.Assigns.Memory.fold_fuse_same process_out m state_before_call
     in
     match lvaloption with
     | None -> state_with_outputs
     | Some lval ->
-      let r_dpds =
-        Function_Froms.Memory.collapse_return froms_deps_return
-      in
-      let r_dpds = Function_Froms.Deps.to_zone r_dpds in
+      let r_dpds = Eva.Deps.to_zone return_deps in
       let (l_loc, exact, l_dpds, l_decl) = get_lval_infos lval stmt in
       process_call_return
         pdg
diff --git a/src/plugins/scope/zones.ml b/src/plugins/scope/zones.ml
index 03a5d675411bf205295840b9b9102a57a8931f6f..3c2ecaa401f5672483771b620db63c95b9c8dea9 100644
--- a/src/plugins/scope/zones.ml
+++ b/src/plugins/scope/zones.ml
@@ -79,9 +79,8 @@ let process_call_res data stmt lvaloption froms =
   let data = match lvaloption with
     | None -> false, data
     | Some lval ->
-      let ret_dpds = froms.Function_Froms.deps_return in
-      let r_dpds = Function_Froms.Memory.collapse_return ret_dpds in
-      let r_dpds = Function_Froms.Deps.to_zone r_dpds in
+      let ret_dpds = froms.Eva.Assigns.return in
+      let r_dpds = Eva.Deps.to_zone ret_dpds in
       let l_dpds, exact, l_zone =
         get_lval_zones ~for_writing:true  stmt lval in
       compute_new_data data l_zone l_dpds exact r_dpds
@@ -92,10 +91,10 @@ let process_call_res data stmt lvaloption froms =
  * Moreover, we need to add the part of [data_after] that has not been
  * modified for sure. *)
 let process_froms data_after froms =
-  let from_table = froms.Function_Froms.deps_table in
+  let from_table = froms.Eva.Assigns.memory in
   let process_out_call out deps (to_prop, used, new_data) =
-    let out_dpds = Function_Froms.DepsOrUnassigned.to_zone deps in
-    let default = Function_Froms.DepsOrUnassigned.may_be_unassigned deps in
+    let out_dpds = Eva.Assigns.DepsOrUnassigned.to_zone deps in
+    let default = Eva.Assigns.DepsOrUnassigned.may_be_unassigned deps in
     let exact = not default in
     (* be careful to compare out with data_after and not new_data *)
     if (Data.intersects data_after out) then
@@ -113,12 +112,12 @@ let process_froms data_after froms =
   let used = false in (* is the call needed ? *)
   let to_prop, used, new_data =
     match from_table with
-    | Function_Froms.Memory.Bottom -> to_prop, used, new_data
-    | Function_Froms.Memory.Top ->
-      let v = Function_Froms.DepsOrUnassigned.top in
+    | Bottom -> to_prop, used, new_data
+    | Top ->
+      let v = Eva.Assigns.DepsOrUnassigned.top in
       process_out_call Locations.Zone.top v (to_prop, used, new_data)
-    | Function_Froms.Memory.Map m ->
-      Function_Froms.Memory.fold process_out_call m (to_prop, used, new_data)
+    | Map m ->
+      Eva.Assigns.Memory.fold process_out_call m (to_prop, used, new_data)
   in let data = Data.merge to_prop new_data in
   (used, data)