From 41ce82a4010409a76e90043e6a2e128f8d608961 Mon Sep 17 00:00:00 2001
From: Andre Maroneze <andre.oliveiramaroneze@cea.fr>
Date: Tue, 26 Mar 2019 20:02:11 +0100
Subject: [PATCH] [Eva] keep real lower/upper bounds for floating-point
 widening hints

---
 .../abstract_interp/fc_float.ml               | 54 ++++++++++++++-----
 .../abstract_interp/float_sig.mli             |  2 +-
 .../ast_queries/cil_datatype.ml               | 40 ++++++++++++--
 .../ast_queries/cil_datatype.mli              |  2 +
 src/plugins/value/utils/widen.ml              | 24 +++++----
 5 files changed, 94 insertions(+), 28 deletions(-)

diff --git a/src/kernel_services/abstract_interp/fc_float.ml b/src/kernel_services/abstract_interp/fc_float.ml
index 58da42b53ee..5f0b009bab1 100644
--- a/src/kernel_services/abstract_interp/fc_float.ml
+++ b/src/kernel_services/abstract_interp/fc_float.ml
@@ -61,6 +61,7 @@ let cmp_ieee = (compare: float -> float -> int)
 (* replace "noalloc" with [@@noalloc] for OCaml version >= 4.03.0 *)
 [@@@ warning "-3"]
 external compare : float -> float -> int = "float_compare_total" "noalloc"
+let total_compare = compare
 [@@@ warning "+3"]
 
 let of_float round prec f = round >>% fun () -> round_to_precision prec f
@@ -118,7 +119,7 @@ let le f1 f2 = compare f1 f2 <= 0
 
 module Widen_Hints = struct
 
-  include Datatype.Float.Set
+  include Cil_datatype.Logic_real.Set
 
   let pretty fmt s =
     if not (is_empty s) then
@@ -126,29 +127,55 @@ module Widen_Hints = struct
         ~pre:"@[<hov 1>{"
         ~suf:"}@]"
         ~sep:";@ "
-        iter Floating_point.pretty fmt s
+        iter
+        (fun fmt r -> Format.pp_print_string fmt r.Cil_types.r_literal) fmt s
 
-  let of_list l =
+  let logic_real_of_float f =
+    { Cil_types.r_literal = Format.asprintf "%10.7g" f;
+      r_nearest = f;
+      r_lower = f;
+      r_upper = f; }
+
+  let of_float_list l =
     match l with
     | [] -> empty
-    | [e] -> singleton e
+    | [e] -> singleton (logic_real_of_float e)
     | e :: q ->
-      List.fold_left (fun acc x -> add x acc) (singleton e) q
-
-  let m_pi = 3.1415929794311523 (* single-precision *)
-  let m_pi_2 = 1.5707964897155761 (* single-precision *)
-  let max_single_float = Floating_point.max_single_precision_float
+      List.fold_left
+        (fun acc x -> add (logic_real_of_float x) acc)
+        (singleton (logic_real_of_float e)) q
 
   let default_widen_hints =
-    let l = [0.0;1.0;m_pi_2;m_pi;10.;1e10;max_single_float;1e80] in
-    union (of_list l) (of_list (List.map (fun x -> -. x) l))
+    let l = [0.0;1.0;10.0;1e10;Floating_point.max_single_precision_float;1e80] in
+    union (of_float_list l) (of_float_list (List.map (fun x -> -. x) l))
+
+  exception Found of float
+
+  let nearest_float_ge f s =
+    try
+      iter (fun e ->
+          if total_compare e.Cil_types.r_upper f >= 0
+          then raise (Found e.Cil_types.r_upper))
+        s;
+      raise Not_found
+    with Found r -> r
+
+  let nearest_float_le f s =
+    try
+      let els_desc = List.rev (elements s) in
+      List.iter (fun e ->
+          if total_compare e.Cil_types.r_lower f <= 0
+          then raise (Found e.Cil_types.r_lower))
+        els_desc;
+      raise Not_found
+    with Found r -> r
 
 end
 
 type widen_hints = Widen_Hints.t
 
 let widen_up wh prec f =
-  let r = try Widen_Hints.nearest_elt_ge f wh
+  let r = try Widen_Hints.nearest_float_ge f wh
     with Not_found ->
       if le f max_float then max_float
       else infinity
