From 1ce73b95e1e2787e9031879922ba1480219113c9 Mon Sep 17 00:00:00 2001
From: Valentin Perrelle <valentin.perrelle@cea.fr>
Date: Wed, 1 Dec 2021 16:36:15 +0100
Subject: [PATCH] [Eva] multidim: add annotation to hint the apropriate
 segmentation

---
 .../abstract_interp/abstract_memory.ml        | 31 +++++++++++-
 .../abstract_interp/abstract_memory.mli       |  5 ++
 src/plugins/value/domains/multidim_domain.ml  | 47 ++++++++++++++----
 src/plugins/value/utils/eva_annotations.ml    | 48 +++++++++++++++++++
 src/plugins/value/utils/eva_annotations.mli   |  8 ++++
 5 files changed, 130 insertions(+), 9 deletions(-)

diff --git a/src/kernel_services/abstract_interp/abstract_memory.ml b/src/kernel_services/abstract_interp/abstract_memory.ml
index ea1ff820b2e..2f3e1be7fa3 100644
--- a/src/kernel_services/abstract_interp/abstract_memory.ml
+++ b/src/kernel_services/abstract_interp/abstract_memory.ml
@@ -211,6 +211,7 @@ sig
     weak:bool -> Abstract_offset.typed_offset -> t -> t
   val incr_bound : oracle:oracle -> Cil_types.varinfo -> Integer.t option ->
     t -> t
+  val add_segmentation_bounds : oracle:oracle -> Cil_types.exp list -> t -> t
 end
 
 
@@ -661,6 +662,7 @@ sig
   val incr_bound :
     oracle:oracle -> Bound.Var.t -> Integer.t option -> t -> t
   val map : (submemory -> submemory) -> t -> t
+  val add_segmentation_bounds : oracle:oracle -> bound list -> t -> t
 end
 
 module Segmentation (Config : Config) (M : ProtoMemory) =
@@ -925,6 +927,7 @@ struct
   let write ~oracle f m lindex (* included *) uindex (* excluded *) = (* lindex < uindex *)
     let open (val (B.operators oracle)) in
     let age = Integer.succ m.age in
