From 86bc236d8902280ccee0cfbc87d1ea0bcec570af Mon Sep 17 00:00:00 2001
From: Valentin Perrelle <valentin.perrelle@cea.fr>
Date: Fri, 15 Apr 2022 18:11:28 +0200
Subject: [PATCH] [Eva] multidim: fix missing references updates

---
 .../value/domains/multidim/multidim_domain.ml | 83 ++++++++++---------
 .../value/domains/multidim/typed_memory.ml    |  6 +-
 .../value/domains/multidim/typed_memory.mli   |  3 +
 tests/value/oracle_multidim/nonlin.res.oracle |  2 -
 4 files changed, 50 insertions(+), 44 deletions(-)
 delete mode 100644 tests/value/oracle_multidim/nonlin.res.oracle

diff --git a/src/plugins/value/domains/multidim/multidim_domain.ml b/src/plugins/value/domains/multidim/multidim_domain.ml
index 1e6bf9c0d25..d5cadfc6f06 100644
--- a/src/plugins/value/domains/multidim/multidim_domain.ml
+++ b/src/plugins/value/domains/multidim/multidim_domain.ml
@@ -108,11 +108,6 @@ struct
     map (fun _ -> new_v) v
 end
 
-let no_oracle exp =
-  match Cil.isInteger exp with
-  | None -> Value.top
-  | Some i -> Value.inject_int i
-
 let convert_oracle oracle =
   fun exp ->
   match Ival.project_int_val (Value.project_ival (oracle exp)) with
@@ -162,6 +157,12 @@ struct
       | `Top -> false
       | `Value o -> not (Base.is_weak b) && Offset.is_singleton o
 