@@ -156,14 +183,13 @@ let widen_up wh prec f =
   round_to_precision Up prec r
 
 let widen_down wh prec f =
-  let r = try Widen_Hints.nearest_elt_le f wh
+  let r = try Widen_Hints.nearest_float_le f wh
     with Not_found ->
       if le (-. max_float) f then (-. max_float)
       else neg_infinity
   in
   round_to_precision Down prec r
 
-
 let neg = (~-.)
 let abs = abs_float
 
diff --git a/src/kernel_services/abstract_interp/float_sig.mli b/src/kernel_services/abstract_interp/float_sig.mli
index 5febce60c3d..50ade7d97da 100644
--- a/src/kernel_services/abstract_interp/float_sig.mli
+++ b/src/kernel_services/abstract_interp/float_sig.mli
@@ -32,7 +32,7 @@ type prec = Single | Double | Long_Double | Real
 
 
 module type Widen_Hints = sig
-  include FCSet.S with type elt = Datatype.Float.t
+  include FCSet.S with type elt = Cil_datatype.Logic_real.t
   include Datatype.S with type t:=t
 
   val default_widen_hints: t
diff --git a/src/kernel_services/ast_queries/cil_datatype.ml b/src/kernel_services/ast_queries/cil_datatype.ml
index 49792655353..f0b07d285ac 100644
--- a/src/kernel_services/ast_queries/cil_datatype.ml
+++ b/src/kernel_services/ast_queries/cil_datatype.ml
@@ -1446,15 +1446,26 @@ let is_exact_float r =
   classify_float r.r_upper = FP_normal &&
   Datatype.Float.equal r.r_upper r.r_lower
 
+[@@@ warning "-3"]
+(* [float_compare_total] is used to ensure -0.0 and 0.0 are distinct *)
+external float_compare_total : float -> float -> int = "float_compare_total" "noalloc"
+[@@@ warning "+3"]
+
+let compare_logic_real r1 r2 =
+  let c = float_compare_total r1.r_lower r2.r_lower in
+  if c <> 0 then c else
+    let c = float_compare_total r1.r_nearest r2.r_nearest in
+    if c <> 0 then c else
+      let c = float_compare_total r1.r_upper r2.r_upper in
+      if c <> 0 then c else
+        String.compare r1.r_literal r2.r_literal
+
 let compare_logic_constant c1 c2 = match c1,c2 with
   | Integer (i1,_), Integer(i2,_) -> Integer.compare i1 i2
   | LStr s1, LStr s2 -> Datatype.String.compare s1 s2
   | LWStr s1, LWStr s2 -> compare_list Datatype.Int64.compare s1 s2
   | LChr c1, LChr c2 -> Datatype.Char.compare c1 c2
-  | LReal r1, LReal r2 ->
-    if is_exact_float r1 && is_exact_float r2
-    then Datatype.Float.compare r1.r_lower r2.r_lower
-    else Datatype.String.compare r1.r_literal r2.r_literal
+  | LReal r1, LReal r2 -> compare_logic_real r1 r2
   | LEnum e1, LEnum e2 -> Enumitem.compare e1 e2
   | Integer _,(LStr _|LWStr _ |LChr _|LReal _|LEnum _) -> 1
   | LStr _ ,(LWStr _ |LChr _|LReal _|LEnum _) -> 1
@@ -1882,6 +1893,27 @@ module Logic_label = struct
     end)
 end
 
+module Logic_real = struct
+  let pretty_ref = ref (fun _ _ -> assert false)
+  include Make_with_collections
+            (struct
+              type t = logic_real
+              let name = "Logic_real"
+              let reprs =
+                [{ r_literal = ""; r_nearest = 0.0; r_lower = 0.0; r_upper = 0.0; }]
+              let compare = compare_logic_real
+              let hash r =
+                let fhash = Datatype.Float.hash in
+                fhash r.r_lower + 3 * fhash r.r_nearest + 7 * fhash r.r_upper +
+                  11 * Datatype.String.hash r.r_literal
+              let equal r1 r2 = compare r1 r2 = 0
+              let copy = Datatype.undefined
+              let internal_pretty_code = Datatype.undefined
+              let pretty fmt t = !pretty_ref fmt t
+              let varname _ = "logic_real"
+            end)
+end
+
 module Global_annotation = struct
   let pretty_ref = ref (fun _ -> assert false)
   include Make_with_collections