+    let same_bounds = lindex == uindex in
     let lindex = lindex, age and uindex = uindex, age in
     (* (start,head) : segmentation kept identical below the write indexes,
                       head is a list in reverse order
@@ -967,7 +970,7 @@ struct
       and next_is_empty = is_empty_segment ~oracle uindex u in
       let tail' =
         (if previous_is_empty then [] else [(v,lindex)]) @
-        [(f v,uindex)] @
+        (if same_bounds then [] else [(f v,uindex)]) @
         (if next_is_empty then [] else [(v,u)]) @
         tail
       and head',start' = match head with (* change last bound to match lindex *)
@@ -1014,6 +1017,12 @@ struct
 
   let map f m =
     { m with segments = check_segments (List.map (fun (v,u) -> f v, u) m.segments) }
+
+  let add_segmentation_bounds ~oracle bounds m =
+    let add_bound m b =
+      write ~oracle (fun _ -> assert false) m b b
+    in
+    List.fold_left add_bound m bounds
 end
 
 
@@ -1355,6 +1364,20 @@ struct
           let array_value = A.incr_bound ~oracle vi x a.array_value in
           Array { a with array_value=A.map aux array_value }
       in aux
+
+    let add_segmentation_bounds ~oracle bounds = function
+      | Array a ->
+        let convert_bound exp =
+          try
+            Some (Bound.of_exp exp)
+          with Bound.UnsupportedBoundExpression -> None
+        in
+        let bounds = List.filter_map convert_bound bounds in
+        Array {
+          a with
+          array_value = A.add_segmentation_bounds ~oracle bounds a.array_value
+        }
+      | m -> m (* Ignore segmentation hints on non-array *)
   end
   and S : Structures with type submemory = ProtoMemory.t =
     Structures (Config) (ProtoMemory)
@@ -1425,4 +1448,10 @@ struct
         src
     in
     write ~oracle ~weak f offset dst
+
+  let segmentation_hint ~oracle m offset bounds =
+    let f ~weak:_ _typ m =
+      add_segmentation_bounds ~oracle bounds m
+    in
+    write ~oracle ~weak:false f offset m
 end
diff --git a/src/kernel_services/abstract_interp/abstract_memory.mli b/src/kernel_services/abstract_interp/abstract_memory.mli
index 229dfee4bb7..32ac5f50cb6 100644
--- a/src/kernel_services/abstract_interp/abstract_memory.mli
+++ b/src/kernel_services/abstract_interp/abstract_memory.mli
@@ -123,4 +123,9 @@ sig
 
   (* Pretty prints memory *)
   val pretty : Format.formatter -> t -> unit
+
+  (* Update the array segmentation at the given offset so the given bound
+     expressions appear in the segmentation *)
+  val segmentation_hint : oracle:oracle ->
+    t -> location -> Cil_types.exp list -> t
 end
diff --git a/src/plugins/value/domains/multidim_domain.ml b/src/plugins/value/domains/multidim_domain.ml
index 2908b421633..82ae326f38f 100644
--- a/src/plugins/value/domains/multidim_domain.ml
+++ b/src/plugins/value/domains/multidim_domain.ml
@@ -246,6 +246,12 @@ struct
     | `Top -> Memory.top
     | `Value loc ->
       Memory.overwrite ~oracle ~weak dst loc src
+
+  let segmentation_hint ~oracle m loc bounds =
+    match loc with
+    | `Top -> Memory.top
+    | `Value loc ->
+      Memory.segmentation_hint ~oracle m loc bounds
 end
 
 (* References to variables inside array segmentation.
@@ -335,7 +341,6 @@ struct
       | Lval lval ->
         let value = get state (Location.of_lval oracle lval) in
         Bottom.non_bottom value (* TODO: handle exception *)
-      | Info (e, _) -> oracle e
       | Const (CInt64 (i,_,_)) -> Value.inject_int i
       | UnOp (op, e, typ) -> Value.forward_unop typ op (oracle e)
       | BinOp (op, e1, e2, TFloat (fkind, _)) ->
@@ -348,7 +353,7 @@ struct
         and dst_type = scalar_type typ in
         Value.forward_cast ~src_type ~dst_type (oracle e)
       | _ ->
-        Value_parameters.fatal
+        Self.fatal
           "This type of array index expression is not supported: %a"
           Cil_printer.pp_exp exp
     in
@@ -365,22 +370,26 @@ struct
     let oracle = mk_oracle state in
     read (Memory.extract ~oracle) (Memory.smash ~oracle) state src
 
+  let add_references state vi refs' =
+    let base = Base.of_varinfo vi in
+    let memory, refs = find_or_top state base in
+    let refs'' = References.union refs (References.of_list refs') in
+    add base (memory, refs'') state
+
+  let add_references_l state l refs =
+    List.fold_left (fun state vi -> add_references state vi refs) state l
+
   let write (update : memory -> offset -> memory)
       (state : state) (loc : mdlocation) : state =
-    let bases = Location.bases loc in
     let f base off state =
       if covers_base base then
         let memory, refs = find_or_top state base in
         add base (update memory off, refs) state
       else
         state
-    and add_ref state vi =
-      let base = Base.of_varinfo vi in
-      let memory, refs = find_or_top state base in
-      add base (memory, References.union refs (References.of_list bases)) state
     in
     let state = Location.fold f loc state in
-    List.fold_left add_ref state (Location.references loc)
+    add_references_l state (Location.references loc) (Location.bases loc)
 
   let set (state : state) (dst : mdlocation) (v : value) : state =
     let weak = not (Location.is_singleton dst)
@@ -673,6 +682,28 @@ struct
     in
     reduce predicate truth state
 
+  let interpret_acsl_extension extension _env state =
+    if extension.ext_name = "array_partition" then
+      let annotation = Eva_annotations.read_array_segmentation extension in
+      let vi,offset,bounds = annotation in
+      (* Update the segmentation *)
+      let lval = Cil_types.Var vi, offset in
+      let loc = Location.of_lval (mk_oracle' state) lval in
+      let oracle = mk_oracle state in
+      let update m offset =
+        Memory.segmentation_hint ~oracle m offset bounds
+      in
+      let state = write update state loc in
+      (* Update the references *)
+      let add acc e =
+        let r = Cil.extract_varinfos_from_exp e in
+        (Cil_datatype.Varinfo.Set.to_seq r |> List.of_seq) @ acc
+      in
+      let references = List.fold_left add [] bounds in
+      add_references_l state references (Location.bases loc)
+    else
+      state
+
   let empty () = top
 
   let initialize_variable lval _loc ~initialized:_ init_value state =
diff --git a/src/plugins/value/utils/eva_annotations.ml b/src/plugins/value/utils/eva_annotations.ml
index fd9feb85a1e..2c0ab30aefb 100644
--- a/src/plugins/value/utils/eva_annotations.ml
+++ b/src/plugins/value/utils/eva_annotations.ml
@@ -351,3 +351,51 @@ module Allocation = struct
 end
 
 let get_allocation = Allocation.get
+
+
+module ArraySegmentation = Register (struct
+    module Interp = Db.Properties.Interp
+
+    type t = Cil_types.varinfo * Cil_types.offset * Cil_types.exp list
+    let name = "array_partition"
+    let is_loop_annot = false
+
+    let convert = function
+      | {term_node =  TLval (TVar {lv_origin=Some vi}, toffset)} :: tbounds ->
+        begin try
+            let offset = !Interp.term_offset_to_offset ~result:None toffset
+            and bounds = List.map (!Interp.term_to_exp ~result:None) tbounds in
+            Some (vi, offset, bounds)
+          with
+            Interp.No_conversion -> None
+        end
+      | _ -> None
+
+    let parse ~typing_context:context lexprs =
+      let open Logic_typing in
+      let l = List.map (context.type_term context context.pre_state) lexprs in
+      Extlib.the ~exn:Parse_error (convert l)
+
+    let import = function
+      | Ext_terms l -> Option.get (convert l)
+      | _ -> assert false
+
+    let export (vi, offset, bounds) =
+      let lv = Cil.cvar_to_lvar vi
+      and toffset = Logic_utils.offset_to_term_offset offset
+      and tbounds = List.map Logic_utils.expr_to_term bounds in
+      let tlval = TVar lv, toffset in
+      let tarray = Logic_const.term (TLval tlval) (Cil.typeOfTermLval tlval) in
+      Ext_terms (tarray :: tbounds)
+
+    let print fmt (vi,offset,bounds) =
+      Format.fprintf fmt "%a, %a"
+        Cil_printer.pp_lval (Var vi, offset)
+        (Pretty_utils.pp_list ~sep:",@ " Cil_printer.pp_exp) bounds
+  end)
+
+
+type array_segmentation = ArraySegmentation.t
+let get_array_segmentation = ArraySegmentation.get
+let add_array_segmentation = ArraySegmentation.add
+let read_array_segmentation ext = ArraySegmentation.import ext.ext_kind
diff --git a/src/plugins/value/utils/eva_annotations.mli b/src/plugins/value/utils/eva_annotations.mli
index 4aaa3084dee..98d914d689a 100644
--- a/src/plugins/value/utils/eva_annotations.mli
+++ b/src/plugins/value/utils/eva_annotations.mli
@@ -59,11 +59,15 @@ type flow_annotation =
 
 type allocation_kind = By_stack | Fresh | Fresh_weak | Imprecise
 
+type array_segmentation =
+  Cil_types.varinfo * Cil_types.offset * Cil_types.exp list
+
 val get_slevel_annot : Cil_types.stmt -> slevel_annotation option
 val get_unroll_annot : Cil_types.stmt -> unroll_annotation list
 val get_flow_annot : Cil_types.stmt -> flow_annotation list
 val get_subdivision_annot : Cil_types.stmt -> int list
 val get_allocation: Cil_types.stmt -> allocation_kind
+val get_array_segmentation : Cil_types.stmt -> array_segmentation list
 
 val add_slevel_annot : emitter:Emitter.t ->
   Cil_types.stmt -> slevel_annotation -> unit
@@ -73,4 +77,8 @@ val add_flow_annot : emitter:Emitter.t ->
   Cil_types.stmt -> flow_annotation -> unit
 val add_subdivision_annot : emitter:Emitter.t ->
   Cil_types.stmt -> int -> unit
+val add_array_segmentation : emitter:Emitter.t ->
+  Cil_types.stmt -> array_segmentation -> unit
 [@@@ api_end]
+
+val read_array_segmentation : Cil_types.acsl_extension -> array_segmentation
-- 
GitLab