diff --git a/src/plugins/eva/domains/cvalue/builtins_memory.ml b/src/plugins/eva/domains/cvalue/builtins_memory.ml
index 85181c1a4218956a3ac6f9d36fa17d21cb6108dd..9f0caf109ce2a5d48016d9c8066bf10c30d40fbf 100644
--- a/src/plugins/eva/domains/cvalue/builtins_memory.ml
+++ b/src/plugins/eva/domains/cvalue/builtins_memory.ml
@@ -74,25 +74,8 @@ let frama_c_offset _state = function
 
 let () = register_builtin "Frama_C_offset" frama_c_offset
 
-exception Memcpy_result of (Cvalue.Model.t * Assigns.t * Zone.t)
 
-exception Indeterminate of V_Or_Uninitialized.t
-
-(*  Called by the [memcpy] builtin. Warns when the offsetmap contains
-    an indeterminate value, when the imprecision category is enabled *)
-let memcpy_check_indeterminate_offsetmap offsm =
-  if Self.is_debug_key_enabled dkey then
-    try
-      let aux_offset _ (v, _, _) =
-        match v with
-        | V_Or_Uninitialized.C_init_noesc _ -> ()
-        | _ -> raise (Indeterminate v)
-      in
-      V_Offsetmap.iter aux_offset offsm
-    with Indeterminate v ->
-      Self.debug ~current:true ~dkey ~once:true
-        "@[In memcpy@ builtin:@ precise@ copy of@ indeterminate@ values %a@]%t"
-        V_Or_Uninitialized.pretty v Eva_utils.pp_callstack
+let plevel = Parameters.ArrayPrecisionLevel.get
 
 (* Create a dependency [\from arg_n] where n is the nth argument of the
    currently called function. *)
@@ -103,206 +86,209 @@ let deps_nth_arg n =
     Deps.add_data Deps.bottom (Locations.zone_of_varinfo vi)
   with Failure _ -> Kernel.fatal "%d arguments expected" n
 