diff --git a/src/kernel_services/ast_queries/cil_datatype.mli b/src/kernel_services/ast_queries/cil_datatype.mli
index f2552c6d88b..983c6ba3a37 100644
--- a/src/kernel_services/ast_queries/cil_datatype.mli
+++ b/src/kernel_services/ast_queries/cil_datatype.mli
@@ -279,6 +279,8 @@ module Term_lhost: S_with_collections_pretty with type t = term_lhost
 module Term_offset: S_with_collections_pretty with type t = term_offset
 module Term_lval: S_with_collections_pretty with type t = term_lval
 
+module Logic_real: S_with_collections_pretty with type t = logic_real
+
 module Predicate: S_with_pretty with type t = predicate
 module Identified_predicate: 
   S_with_collections_pretty with type t = identified_predicate
diff --git a/src/plugins/value/utils/widen.ml b/src/plugins/value/utils/widen.ml
index 94d70a29988..8bd8db47b52 100644
--- a/src/plugins/value/utils/widen.ml
+++ b/src/plugins/value/utils/widen.ml
@@ -34,12 +34,18 @@ let dkey = Widen_hints_ext.dkey
    reuse loop indexes...
 *)
 
-let rec constFoldTermToNearestFloat = function
-  | TConst (LReal r) -> Some (r.r_nearest)
+let rec constFoldTermToLogicReal = function
+  | TConst (LReal r) -> Some r
   | TUnOp (Neg,e) -> begin
-      match (constFoldTermToNearestFloat e.term_node) with
+      match (constFoldTermToLogicReal e.term_node) with
       | None -> None
-      | Some e -> Some (-. e)
+      | Some e -> Some { r_literal =
+                           if String.get e.r_literal 0 = '-' then
+                             String.sub e.r_literal 1 (String.length e.r_literal - 1)
+                           else "-" ^ e.r_literal;
+                         r_nearest = -. e.r_nearest;
+                         r_lower = -. e.r_upper;
+                         r_upper = -. e.r_lower; }
     end
   | _ -> None
 
@@ -87,7 +93,7 @@ class pragma_widen_visitor init_widen_hints init_enclosing_loops = object(self)
           | { term_node= TConst (Integer(v,_))} ->
             (lv, Ival.Widen_Hints.add v lint, lfloat, lt)
           | _ ->
-            match constFoldTermToNearestFloat t.term_node with
+            match constFoldTermToLogicReal t.term_node with
             | Some f -> (lv, lint, Fc_float.Widen_Hints.add f lfloat, lt)
             | None -> (lv, lint, lfloat, t::lt)
         in
@@ -277,7 +283,7 @@ let base_of_static_hvars hvars =
     (* syntactic constraints prevent this from happening *)
     Value_parameters.fatal "unsupported lhost: %a" Printer.pp_lval (Mem e, offset)
 
-type threshold = Int_th of Integer.t | Float_th of float
+type threshold = Int_th of Integer.t | Real_th of logic_real
 
 (* try parsing as int, then as float *)
 let threshold_of_threshold_term ht =
@@ -290,8 +296,8 @@ let threshold_of_threshold_term ht =
   match Logic_utils.constFoldTermToInt ht with
   | Some i -> Int_th i
   | None ->
-    match constFoldTermToNearestFloat ht.term_node with
-    | Some f -> Float_th f
+    match constFoldTermToLogicReal ht.term_node with
+    | Some f -> Real_th f
     | None ->
       Value_parameters.abort ~source:(fst ht.term_loc)
         "could not parse widening hint: %a@ \
@@ -310,7 +316,7 @@ let thresholds_of_threshold_terms hts =
             Printer.pp_term ht;
         has_int := true;
         Ival.Widen_Hints.add i int_acc, float_acc
-      | Float_th f ->
+      | Real_th f ->
         if !has_int then
           Value_parameters.abort ~source:(fst ht.term_loc)
             "widening hint mixing integers and floats: %a"
-- 
GitLab