From aed92e803dc74657e046aea8de50506514658096 Mon Sep 17 00:00:00 2001
From: Valentin Perrelle <valentin.perrelle@cea.fr>
Date: Wed, 6 Jun 2018 16:32:17 +0200
Subject: [PATCH] [Eva] Extends value partitioning to any expression, not just
 lvalues

---
 src/plugins/value/engine/partition.ml         | 63 +++++++++---------
 src/plugins/value/engine/partition.mli        | 16 ++---
 .../value/engine/partitioned_dataflow.ml      |  2 +-
 .../value/engine/partitioning_parameters.ml   | 19 +++---
 .../value/engine/state_partitioning.mli       |  8 ++-
 .../value/engine/trace_partitioning.ml        | 64 ++-----------------
 src/plugins/value/engine/transfer_stmt.ml     | 51 +++++++++++++++
 src/plugins/value/engine/transfer_stmt.mli    |  5 ++
 8 files changed, 118 insertions(+), 110 deletions(-)

diff --git a/src/plugins/value/engine/partition.ml b/src/plugins/value/engine/partition.ml
index 44642d2a166..64e2e6c5d80 100644
--- a/src/plugins/value/engine/partition.ml
+++ b/src/plugins/value/engine/partition.ml
@@ -24,7 +24,7 @@
 let opt_flatten (type a) (o : a option option) : a option =
   Extlib.opt_conv None o
 
-module LvalMap = Cil_datatype.LvalStructEq.Map
+module ExpMap = Cil_datatype.ExpStructEq.Map
 module IList = Datatype.List (Datatype.Int)
 
 type branch = int
@@ -34,8 +34,8 @@ type key = {
   transfer_stamp : int option;
   branches : branch list;
   loops : int list;
-  static_split : Integer.t LvalMap.t;
-  dynamic_split : Integer.t LvalMap.t;
+  static_split : Integer.t ExpMap.t;
+  dynamic_split : Integer.t ExpMap.t;
 }
 
 module Key =
@@ -49,8 +49,8 @@ struct
     Extlib.opt_compare (-) k1.ration_stamp k2.ration_stamp
     <?> (Extlib.opt_compare (-), k1.transfer_stamp, k2.transfer_stamp)
     <?> (IList.compare, k1.loops, k2.loops)
-    <?> (LvalMap.compare Integer.compare, k1.static_split, k2.static_split)
-    <?> (LvalMap.compare Integer.compare, k1.dynamic_split, k2.dynamic_split)
+    <?> (ExpMap.compare Integer.compare, k1.static_split, k2.static_split)
+    <?> (ExpMap.compare Integer.compare, k1.dynamic_split, k2.dynamic_split)
     <?> (IList.compare, k1.branches, k2.branches)
 end
 
@@ -67,10 +67,10 @@ type action =
   | Ration of int
   | Ration_merge of int option
   | Transfer_merge
-  | Static_split of Cil_types.lval
-  | Dynamic_split of Cil_types.lval
-  | Static_merge of Cil_types.lval
-  | Dynamic_merge of Cil_types.lval
+  | Static_split of Cil_types.exp
+  | Dynamic_split of Cil_types.exp
+  | Static_merge of Cil_types.exp
+  | Dynamic_merge of Cil_types.exp
   | Update_dynamic_splits
 
 exception InvalidAction
@@ -83,7 +83,7 @@ sig
   exception Cant_split
 
   val join : t -> t -> t
-  val split : t -> Cil_types.lval -> (Integer.t * t) list
+  val split : t -> Cil_types.exp -> (Integer.t * t) list
 end
 
 
@@ -100,8 +100,8 @@ struct
     transfer_stamp = None;
     branches = [];
     loops = [];