+(* -------------------------------------------------------------------------- *)
+(*                             Memcpy & Memmove                               *)
+(* -------------------------------------------------------------------------- *)
+
+(*  Warns when the value is indeterminate. *)
+let warn_indeterminate_value ?(precise = false) = function
+  | V_Or_Uninitialized.C_init_noesc _ -> ()
+  | _ ->
+    Self.result ~dkey ~current:true ~once:true
+      "@[In memcpy builtin:@ %sprecise copy@ of indeterminate values.@]%t"
+      (if precise then "" else "im") Eva_utils.pp_callstack
+
+(*  Warns when the offsetmap contains an indeterminate value. *)
+let check_indeterminate_offsetmap offsm =
+  if Self.is_debug_key_enabled dkey then
+    let warn _ (v, _, _) = warn_indeterminate_value ~precise:true v in
+    V_Offsetmap.iter warn offsm
+
+(* Adds \from dependency from [src_loc] to [dst_loc]. *)
+let add_deps_loc ~exact ~src_loc ~dst_loc deps_table =
+  let src_zone = Locations.(enumerate_valid_bits Read src_loc) in
+  let dst_zone = Locations.(enumerate_valid_bits Write dst_loc) in
+  let deps = Deps.(add_data bottom src_zone) in
+  Assigns.Memory.add_binding ~exact deps_table dst_zone deps
+
+(* Adds a sure \from dependency of size [size] from [src] to [dst].
+   Also returns the written zone if it is a singleton. *)
+let add_sure_deps ~size ~src ~dst (deps_table, sure_output) =
+  let size = Int_Base.inject size in
+  let src_loc = Locations.make_loc src size in
+  let dst_loc = Locations.make_loc dst size in
+  let exact = Location_Bits.cardinal_zero_or_one dst in
+  let deps_table = add_deps_loc ~exact ~src_loc ~dst_loc deps_table in
+  let dst_zone = Locations.(enumerate_valid_bits Write dst_loc) in
+  let sure_zone = if exact then dst_zone else Zone.bottom in
+  deps_table, Zone.join sure_zone sure_output
+
+(* Copy the offsetmap of size [size] from [src] to [dst] in [state]. *)
+let copy_offsetmap ~exact ~size ~src ~dst ~dst_lval state =
+  match Cvalue.Model.copy_offsetmap src size state with
+  | `Bottom -> Cvalue.Model.bottom
+  | `Value offsetmap ->
+    check_indeterminate_offsetmap offsetmap;
+    let prefix = "Builtin memcpy" in
+    Cvalue_transfer.warn_imprecise_offsm_write ~prefix dst_lval offsetmap;
+    Cvalue.Model.paste_offsetmap ~from:offsetmap ~dst_loc:dst ~size ~exact state
+
+(* Returns the min and max size of a copy from an Ival.t. *)
+let min_max_size size =
+  let min, max = Ival.min_and_max size in
+  let min = Option.fold min ~none:Int.zero ~some:Int.(max zero) in
+  let max_max = Bit_utils.max_bit_size () in
+  let max = Option.fold max ~none:max_max ~some:Int.(max zero) in
+  min, max
+
+(* Computes the remaining [src], [dst] and [size] to be copied after [factor]
+   bits have already been copied. *)
+let shift ~factor:i src dst size =
+  let new_size = Int.sub size i in
+  let ival = Ival.inject_singleton i in
+  let new_src = Location_Bits.shift ival src in
+  let new_dst = Location_Bits.shift ival dst in
+  new_src, new_dst, new_size
+
+(* Performs the remaining copy from [src] to [dst] for all other possible [size],
+   after the copy for the minimum size has already been done.
+   Iterates on all possible values of [size] (after the first), and does the
+   copy for [previous_size..size]. *)
+let copy_remaining_size_by_size ~src ~dst ~dst_lval ~size state =
+  let exception Result of Cvalue.Model.t in
+  let do_size size (state, previous_size) =
+    (* First iteration: this copy has already been performed, skip. *)
+    if Int.(equal previous_size minus_one)
+    then state, size
+    else
+      (* Copy data between [previous_size] and [size]. *)
+      let src, dst, size = shift ~factor:previous_size src dst size in
+      (* [exact] is false as all these copies may not happen according to the
+          concrete value of [size]. *)
+      let exact = false in
+      let new_state = copy_offsetmap ~exact ~size ~src ~dst ~dst_lval state in
+      (* If this copy failed, the current size is completely invalid, and so
+         will be the following ones. Stop now with the previous state. *)
+      if not (Cvalue.Model.is_reachable new_state) then raise (Result state);
+      new_state, size
+  in
+  try fst (Ival.fold_int do_size size (state, Int.minus_one))
+  with Result state -> state
+
+(* Copy the value at location [src_loc] to location [dst_loc] in [state]. *)
+let imprecise_copy ~src_loc ~dst_loc ~dst_lval state =
+  Self.debug ~dkey ~once:true
+    ~current:true "In memcpy builtin: too many sizes to enumerate, \
+                   possible loss of precision";
+  (* conflate_bottom:false as we want to copy padding bits *)
+  let v = Model.find_indeterminate ~conflate_bottom:false state src_loc in
+  warn_indeterminate_value v;
+  let value = Cvalue.V_Or_Uninitialized.get_v v in
+  let prefix = "Builtin memcpy" in
+  Cvalue_transfer.warn_imprecise_write ~prefix dst_lval dst_loc value;
+  let new_state =
+    Cvalue.Model.add_indeterminate_binding ~exact:false state dst_loc v
+  in
+  (* Beware that all sizes may be invalid, in which case [add_binding] will
+     return [bottom]. In this case, return the previously computed state *)
+  if Model.is_reachable new_state then new_state else state
+
+(* Creates the location {loc + [0..size]} of size char. *)
+let char_location loc max_size =
+  let size_char = Bit_utils.sizeofchar () in
+  let max = Int.sub max_size size_char in
+  (* Use ranges modulo char_bits to read and write byte-by-byte, which can
+     preserve some precision.*)
+  let shift =
+    Ival.inject_interval ~min:(Some Int.zero) ~max:(Some max)
+      ~rem:Int.zero ~modu:size_char
+  in
+  let loc = Location_Bits.shift shift loc in
+  make_loc loc (Int_Base.inject size_char)
+
+let compute_memcpy ~dst_lval ~dst ~src ~size state =
+  let size_min, size_max = min_max_size size in
+  (* Empty \from dependencies and sure output *)
+  let empty_deps = Assigns.Memory.empty, Zone.bottom in
+  (* First step: copy the bits we are sure to copy, i.e. for the minimum size
+     [size_min] if it is not zero. *)
+  let state, (deps_table, written_zone) =
+    if Int.gt size_min Int.zero
+    then
+      let state =
+        copy_offsetmap ~exact:true ~size:size_min ~src ~dst ~dst_lval state
+      in
+      (* If the copy succeeded, update \from dependencies and sure output. *)
+      if Cvalue.Model.is_reachable state
+      then state, add_sure_deps ~size:size_min ~src ~dst empty_deps
+      else state, empty_deps
+    else state, empty_deps
+  in
+  (* Stop here if the first copy failed or if there is nothing more to copy. *)
+  if not (Cvalue.Model.is_reachable state) || Int.equal size_min size_max
+  then state, deps_table, written_zone
+  else
+    (* Second step: size is imprecise, we will now copy some bits that we are
+       not sure to copy, for all possible sizes greater than [size_min]. *)
+    (* [size_min] bits have already been copied. *)
+    let src_shift, dst_shift, size_diff = shift ~factor:size_min src dst size_max in
+    (* Remaining locations to be read/written, as locations of char size. *)
+    let src_loc = char_location src_shift size_diff
+    and dst_loc = char_location dst_shift size_diff in
+    let deps_table = add_deps_loc ~exact:false ~src_loc ~dst_loc deps_table in
+    (* If there is sufficiently few possible sizes, iter on each possible size
+       via [copy_remaining_size_by_size]. Otherwise, use [imprecise_copy] in one
+       step to read the entire range src+(size_min..size_max-1) as one byte, and
+       write the result as one byte in dst+(size_min..size_max-1). *)
+    let state =
+      if Ival.cardinal_is_less_than size (plevel () / 10)
+      then copy_remaining_size_by_size ~src ~dst ~dst_lval ~size state
+      else imprecise_copy ~src_loc ~dst_loc ~dst_lval state
+    in
+    state, deps_table, written_zone
 
