From b2267252febe547a57c2e63ef11a5ae4e6ff7353 Mon Sep 17 00:00:00 2001
From: Julien Signoles <julien.signoles@cea.fr>
Date: Fri, 1 Mar 2019 18:22:52 +0100
Subject: [PATCH] [Interval_system] R.I.P. normalization

---
 src/plugins/e-acsl/interval_system.ml | 234 +++++++++++---------------
 1 file changed, 95 insertions(+), 139 deletions(-)

diff --git a/src/plugins/e-acsl/interval_system.ml b/src/plugins/e-acsl/interval_system.ml
index 050410b9f4e..9be3db24ede 100644
--- a/src/plugins/e-acsl/interval_system.ml
+++ b/src/plugins/e-acsl/interval_system.ml
@@ -60,7 +60,7 @@ module Ivar =
   end)
 
 (**************************************************************************)
-(***************************** Solver *************************************)
+(***************************** Helpers ************************************)
 (**************************************************************************)
 
 exception Not_an_integer
@@ -135,6 +135,10 @@ let interv_of_typ_containing_interv i =
     (* infinity *)
     Ival.inject_range None None
 
+(**************************************************************************)
+(***************************** Solver *************************************)
+(**************************************************************************)
+
 (* Build the system of interval equations for the logic function called
   through [t].
   Example: the following function:
@@ -149,97 +153,73 @@ let interv_of_typ_containing_interv i =
   and Z the for the f(Z) (from long-1, long-2 and long-3) *)
 let build ~infer t =
   let rec aux ieqs ivars t = match t.term_node with
-  | Tapp(li, _, args) ->
-    if li.l_type = Some Linteger && is_recursive li then begin
-      let args_lty = List.map2
-        (fun lv arg ->
-          try
-            (* speed-up convergence; because of this approximation, no need to
-               associate [i] to [lv] in [Interval.Env]: the very same interval
-               will be computed anyway. *)
-            let i = interv_of_typ_containing_interv (infer arg) in
-            Ctype (TInt(ikind_of_interv i, []))
-          with
-          | Cil.Not_representable -> Linteger
-          | Not_an_integer -> lv.lv_type)
-        li.l_profile
-        args
-      in
-      (* x *)
-      let ivar =
-        { iv_name = li.l_var_info.lv_name; iv_types = args_lty }
-      in
-      (* Adding x = g(x) if it is not yet in the system of equations.
-        Without this check, the algorithm would not terminate. *)
-      let ieqs, ivars =
-        if List.exists (fun ivar' -> Ivar.equal ivar ivar') ivars
-        then ieqs, ivars
-        else
-          let (iexp:ival_exp), ieqs, ivars =
-            aux ieqs (ivar :: ivars) (Misc.term_of_li li)
-          in
-          (* Adding x = g(x) *)
-          let ieqs = Ivar.Map.add ivar iexp ieqs in
-          ieqs, ivars
-      in
-      Ivar ivar, ieqs, ivars
-    end else
+    | Tapp(li, _, args) ->
+      if li.l_type = Some Linteger && is_recursive li then begin
+        let args_lty =
+          List.map2
+            (fun lv arg ->
+               try
+                 (* speed-up convergence; because of this approximation, no need
+                    to associate [i] to [lv] in [Interval.Env]: the very same
+                    interval will be computed anyway. *)
+                 let i = interv_of_typ_containing_interv (infer arg) in
+                 Ctype (TInt(ikind_of_interv i, []))
+               with
+               | Cil.Not_representable -> Linteger
+               | Not_an_integer -> lv.lv_type)
+            li.l_profile
+            args
+        in
+        let ivar = { iv_name = li.l_var_info.lv_name; iv_types = args_lty } in
+        (* Adding x = g(x) if it is not yet in the system of equations.
+           Without this check, the algorithm would not terminate. *)
+        let ieqs, ivars =
+          if List.exists (fun ivar' -> Ivar.equal ivar ivar') ivars
+          then ieqs, ivars
+          else
+            let iexp, ieqs, ivars =
+              aux ieqs (ivar :: ivars) (Misc.term_of_li li)
+            in
+            (* Adding x = g(x) *)
+            let ieqs = Ivar.Map.add ivar iexp ieqs in
+            ieqs, ivars
+        in
+        Ivar ivar, ieqs, ivars
+      end else begin
+        try Iconst (infer t), ieqs, ivars
+        with Not_an_integer -> Iunsupported, ieqs, ivars
+      end
+    | TConst _ ->
       (try Iconst(infer t), ieqs, ivars
-      with Not_an_integer -> Iunsupported, ieqs, ivars)
-  | TConst _ ->
-    (try Iconst(infer t), ieqs, ivars
-    with Not_an_integer -> Iunsupported, ieqs, ivars)
-  | TLval(TVar _, _) ->
-    (try Iconst(infer t), ieqs, ivars
-    with Not_an_integer -> Iunsupported, ieqs, ivars)
-  | TBinOp (PlusA, t1, t2) ->
-    let iexp1, ieqs, ivars = aux ieqs ivars t1 in
-    let iexp2, ieqs, ivars = aux ieqs ivars t2 in
-    Ibinop(Ival_add, iexp1, iexp2), ieqs, ivars
-  | TBinOp (MinusA, t1, t2) ->
-    let iexp1, ieqs, ivars = aux ieqs ivars t1 in
-    let iexp2, ieqs, ivars = aux ieqs ivars t2 in
-    Ibinop(Ival_min, iexp1, iexp2), ieqs, ivars
-  | TBinOp (Mult, t1, t2) ->
-    let iexp1, ieqs, ivars = aux ieqs ivars t1 in
-    let iexp2, ieqs, ivars = aux ieqs ivars t2 in
-    Ibinop(Ival_mul, iexp1, iexp2), ieqs, ivars
-  | TBinOp (Div, t1, t2) ->
-    let iexp1, ieqs, ivars = aux ieqs ivars t1 in
-    let iexp2, ieqs, ivars = aux ieqs ivars t2 in
-    Ibinop(Ival_div, iexp1, iexp2), ieqs, ivars
-  | Tif(_, t1, t2) ->
-    let iexp1, ieqs, ivars = aux ieqs ivars t1 in
-    let iexp2, ieqs, ivars = aux ieqs ivars t2 in
-    Ibinop(Ival_union, iexp1, iexp2), ieqs, ivars
-  | _ ->
-    Iunsupported, ieqs, ivars
+       with Not_an_integer -> Iunsupported, ieqs, ivars)
+    | TLval(TVar _, _) ->
+      (try Iconst(infer t), ieqs, ivars
+       with Not_an_integer -> Iunsupported, ieqs, ivars)
+    | TBinOp (PlusA, t1, t2) ->
+      let iexp1, ieqs, ivars = aux ieqs ivars t1 in
+      let iexp2, ieqs, ivars = aux ieqs ivars t2 in
+      Ibinop(Ival_add, iexp1, iexp2), ieqs, ivars
+    | TBinOp (MinusA, t1, t2) ->
+      let iexp1, ieqs, ivars = aux ieqs ivars t1 in
+      let iexp2, ieqs, ivars = aux ieqs ivars t2 in
+      Ibinop(Ival_min, iexp1, iexp2), ieqs, ivars
+    | TBinOp (Mult, t1, t2) ->
+      let iexp1, ieqs, ivars = aux ieqs ivars t1 in
+      let iexp2, ieqs, ivars = aux ieqs ivars t2 in
+      Ibinop(Ival_mul, iexp1, iexp2), ieqs, ivars
+    | TBinOp (Div, t1, t2) ->
+      let iexp1, ieqs, ivars = aux ieqs ivars t1 in
+      let iexp2, ieqs, ivars = aux ieqs ivars t2 in
+      Ibinop(Ival_div, iexp1, iexp2), ieqs, ivars
+    | Tif(_, t1, t2) ->
+      let iexp1, ieqs, ivars = aux ieqs ivars t1 in
+      let iexp2, ieqs, ivars = aux ieqs ivars t2 in
+      Ibinop(Ival_union, iexp1, iexp2), ieqs, ivars
+    | _ ->
+      Iunsupported, ieqs, ivars
   in
   aux Ivar.Map.empty [] t
 