-    static_split = LvalMap.empty;
-    dynamic_split = LvalMap.empty;
+    static_split = ExpMap.empty;
+    dynamic_split = ExpMap.empty;
   }
 
   let is_empty (p : 'a partition) : bool =
@@ -128,38 +128,38 @@ struct
   let add_list (p : t) (l : (key * state) list) : t =
     List.fold_left (fun p (k,x) -> add p k x) p l
 
-  let split_state ~(static : bool) (lval : Cil_types.lval)
+  let split_state ~(static : bool) (exp : Cil_types.exp)
       (key : key) (state : state) : (key * state) list =
     try
       let update_key (v,x) =
         let k =
           if static then
-            { key with static_split = LvalMap.add lval v key.static_split }
+            { key with static_split = ExpMap.add exp v key.static_split }
           else
-            { key with dynamic_split = LvalMap.add lval v key.dynamic_split }
+            { key with dynamic_split = ExpMap.add exp v key.dynamic_split }
         in
         (k,x)
       in
-      List.map update_key (Domain.split state lval)
+      List.map update_key (Domain.split state exp)
     with Domain.Cant_split ->
       [(key,state)]
 
-  let split ~(static : bool) (p : t) (lval : Cil_types.lval) =
+  let split ~(static : bool) (p : t) (exp : Cil_types.exp) =
     let add_split key state p =
-      add_list p (split_state ~static lval key state)
+      add_list p (split_state ~static exp key state)
     in
     KMap.fold add_split p KMap.empty
 
   let update_dynamic_splits p =
     (* Update one state *)
     let update_state key state p =
-      (* Split the states in the list l for the given lval *)
-      let update_lval lval _ l =
+      (* Split the states in the list l for the given exp *)
+      let update_exp exp _ l =
         let static = false in
-        List.fold_left (fun l (k,s) -> split_state ~static lval k s @ l) [] l
+        List.fold_left (fun l (k,s) -> split_state ~static exp k s @ l) [] l
       in
-      (* Foreach lval in original state: split *)
-      let l = LvalMap.fold update_lval key.dynamic_split [(key,state)] in
+      (* Foreach exp in original state: split *)
+      let l = ExpMap.fold update_exp key.dynamic_split [(key,state)] in
       add_list p l
     in
     KMap.fold update_state p KMap.empty
@@ -168,11 +168,11 @@ struct
     KMap.fold (fun k x acc -> add acc (f k) x) p empty
 
   let transfer_keys p = function
-    | Static_split lval ->
-      split ~static:true p lval
+    | Static_split exp ->
+      split ~static:true p exp
 
-    | Dynamic_split lval ->
-      split ~static:false p lval
+    | Dynamic_split exp ->
+      split ~static:false p exp
 
     | Update_dynamic_splits ->
       update_dynamic_splits p
@@ -230,11 +230,11 @@ struct
         | Transfer_merge -> fun k ->
           { k with transfer_stamp = None }
 
-        | Static_merge lval -> fun k ->
-          { k with static_split = LvalMap.remove lval k.static_split }
+        | Static_merge exp -> fun k ->
+          { k with static_split = ExpMap.remove exp k.static_split }
 
-        | Dynamic_merge lval -> fun k ->
-          { k with dynamic_split = LvalMap.remove lval k.dynamic_split }
+        | Dynamic_merge exp -> fun k ->
+          { k with dynamic_split = ExpMap.remove exp k.dynamic_split }
       in
       map_keys transfer p
 
@@ -247,6 +247,7 @@ struct
       let add p y =
         let k' = { k with transfer_stamp = Some !t } in
         incr t;
+        assert (not (KMap.mem k' p));
         KMap.add k' y p
       in
       match f x with
diff --git a/src/plugins/value/engine/partition.mli b/src/plugins/value/engine/partition.mli
index 161b95ca6ba..6d2c5a27f3a 100644
--- a/src/plugins/value/engine/partition.mli
+++ b/src/plugins/value/engine/partition.mli
@@ -52,15 +52,15 @@
 
 type branch = int
 
-module LvalMap = Cil_datatype.LvalStructEq.Map
+module ExpMap = Cil_datatype.ExpStructEq.Map
 
 type key = private {
   ration_stamp : int option;
   transfer_stamp : int option;
   branches : branch list;
   loops : int list;
-  static_split : Integer.t LvalMap.t;
-  dynamic_split : Integer.t LvalMap.t;
+  static_split : Integer.t ExpMap.t;
+  dynamic_split : Integer.t ExpMap.t;
 }
 
 type 'a partition
@@ -73,10 +73,10 @@ type action =
   | Ration of int (* starting ration stamp *)
   | Ration_merge of int option (* new ration stamp for the merge state *)
   | Transfer_merge
-  | Static_split of Cil_types.lval
-  | Dynamic_split of Cil_types.lval
-  | Static_merge of Cil_types.lval
-  | Dynamic_merge of Cil_types.lval
+  | Static_split of Cil_types.exp
+  | Dynamic_split of Cil_types.exp
+  | Static_merge of Cil_types.exp
+  | Dynamic_merge of Cil_types.exp
   | Update_dynamic_splits
 
 exception InvalidAction
@@ -89,7 +89,7 @@ sig
   exception Cant_split
 
   val join : t -> t -> t
-  val split : t -> Cil_types.lval -> (Integer.t * t) list
+  val split : t -> Cil_types.exp -> (Integer.t * t) list
 end
 
 
diff --git a/src/plugins/value/engine/partitioned_dataflow.ml b/src/plugins/value/engine/partitioned_dataflow.ml
index e3d842a247f..14ede992285 100644
--- a/src/plugins/value/engine/partitioned_dataflow.ml
+++ b/src/plugins/value/engine/partitioned_dataflow.ml
@@ -97,7 +97,7 @@ module Make_Dataflow
 
   (* --- Abstract values storage --- *)
 
-  module Partition = Trace_partitioning.Make (Domain) (AnalysisParam)
+  module Partition = Trace_partitioning.Make (Domain) (Transfer) (AnalysisParam)
 
   type store = Partition.store
   type widening = Partition.widening
diff --git a/src/plugins/value/engine/partitioning_parameters.ml b/src/plugins/value/engine/partitioning_parameters.ml
index d625a9ef865..87d8cd6fd75 100644
--- a/src/plugins/value/engine/partitioning_parameters.ml
+++ b/src/plugins/value/engine/partitioning_parameters.ml
@@ -102,7 +102,7 @@ struct
     let add name l =
       try
         let vi = Globals.Vars.find_from_astinfo name VGlobal in
-        Cil.var vi :: l
+        Cil.evar vi :: l
       with Not_found ->
         warn ~current:false "cannot find the global variable %s for value \
                             partitioning" name;
@@ -111,19 +111,18 @@ struct
     ValuePartitioning.fold add []
 
   let flow_actions stmt =
-    let term_to_lval = function
-      | {term_node = TLval tlv} ->
-        !Db.Properties.Interp.term_lval_to_lval ~result:None tlv
-      | _ ->
-        warn "split/merge expressions must be lvalues";
-        raise Exit
+    let term_to_exp term =
+      !Db.Properties.Interp.term_to_exp ~result:None term
     in
     let map_annot acc t =
       try
         match t with
-        | FlowSplit t -> Partition.Static_split (term_to_lval t) :: acc
-        | FlowMerge t -> Partition.Static_merge (term_to_lval t) :: acc
-      with Exit -> acc (* Impossible to convert term to lval *)
+        | FlowSplit t -> Partition.Static_split (term_to_exp t) :: acc
+        | FlowMerge t -> Partition.Static_merge (term_to_exp t) :: acc
+      with 
+        Db.Properties.Interp.No_conversion ->
+        warn "split/merge expressions must be valid expressions";
+        acc (* Impossible to convert term to lval *)
     in
     List.fold_left map_annot [] (get_flow_annot stmt)
 end
diff --git a/src/plugins/value/engine/state_partitioning.mli b/src/plugins/value/engine/state_partitioning.mli
index 7395634a2c1..86956872280 100644
--- a/src/plugins/value/engine/state_partitioning.mli
+++ b/src/plugins/value/engine/state_partitioning.mli
@@ -38,7 +38,7 @@ sig
   val merge : Cil_types.stmt -> bool
   val unroll : loop -> int
   val history_size : int
-  val universal_splits : Cil_types.lval list
+  val universal_splits : Cil_types.exp list
   val flow_actions : Cil_types.stmt -> Partition.action list
 end
 
@@ -143,6 +143,8 @@ end
 
 module type Domain = Partitioning.Domain
 
-module type Partitioning =
-  functor (Domain : Domain) (Kf : Kf) ->
+module type Partitioning = functor
+  (Domain : Abstract_domain.External)
+  (Transfer : Transfer_stmt.S with type state = Domain.t)
+  (Kf : Kf) ->
     Partition with type state = Domain.t
diff --git a/src/plugins/value/engine/trace_partitioning.ml b/src/plugins/value/engine/trace_partitioning.ml
index 081851332d4..c93ab0ad40d 100644
--- a/src/plugins/value/engine/trace_partitioning.ml
+++ b/src/plugins/value/engine/trace_partitioning.ml
@@ -26,7 +26,10 @@ open State_partitioning
 open Partition
 
 
-module Make (Domain : Domain) (Kf : Kf) =
+module Make
+    (Domain : Abstract_domain.External)
+    (Transfer : Transfer_stmt.S with type state = Domain.t)
+    (Kf : Kf) =
 struct
   module Parameters = Partitioning_parameters.Make (Kf)
 
@@ -36,63 +39,10 @@ struct
   (* Add the split function to the domain *)
   module Domain =
   struct
-    include Domain
+    exception Cant_split = Transfer.Cant_split
+    let split = Transfer.split_by_value
 
-    module Val = struct
-      include Main_values.CVal
-      include Structure.Open (Structure.Key_Value) (Main_values.CVal)
-      let reduce t = t
-    end
-
-    module Eva =
-      Evaluation.Make
-        (Val)
-        (Main_locations.PLoc)
-        (Cvalue_domain.State)
-
-    exception Cant_split
-
-    (* TODO: size of split limit *)
-    let split state lval =
-      (* Whenever the split fail, warn the user and exit with an exception *)
-      let fail message =
-        Value_parameters.warning ~once:true message;
-        raise Cant_split
-      in
-      (* Get the cvalue *)
-      let cvalue = match get Cvalue_domain.key with
-        | Some get_cvalue -> get_cvalue state
-        | None -> fail "cannot partition by value when the CValue domain is not\
-                        active"
-      in
-      (* Retrieve the location of the lval *)
-      let cstate = Cvalue_domain.inject cvalue in
-      let location = match Eva.lvaluate ~for_writing:true cstate lval with
-      | `Value (_valuation, loc, _typ), _alarmset ->
-        Precise_locs.imprecise_location loc
-      | `Bottom, _alarmset ->
-        fail "cannot partition by value on an imprecise lvalue"
-      in
-      (* Extract the ival *)
-      let ival =
-        try
-          let v = Cvalue.Model.find cvalue location in
-          Cvalue.V.project_ival v
-        with Cvalue.V.Not_based_on_null ->
-          fail "cannot partition by value on pointers"
-      in
-      (* Build a state with the lvalue set to a singleton *)
-      let build i acc =
-        let v = Cvalue.V.inject_int i in
-        let cvalue = Cvalue.Model.add_binding ~exact:true cvalue location v in
-        let new_state = set Cvalue_domain.key cvalue state in
-        (i,new_state) :: acc
-      in
-      (* For each integer of the ival, build a new state *)
-      try
-        Ival.fold_int build ival []
-      with Abstract_interp.Error_Top ->
-        fail "too many values to partition by value on"
+    include Domain
   end
 
   module Index = Partitioning.Make (Domain)
diff --git a/src/plugins/value/engine/transfer_stmt.ml b/src/plugins/value/engine/transfer_stmt.ml
index 22ec00d8c54..b3cea597c20 100644
--- a/src/plugins/value/engine/transfer_stmt.ml
+++ b/src/plugins/value/engine/transfer_stmt.ml
@@ -40,6 +40,8 @@ module type S = sig
     state -> (stmt * lval list * lval list * lval list * stmt ref list) list ->
     unit or_bottom
   val enter_scope: kernel_function -> varinfo list -> state -> state
+  exception Cant_split
+  val split_by_value: state -> exp -> (Integer.t * state) list
   type call_result = {
     states: state list or_bottom;
     cacheable: Value_types.cacheable;
@@ -872,6 +874,55 @@ module Make (Abstract: Abstractions.Eva) = struct
     in
     List.fold_left initialize_volatile state vars
 
+
+  (* ------------------------------------------------------------------------ *)
+  (*                               Split by value                             *)
+  (* ------------------------------------------------------------------------ *)
+
+  exception Cant_split
+
+  let split_by_value state exp =
+    (* Whenever the split fails, warn the user and raise an exception *)
+    let fail message =
+      Value_parameters.warning ~once:true message;
+      raise Cant_split
+    in
+    (* Evaluate the expression *)
+    let valuation, value = match Eval.evaluate ~reduction:false state exp with
+      | `Value (valuation, value), alarms when Alarmset.is_empty alarms ->
+        valuation, value
+      | _ ->
+        fail "the split expression cannot be evaluated safely on all states";
+    in
+    (* Get the cvalue *)
+    let cvalue = match Value.get Main_values.cvalue_key with
+      | Some get_cvalue -> get_cvalue value
+      | None -> fail "cannot partition by value when the CValue domain is not\
+                      active"
+    in
+    (* Extract the ival *)
+    let ival =
+      try
+        Cvalue.V.project_ival cvalue
+      with Cvalue.V.Not_based_on_null ->
+        fail "cannot partition by value on pointers"
+    in
+    (* Build a state with the lvalue set to a singleton *)
+    let build i acc =
+      let value = Value.inject_int (Cil.typeOf exp) i in
+      match Eval.assume ~valuation state exp value with
+      | `Value valuation ->
+        (i, TF.update valuation state) :: acc
+      | _ -> (* This value cannot be set in the state ; the evaluation of
+                expr was unprecise *)
+        acc
+    in
+    (* For each integer of the ival, build a new state *)
+    begin try
+      Ival.fold_int build ival []
+    with Abstract_interp.Error_Top ->
+      fail "too many values to partition by value on"
+    end
 end
 
 
diff --git a/src/plugins/value/engine/transfer_stmt.mli b/src/plugins/value/engine/transfer_stmt.mli
index a2e64699a2e..7d8cee112e8 100644
--- a/src/plugins/value/engine/transfer_stmt.mli
+++ b/src/plugins/value/engine/transfer_stmt.mli
@@ -51,6 +51,11 @@ module type S = sig
 
   val enter_scope: kernel_function -> varinfo list -> state -> state
 
+  exception Cant_split
+
+  val split_by_value: state -> exp -> (Integer.t * state) list
+
+
   type call_result = {
     states: state list or_bottom;
     cacheable: Value_types.cacheable;
-- 
GitLab