-let frama_c_memcpy name state actuals =
-  let prefix = "Builtin " ^ name in
-  let compute (exp_dst,dst_bytes) (_exp_src,src_bytes) (_exp_size,size) =
-    let dst_lval = lval_of_address exp_dst in
-    let plevel = Parameters.ArrayPrecisionLevel.get() in
+let frama_c_memcpy _name state actuals =
+  match actuals with
+  | [(dst_exp, dst_cvalue); (_src_exp, src_cvalue); (_size_exp, size_cvalue)] ->
+    let dst_lval = lval_of_address dst_exp in
     let size =
-      try Cvalue.V.project_ival size
+      try Cvalue.V.project_ival size_cvalue
       with Cvalue.V.Not_based_on_null -> Ival.top (* TODO: use size_t *)
     in
-    let min,max = Ival.min_and_max size in
-    let min = match min with None -> Int.zero | Some m -> Int.max m Int.zero in
-    let char_bits = Bit_utils.sizeofchar() in
-    let size_min = Int.mul char_bits min in
-    let src = loc_bytes_to_loc_bits src_bytes in
-    let dst = loc_bytes_to_loc_bits dst_bytes in
-    (* Remove read-only destinations *)
-    let dst_bits =
+    (* Convert locations and size into bits. *)
+    let size = Ival.scale (Bit_utils.sizeofchar ()) size in
+    let src = loc_bytes_to_loc_bits src_cvalue in
+    let dst = loc_bytes_to_loc_bits dst_cvalue in
+    (* Remove read-only destinations. *)
+    let dst =
       Location_Bits.filter_base (fun b -> not (Base.is_read_only b)) dst
     in