-(* Normalize the expression.
-  An expression is said to be normalized if it is:
-    - either Iunsupported
-    - or an expression that contains no Iunsupported *)
-let normalize_iexp iexp =
-  let rec has_unsupported iexp = match iexp with
-    | Iunsupported | Ibinop(_, Iunsupported, _) | Ibinop(_, _, Iunsupported) ->
-      true
-    | Ibinop(_, (Iconst _ | Ivar _), (Iconst _ | Ivar _))
-    | Iconst _ | Ivar _ ->
-      false
-    | Ibinop(_, iexp1, iexp2) ->
-      has_unsupported iexp1 || has_unsupported iexp2
-  in
-  if has_unsupported iexp then Iunsupported else iexp
-
-let normalize_ieqs ieqs =
-  Ivar.Map.map (fun iexp -> normalize_iexp iexp) ieqs
-
-let get_ival_of_iconst ieqs ivar = match Ivar.Map.find ieqs ivar with
-  | Iconst i -> i
-  | Ivar _ | Ibinop _| Iunsupported -> Options.fatal "not an Iconst"
-
 (*************************************************************************)
 (******************************** Solver *********************************)
 (*************************************************************************)
@@ -277,7 +257,7 @@ let iterate indexes max =
       [-chain_of_ivalmax.(index), chain_of_ivalmax.(index)]
     - a variable that is associated to an index [index] equaling [max] will
       be given the whole interval of integers [Z]. *)
-let to_iconsts indexes ieqs chain_of_ivalmax =
+let to_iconsts chain_of_ivalmax indexes ieqs: Ival.t Ivar.Map.t =
   let max = Array.length chain_of_ivalmax in
   let indexes_i = ref 0 in
   Ivar.Map.map
@@ -293,73 +273,48 @@ let to_iconsts indexes ieqs chain_of_ivalmax =
           assert false
       in
       indexes_i := !indexes_i + 1;
-      Iconst ival)
+      ival)
     ieqs
 