+  let to_singleton_var map =
+    match map_to_singleton map with
+    | Some (Base.Var (vi, _), `Value (Offset.NoOffset t)) (* maybe too restrictive *)
+      when Typed_memory.are_typ_compatible vi.vtype t -> Some (vi)
+    | _ -> None
+
   let references (map : t) =
     let module Set = Cil_datatype.Varinfo.Set in
     let add_refs _b o acc =
@@ -355,11 +356,6 @@ struct
       then remove b state
       else add b v state
 
-    let remove_var (state : t) (v : Cil_types.varinfo) =
-      remove (Base.of_varinfo v) state
-
-    let remove_vars (state : t) (l : Cil_types.varinfo list) =
-      List.fold_left remove_var state l
   end
 
   include Datatype.Pair_with_collections
@@ -411,7 +407,7 @@ struct
       (fun state b -> add_references state b referers)
       state referees
 
-  let update_references ~oracle (dst : Cil_types.varinfo)
+  let update_var_references ~oracle (dst : Cil_types.varinfo)
       (src : Cil_types.exp option) (base_map,tracked : t) =
     let incr = Option.bind src (fun expr ->
         match expr.Cil_types.enode with
@@ -451,6 +447,19 @@ struct
     in
     base_map, tracked
 
+  let update_references ~oracle (dst : mdlocation)
+      (src : Cil_types.exp option) (state : t) =
+    let remove_references b state =
+      match Base.to_varinfo b with
+      | exception Base.Not_a_C_variable -> state (* only references to variables are kept *)
+      | vi -> update_var_references ~oracle vi None state
+    in
+    match Location.to_singleton_var dst with
+    | Some vi ->
+      update_var_references ~oracle vi src state
+    | None -> (* Weak update of referee, remove references *)
+      Location.fold (fun b _offset state -> remove_references b state) dst state
+
 
   (* Accesses *)
 
@@ -553,7 +562,7 @@ struct
       write (fun m off -> Memory.overwrite ~oracle ~weak m off value) state dst
 
   let erase ~oracle ?(weak: bool option)
-      (state : state) (dst : mdlocation) (b : Abstract_memory.bit): state =
+      (state : state) (dst : mdlocation) (b : Abstract_memory.bit) : state =
     let oracle = convert_oracle oracle
     and weak = match weak with
       | None -> not (Location.is_singleton dst)
@@ -568,6 +577,12 @@ struct
     let oracle = convert_oracle oracle in
     write' (fun m off -> Memory.reinforce ~oracle f m off) state dst
 
+  let remove ~oracle (vi : Cil_types.varinfo) (state : t) =
+    let state = update_var_references ~oracle vi None state in
+    let map, tracked = state in
+    BaseMap.remove (Base.of_varinfo vi) map, tracked
+
+
   (* Lattice *)
 
   let top = BaseMap.empty, None
@@ -685,11 +700,12 @@ struct
   let update valuation state =
     assume_valuation valuation state
 
-  let assign_lval lval assigned_value oracle state =
-    match Location.of_lval oracle lval with
+  let assign' ~oracle ~value dst src state =
+    match Location.of_lval oracle dst with
     | `Top -> top
     | `Value dst ->
-      match assigned_value with
+      let state = update_references ~oracle dst src state in
+      match value with
       | Assign value ->
         set ~oracle state dst (Value_or_Uninitialized.of_value value)
       | Copy (right, value) ->
@@ -700,22 +716,10 @@ struct
         | `Value src ->
           overwrite ~oracle state dst src
 
-  let assign _kinstr left expr assigned_value valuation state =
+  let assign _kinstr { lval=dst } src assigned_value valuation state =
     let+ state = assume_valuation valuation state in
     let oracle = valuation_to_oracle state valuation in
-    (* Update references *)
-    let state =
-      match left.lval with
-      | Mem _, _ -> state (* Do nothing *)
-      | Var vi, offset ->
-        let expr = match offset with
-          | NoOffset -> Some expr
-          | _ -> None
-        in
-        update_references ~oracle vi expr state
-    in
-    (* Update destination *)
-    assign_lval left.lval assigned_value oracle state
+    assign' ~oracle ~value:assigned_value dst (Some src) state
 
   let assume _stmt _expr _pos valuation state =
     assume_valuation valuation state
@@ -727,7 +731,8 @@ struct
         "The multidim domain does not support recursive calls yet";
     let oracle = valuation_to_oracle state valuation in
     let bind state arg =
-      state >>-: assign_lval (Cil.var arg.formal) arg.avalue oracle
+      state >>-:
+      assign' ~oracle ~value:arg.avalue (Cil.var arg.formal) (Some arg.concrete)
     in
     List.fold_left bind (`Value state) call.arguments
 
@@ -737,7 +742,8 @@ struct
     | Some f, Some return ->
       let args = List.map (fun arg -> arg.avalue) call.arguments in
       let+ assigned_result = f args in
-      assign_lval (Cil.var return) assigned_result no_oracle post
+      let oracle = mk_oracle post in
+      assign' ~oracle ~value:assigned_result (Cil.var return) None post
 
   let show_expr valuation state fmt expr =
     match expr.enode with
@@ -761,13 +767,7 @@ struct
 
   let leave_scope _kf vars state =
     let oracle = mk_oracle state in
-    let state =
-      List.fold_left
-        (fun state vi -> update_references ~oracle vi None state)
-        state vars
-    in
-    let (base_map,tracked) = state in
-    BaseMap.remove_vars base_map vars, tracked
+    List.fold_left (fun state vi -> remove ~oracle vi state) state vars
 
   let logic_assign assign location state =
     let oracle = mk_oracle state
@@ -780,6 +780,7 @@ struct
       | Some (Assigns (_, _ :: _), _) -> Abstract_memory.Bit.top
       | Some (Assigns (_, []), _) -> Abstract_memory.Bit.numerical
     in
+    let state = update_references ~oracle dst None state in
     erase ~oracle ~weak:true state dst b
 
   let reduce_by_papp env li _labels args positive state =
@@ -851,7 +852,8 @@ struct
   let empty () = top
 
   let initialize_variable lval _loc ~initialized:_ init_value state =
-    match Location.of_lval no_oracle lval with
+    let oracle = mk_oracle state in
+    match Location.of_lval oracle lval with
     | `Top -> top
     | `Value dst ->
       let d = match init_value with
@@ -863,7 +865,8 @@ struct
 
   let initialize_variable_using_type _kind vi state =
     let lval = Cil.var vi in
-    match Location.of_lval no_oracle lval with
+    let oracle = mk_oracle state in
+    match Location.of_lval oracle lval with
     | `Top -> top
     | `Value dst ->
       let oracle = mk_oracle state in (* Since dst has no offset, oracle is actually useless *)
diff --git a/src/plugins/value/domains/multidim/typed_memory.ml b/src/plugins/value/domains/multidim/typed_memory.ml
index f6121827bf6..e54285b8de2 100644
--- a/src/plugins/value/domains/multidim/typed_memory.ml
+++ b/src/plugins/value/domains/multidim/typed_memory.ml
@@ -37,11 +37,13 @@ let (<?>) c lcmp =
 
 (* Types compatibility *)
 
-let typ_size t =
+let typ_size t = (* raises Cil.SizeOfError *)
   Integer.of_int (Cil.bitsSizeOf t)
 
 let are_typ_compatible t1 t2 =
-  Integer.equal (typ_size t1) (typ_size t2)
+  Cil_datatype.TypNoAttrs.equal t1 t2 ||
+  try Integer.equal (typ_size t1) (typ_size t2)
+  with Cil.SizeOfError _ -> false
 
 (* Input modules *)
 
diff --git a/src/plugins/value/domains/multidim/typed_memory.mli b/src/plugins/value/domains/multidim/typed_memory.mli
index 3fcb37e3c2f..8edd0e150e7 100644
--- a/src/plugins/value/domains/multidim/typed_memory.mli
+++ b/src/plugins/value/domains/multidim/typed_memory.mli
@@ -23,6 +23,9 @@
 open Lattice_bounds
 open Abstract_memory
 
+val are_typ_compatible : Cil_types.typ -> Cil_types.typ -> bool
+
+
 (* Configuration of the Abstract Memory model *)
 module type Config =
 sig
diff --git a/tests/value/oracle_multidim/nonlin.res.oracle b/tests/value/oracle_multidim/nonlin.res.oracle
deleted file mode 100644
index 1dd995bc17b..00000000000
--- a/tests/value/oracle_multidim/nonlin.res.oracle
+++ /dev/null
@@ -1,2 +0,0 @@
-128a129
-> [eva:nonlin] nonlin.c:94: subdividing on x
-- 
GitLab