-    let deps_return = deps_nth_arg 0 in
-    let empty_cfrom =
-      Assigns.{ memory = Memory.empty; return = deps_return }
+    (* Do the copy. *)
+    let state, memory, sure_output =
+      compute_memcpy ~dst_lval ~dst ~src ~size state
     in
-    let precise_copy state =
-      (* First step: copy the bytes we are sure to copy *)
-      if Int.gt size_min Int.zero then begin
-        match Cvalue.Model.copy_offsetmap src size_min state with
-        | `Bottom -> (* Read failed. Source was invalid, but must be read, we
-                        stop the analysis *)
-          raise (Memcpy_result (Cvalue.Model.bottom,empty_cfrom,Zone.bottom))
-        | `Value offsetmap ->
-          let loc_dst = make_loc dst_bits (Int_Base.inject size_min) in
-          memcpy_check_indeterminate_offsetmap offsetmap;
-          (* Read succeeded. We write the result *)
-          let loc_src = make_loc src (Int_Base.inject size_min) in
-          Cvalue_transfer.warn_imprecise_offsm_write ~prefix dst_lval offsetmap;
-          let new_state =
-            Cvalue.Model.paste_offsetmap
-              ~from:offsetmap ~dst_loc:dst_bits ~size:size_min ~exact:true state
-          in
-          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 = 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 =
-              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, Assigns.Memory.empty, Zone.bottom)
-    in
-    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
-      let dst = Location_Bits.shift size_min_ival dst_bits in
-      let src = Location_Bits.shift size_min_ival src in
-      (* Size remaining to copy imprecisely *)
-      let diff = match max with
-        | Some max -> Some (Int.mul char_bits (Int.pred (Int.sub max min)))
-        | None -> None
-      in
-      (* Imprecise locations remaining to be read/written. By using ranges
-         modulo char_bits, we read and write byte-by-byte, which can preserve
-         some precision in the fallback. If sufficiently few sizes need
-         to be copied, we use a more precise method (see do_size below).
-         However, in all cases, those locations are used to compute the
-         read and written bits. *)
-      let range =
-        Ival.inject_interval ~min:(Some Int.zero) ~max:diff
-          ~rem:Int.zero ~modu:char_bits
-      in
-      let size_char = Int_Base.inject char_bits in
-      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 zone_src = enumerate_valid_bits Locations.Read loc_src in
-        let zone_dst = enumerate_valid_bits Locations.Write  loc_dst in
-        let deps = Deps.data zone_src in
-        let memory =
-          Assigns.Memory.add_binding ~exact:false precise_assigns zone_dst deps
-        in
-        Assigns.{ memory; return = deps_return }
-      in
-      try
-        (* We try to iter on all the slices inside the value of slice.
-           If there are more too many of them, we use a backup solution *)
-        ignore (Ival.cardinal_less_than size (plevel / 10));
-        let do_size s (dst, src, prev_size, state) =
-          let s = Int.mul char_bits s in
-          let diff = Int.sub s prev_size in
-          if Int.equal s size_min then
-            (* occurs the very first time. This copy has already been
-               performed at the beginning, skip *)
-            (dst, src, s, state)
-          else begin
-            (* Copy data between prev_size and s *)
-            match Cvalue.Model.copy_offsetmap src diff state with
-            | `Bottom ->
-              (* This size is completely invalid. The following ones
-                 will also be invalid, stop now with current result *)
-              raise (Memcpy_result (state,c_from,sure_zone))
-            | `Value offsetmap ->
-              memcpy_check_indeterminate_offsetmap offsetmap;
-              Cvalue_transfer.warn_imprecise_offsm_write ~prefix dst_lval offsetmap;
-              let new_state =
-                Cvalue.Model.paste_offsetmap
-                  ~from:offsetmap ~dst_loc:dst ~size:diff ~exact:false state
-              in
-              if Cvalue.Model.is_reachable new_state then
-                let diffi = Ival.inject_singleton diff in
-                let dst = Location_Bits.shift diffi dst in
-                let src = Location_Bits.shift diffi src in
-                (dst, src, s, new_state)
-              else (* As above, invalid size, this time for the destination.
-                      We stop there *)
-                raise (Memcpy_result (state,c_from,sure_zone))
-          end
-        in
-        let _, _, _, state =
-          Ival.fold_int do_size size (dst, src, Int.zero, new_state)
-        in
-        raise (Memcpy_result (state,c_from,sure_zone))
-      with
-      | Abstract_interp.Not_less_than ->
-        Self.debug ~dkey ~once:true
-          ~current:true "In memcpy builtin: too many sizes to enumerate, \
-                         possible loss of precision";
-        (* Too many slices in the size. We read the entire range
-           src+(size_min..size_max-1) in one step, as one byte, and write the
-           result as one byte in dst+(size_min..size_max-1) *)
-        let v = (* conflate_bottom=false: we want to copy padding bits *)
-          Model.find_indeterminate ~conflate_bottom:false state loc_src
-        in
-        begin match v with
-          | V_Or_Uninitialized.C_init_noesc _ -> ()
-          | _ -> Self.result ~dkey ~current:true ~once:true
-                   "@[In memcpy@ builtin:@ imprecise@ copy of@ indeterminate@ values@]%t"
-                   Eva_utils.pp_callstack
-        end;
-        let value = Cvalue.V_Or_Uninitialized.get_v v in
-        Cvalue_transfer.warn_imprecise_write ~prefix dst_lval loc_dst value;
-        let updated_state =
-          Cvalue.Model.add_indeterminate_binding
-            ~exact:false new_state loc_dst v
-        in
-        (* Beware that all the imprecise sizes may be invalid, in which case
-           [add_binding] will return [Bottom]. In this case, return the
-           previously computed state *)
-        if Model.is_reachable updated_state then
-          raise (Memcpy_result (updated_state,c_from,sure_zone))
-        else
-          raise (Memcpy_result (new_state,c_from,sure_zone))
-    in
-    try
-      if Ival.is_zero size then
-        raise (Memcpy_result (state, empty_cfrom, Zone.bottom));
-      let (precise_state,precise_assigns,sure_zone) = precise_copy state in
-      if Option.fold ~none:false ~some:(Int.equal min) max then
-        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_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_assigns = Some (c_assigns,  sure_zone); }
+    (* Build the builtin results. *)
+    let return = deps_nth_arg 0 in
+    let froms = Assigns.{ memory; return } in
+    let c_assigns = Some (froms, sure_output) in
+    let return, c_clobbered =
+      if Model.is_reachable state then
+        (* Copy at least partially succeeded *)
+        Some dst_cvalue, Builtins.clobbered_set_from_ret state dst_cvalue
       else