-(* Assumes [iexp] to be a normalized [ival_exp] and evaluates it when each of
-  its variable is replaced by the corresponding interval from [iconsts]. *)
-let rec eval_iexp iexp iconsts =
-  match iexp with
-  | Iunsupported ->
-    Iconst (Ival.inject_range None None)
-  | Iconst _ ->
-    iexp
-  | Ivar iv ->
-    Ivar.Map.find iv iconsts
-  | Ibinop(_, Iunsupported, _) | Ibinop(_, _, Iunsupported) ->
-    assert false (* because [iexp] has been normalized *)
+let rec eval_iexp env = function
+  | Iunsupported | Ibinop(_, Iunsupported, _) | Ibinop(_, _, Iunsupported) ->
+    Ival.inject_range None None
+  | Iconst i -> i
+  | Ivar iv -> Ivar.Map.find iv env
   | Ibinop(ibinop, iexp1, iexp2) ->
-    let i1 = match eval_iexp iexp1 iconsts with
-      | Iconst i -> i
-      | Iunsupported | Ivar _ | Ibinop _ -> assert false
-    in
-    let i2 = match eval_iexp iexp2 iconsts with
-      | Iconst i -> i
-      | Iunsupported | Ivar _ | Ibinop _ -> assert false
-    in
+    let i1 = eval_iexp env iexp1 in
+    let i2 = eval_iexp env iexp2 in
     match ibinop with
-      | Ival_add -> Iconst (Ival.add_int i1 i2)
-      | Ival_min -> Iconst (Ival.sub_int i1 i2)
-      | Ival_mul -> Iconst (Ival.mul i1 i2)
-      | Ival_div -> Iconst (Ival.div i1 i2)
-      | Ival_union -> Iconst (Ival.join i1 i2)
-
-let equal_iconst iconst1 iconst2 =
-  let i1 = match iconst1 with
-    | Iconst i -> i
-    | Iunsupported | Ivar _ | Ibinop _ -> assert false
-  in
-  let i2 = match iconst2 with
-    | Iconst i -> i
-    | Iunsupported | Ivar _ | Ibinop _ -> assert false
-  in
-  Ival.is_included i1 i2
+    | Ival_add -> Ival.add_int i1 i2
+    | Ival_min -> Ival.sub_int i1 i2
+    | Ival_mul -> Ival.mul i1 i2
+    | Ival_div -> Ival.div i1 i2
+    | Ival_union -> Ival.join i1 i2
 
-let is_post_fixpoint ieqs iconsts =
+let is_post_fixpoint ieqs env =
   Ivar.Map.for_all
     (fun ivar iexp ->
-       let iconst1 = eval_iexp iexp iconsts in
-       let iconst2 = Ivar.Map.find ivar iconsts in
-       equal_iconst iconst1 iconst2)
+       let i1 = eval_iexp env iexp in
+       let i2 = Ivar.Map.find ivar env in
+       Ival.is_included i1 i2)
     ieqs
 
-let rec iterate_till_post_fixpoint ieqs indexes chain_of_ivalmax =
-  let iconsts = to_iconsts indexes ieqs chain_of_ivalmax in
+let rec iterate_till_post_fixpoint chain_of_ivalmax indexes ieqs =
+  let iconsts = to_iconsts chain_of_ivalmax indexes ieqs in
   if is_post_fixpoint ieqs iconsts then
     iconsts
   else
     let index_max = Array.length chain_of_ivalmax in
     iterate indexes index_max;
-    iterate_till_post_fixpoint ieqs indexes chain_of_ivalmax
+    iterate_till_post_fixpoint chain_of_ivalmax indexes ieqs
 
-let solve ieqs ivar chain_of_ivalmax =
-  let ieqs = normalize_ieqs ieqs in
+let solve chain_of_ivalmax ivar ieqs =
   let dim = Ivar.Map.cardinal ieqs in
   let bottom = Array.make dim 0 in
   let post_fixpoint =
-    iterate_till_post_fixpoint ieqs bottom chain_of_ivalmax
+    iterate_till_post_fixpoint chain_of_ivalmax bottom ieqs
   in
-  get_ival_of_iconst ivar post_fixpoint
+  Ivar.Map.find ivar post_fixpoint
 
 let build_and_solve ~infer t =
   (* 1) Build a system of interval equations that constrain the solution: do so
@@ -373,7 +328,7 @@ let build_and_solve ~infer t =
 
      TODO: I do not understand the remark above. The interval of a C global
      variable is computed from its type. *)
-  let iexp, ieqs, _ = build ~infer t in
+  let iexp, ieqs, _ivars = build ~infer t in
   (*  2) Solve it:
       The problem is probably undecidable in the general case.
       Thus we just look for reasonably precise approximations
@@ -386,5 +341,6 @@ let build_and_solve ~infer t =
        but it works fine for now. *)
   in
   match iexp with
-  | Ivar ivar -> solve ieqs ivar chain_of_ivalmax
+  | Ivar ivar -> solve chain_of_ivalmax ivar ieqs
   | Iconst _ | Ibinop _ | Iunsupported -> assert false
+    (* TODO: check this assert false carefully *)
-- 
GitLab