-        Builtins.Full
-          { Builtins.c_values = [ None, Cvalue.Model.bottom];
-            c_clobbered = Base.SetLattice.bottom;
-            c_assigns = Some (c_assigns,  sure_zone); }
-  in
-  match actuals with
-  | [dst; src; size] -> compute dst src size
+        None, Base.SetLattice.bottom
+    in
+    Builtins.Full { c_values = [ return, state ]; c_clobbered; c_assigns }
   | _ -> raise (Builtins.Invalid_nb_of_args 3)
 
 let () =
   register_builtin ~replace:"memcpy" "Frama_C_memcpy" (frama_c_memcpy "memcpy");
   register_builtin ~replace:"memmove" "Frama_C_memmove" (frama_c_memcpy "memmove")
 
+(* -------------------------------------------------------------------------- *)
+(*                                  Memset                                    *)
+(* -------------------------------------------------------------------------- *)
+
 (*  Implementation of [memset] that accepts imprecise arguments. *)
 let frama_c_memset_imprecise state dst_lval dst v size =
   let prefix = "Builtin memset" in
diff --git a/tests/builtins/oracle/memcpy.res.oracle b/tests/builtins/oracle/memcpy.res.oracle
index 1559794a477915a5ad20d5b9232252094712ec3f..8672dfccee497d309e2305e7817f1c99ff5c318a 100644
--- a/tests/builtins/oracle/memcpy.res.oracle
+++ b/tests/builtins/oracle/memcpy.res.oracle
@@ -220,7 +220,7 @@
 [eva] memcpy.c:172: function memcpy: precondition 'valid_src' got status valid.
 [eva] memcpy.c:172: function memcpy: precondition 'separation' got status valid.
 [eva:imprecision] memcpy.c:172: 
-  In memcpy builtin: precise copy of indeterminate values UNINITIALIZED
+  In memcpy builtin: precise copy of indeterminate values.
 [eva] memcpy.c:173: assertion got status valid.
 [eva] computing for function itv <- main_uninit <- main_all.
   Called from memcpy.c:174.
@@ -233,7 +233,7 @@
 [eva:imprecision] memcpy.c:174: 
   In memcpy builtin: too many sizes to enumerate, possible loss of precision
 [eva:imprecision] memcpy.c:174: 
-  In memcpy builtin: imprecise copy of indeterminate values
+  In memcpy builtin: imprecise copy of indeterminate values.
 [eva] memcpy.c:175: assertion got status valid.
 [eva] computing for function make_unknown <- main_uninit <- main_all.
   Called from memcpy.c:178.
@@ -255,7 +255,7 @@
 [eva:imprecision] memcpy.c:181: 
   In memcpy builtin: too many sizes to enumerate, possible loss of precision
 [eva:imprecision] memcpy.c:181: 
-  In memcpy builtin: imprecise copy of indeterminate values
+  In memcpy builtin: imprecise copy of indeterminate values.
 [eva:alarm] memcpy.c:182: Warning: 
   accessing uninitialized left-value. assert \initialized(&b[11]);
 [eva] computing for function make_unknown <- main_uninit <- main_all.
@@ -267,7 +267,7 @@
 [eva] memcpy.c:187: function memcpy: precondition 'valid_src' got status valid.
 [eva] memcpy.c:187: function memcpy: precondition 'separation' got status valid.
 [eva:imprecision] memcpy.c:187: 
-  In memcpy builtin: precise copy of indeterminate values UNINITIALIZED
+  In memcpy builtin: precise copy of indeterminate values.
 [eva] memcpy.c:188: assertion got status valid.
 [eva] computing for function itv <- main_uninit <- main_all.
   Called from memcpy.c:190.
@@ -279,7 +279,7 @@
 [eva:imprecision] memcpy.c:190: 
   In memcpy builtin: too many sizes to enumerate, possible loss of precision
 [eva:imprecision] memcpy.c:190: 
-  In memcpy builtin: imprecise copy of indeterminate values
+  In memcpy builtin: imprecise copy of indeterminate values.
 [eva] memcpy.c:191: assertion got status valid.
 [eva:alarm] memcpy.c:192: Warning: 
   accessing uninitialized left-value. assert \initialized(&b[8]);
@@ -306,7 +306,7 @@
 [eva:imprecision] memcpy.c:200: 
   In memcpy builtin: too many sizes to enumerate, possible loss of precision
 [eva:imprecision] memcpy.c:200: 
-  In memcpy builtin: imprecise copy of indeterminate values
+  In memcpy builtin: imprecise copy of indeterminate values.
 [eva:alarm] memcpy.c:201: Warning: 
   accessing uninitialized left-value. assert \initialized(&b[11]);
 [eva] Recording results for main_uninit