From 916538a344351afa29c69457df4a596820888251 Mon Sep 17 00:00:00 2001
From: Valentin Perrelle <valentin.perrelle@cea.fr>
Date: Wed, 1 Jul 2020 21:06:26 +0200
Subject: [PATCH] [dive] simplify and generalize the computation of values &
 range rating

---
 headers/header_spec.txt                       |   8 +-
 ivette/src/renderer/Dive.tsx                  |  51 +-------
 ivette/src/renderer/dive_style.js             |  45 ++-----
 src/plugins/dive/Makefile.in                  |   2 +-
 src/plugins/dive/build.ml                     |  98 +-------------
 src/plugins/dive/dive_graph.ml                | 121 ++++--------------
 src/plugins/dive/dive_graph.mli               |   3 +-
 src/plugins/dive/dive_types.mli               |  18 +--
 src/plugins/dive/node_range.ml                | 103 +++++++++++++++
 src/plugins/dive/node_range.mli               |  26 ++++
 src/plugins/dive/server_interface.ml          |   4 +-
 src/plugins/dive/tests/dive/oracle/ranges.dot |  62 +++++++++
 .../dive/tests/dive/oracle/ranges.res.oracle  |  25 ++++
 .../dive/tests/dive/oracle/various.dot        |   3 +-
 src/plugins/dive/tests/dive/ranges.i          |  39 ++++++
 15 files changed, 315 insertions(+), 293 deletions(-)
 create mode 100644 src/plugins/dive/node_range.ml
 create mode 100644 src/plugins/dive/node_range.mli
 create mode 100644 src/plugins/dive/tests/dive/oracle/ranges.dot
 create mode 100644 src/plugins/dive/tests/dive/oracle/ranges.res.oracle
 create mode 100644 src/plugins/dive/tests/dive/ranges.i

diff --git a/headers/header_spec.txt b/headers/header_spec.txt
index 7fdecae8316..86d3a36c6be 100644
--- a/headers/header_spec.txt
+++ b/headers/header_spec.txt
@@ -768,14 +768,16 @@ src/plugins/dive/build.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/dive/callstack.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/dive/callstack.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/dive/configure.ac: CEA_LGPL_OR_PROPRIETARY
+src/plugins/dive/dive_graph.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/dive/dive_graph.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/dive/dive_types.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/dive/Dive.mli: CEA_LGPL_OR_PROPRIETARY
-src/plugins/dive/graph_types.mli: CEA_LGPL_OR_PROPRIETARY
-src/plugins/dive/imprecision_graph.ml: CEA_LGPL_OR_PROPRIETARY
-src/plugins/dive/imprecision_graph.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/dive/main.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/dive/Makefile.in: CEA_LGPL_OR_PROPRIETARY
 src/plugins/dive/node_kind.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/dive/node_kind.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/dive/node_range.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/dive/node_range.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/dive/self.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/dive/self.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/dive/server_interface.ml: CEA_LGPL_OR_PROPRIETARY
diff --git a/ivette/src/renderer/Dive.tsx b/ivette/src/renderer/Dive.tsx
index 1065838cea2..48b76da473c 100644
--- a/ivette/src/renderer/Dive.tsx
+++ b/ivette/src/renderer/Dive.tsx
@@ -1,5 +1,3 @@
-import { strict as assert } from 'assert';
-
 import React, { useState, useEffect } from 'react';
 import { renderToString } from 'react-dom/server';
 import * as Dome from 'dome';
@@ -57,39 +55,6 @@ function callstackToString(callstack: callstack): string {
   return callstack.map((cs) => `${cs.fun}:${cs.instr}`).join('/');
 }
 
-function nodeToIntervalString(node: Cytoscape.NodeSingular): string
-{
-  const data = node.data();
-  let interval;
-
-  if (data.float_values) {
-    interval = data.float_values.computed;
-  }
-  else if (data.int_values) {
-    interval = data.int_values.computed;
-  }
-
-  return `[${interval.min} ; ${interval.max}]`;
-}
-
-function range(interval: Interval, limit: Interval): number
-{
-  const l = Math.max(Math.abs(limit.min), Math.abs(limit.max));
-  const x = Math.max(Math.abs(interval.min), Math.abs(interval.max));
-
-  if (x === Infinity) {
-    return 100;
-  }
-
-  if (x <= Math.E) {
-    return 1;
-  }
-
-  const r = Math.log(Math.log(x)) / Math.log(Math.log(l));
-  assert(0.0 <= r && r <= 1.0);
-  return Math.max(1, Math.min(100, Math.floor(r * 100)));
-}
-
 
 export type mode = 'explore' | 'overview';
 
@@ -227,10 +192,10 @@ class Dive {
 
     const tips = [];
 
-    if (node.data().float_values || node.data().int_values) {
+    if (node.data().values) {
       tips.push(tippy(container, {
         ...common,
-        content: nodeToIntervalString(node),
+        content: node.data().values,
         placement: 'top',
         distance: 10,
         arrow: true,
@@ -275,16 +240,8 @@ class Dive {
 
     for (const node of data.nodes)
     {
-      if (node.float_values) {
-        const { computed, limits } = node.float_values;
-        node.float_range = range(computed, limits);
-        node.grade = node.float_values.grade;
-      }
-      else if (node.int_values) {
-        const { computed, limits } = node.int_values;
-        node.int_range = range(computed, limits);
-        node.grade = node.int_values.grade;
-      }
+      if (typeof node.range === 'number')
+        node.stops = `0% ${node.range}% ${node.range}% 100%`;
 
       const previous = this.cy.$id(node.id);
       if (previous.nonempty()) {
diff --git a/ivette/src/renderer/dive_style.js b/ivette/src/renderer/dive_style.js
index 56a3d286fef..5cc034a89ec 100644
--- a/ivette/src/renderer/dive_style.js
+++ b/ivette/src/renderer/dive_style.js
@@ -74,49 +74,28 @@ export default [
     }
   },
   {
-    selector: 'node[float_range]',
+    selector: 'node[stops]',
     style: {
+      'border-color': '#8e6',
+      'background-gradient-stop-colors': '#5d3 #5d3 #9f7 #9f7',
       'background-width-relative-to': 'include-padding',
-      backgroundFill: 'linear-gradient',
-      backgroundGradientDirection: 'to-left',
-      backgroundGradientStopPositions: (ele) => {
-        let r = ele.data('float_range') / 2;
-        return `0% 0% ${r}% ${100-r}% 100% 100%`;
-      }
+      'background-fill': 'linear-gradient',
+      'background-gradient-direction': 'to-top',
+      'background-gradient-stop-positions': 'data(stops)'
     }
   },
   {
-    selector: 'node[int_range]',
+    selector: 'node[range="singleton"]',
     style: {
-      'background-width-relative-to': 'include-padding',
-      backgroundFill: 'linear-gradient',
-      backgroundGradientDirection: 'to-left',
-      backgroundGradientStopPositions: (ele) => {
-        let r = ele.data('int_range') / 2;
-        return `0% ${r}% ${r}% ${100-r}% ${100-r}% 100%`;
-      }
-    }
-  },
-  {
-    selector: 'node[grade="singleton"]',
-    style: {
-      'background-gradient-stop-colors': '#acf #acf #acf #acf #acf #acf',
-      'background-color': '#acf',
-      'border-color': '#8af'
-    }
-  },
-  {
-    selector: 'node[grade="normal"]',
-    style: {
-      'background-gradient-stop-colors': '#4c2 #4c2 #bea #bea #4c2 #4c2',
-      'border-color': '#898'
+      'border-color': '#8af',
+      'background-color': '#acf'
     }
   },
   {
-    selector: 'node[grade="wide"]',
+    selector: 'node[range="wide"]',
     style: {
-      'background-gradient-stop-colors': '#e44 #e44 #faa #faa #e44 #e44',
-      'border-color': '#f88'
+      'border-color': '#f88',
+      'background-color': '#e44'
     }
   },
   {
diff --git a/src/plugins/dive/Makefile.in b/src/plugins/dive/Makefile.in
index 564a285aa3c..3847f07858b 100644
--- a/src/plugins/dive/Makefile.in
+++ b/src/plugins/dive/Makefile.in
@@ -39,7 +39,7 @@ endif
 PLUGIN_DIR ?=.
 PLUGIN_ENABLE:=@ENABLE_DIVE@
 PLUGIN_NAME := Dive
-PLUGIN_CMO := self callstack node_kind dive_graph build main \
+PLUGIN_CMO := self callstack node_kind node_range dive_graph build main \
   server_interface
 PLUGIN_CMI := dive_types
 PLUGIN_DEPENDENCIES:= Eva Studia Server
diff --git a/src/plugins/dive/build.ml b/src/plugins/dive/build.ml
index bd8b9d01028..80018f10e2d 100644
--- a/src/plugins/dive/build.ml
+++ b/src/plugins/dive/build.ml
@@ -42,107 +42,11 @@ let rec list_break n l =
 
 (* --- Precision evaluation --- *)
 
-let _fval_contains_maximal_bounds fkind fval =
-  let top = Fval.top_finite (Fval.kind fkind) in
-  Fval.has_greater_min_bound top fval >= 0 ||
-  Fval.has_smaller_max_bound top fval >= 0
-
-let fkind_limits =
-  let max_single = float_of_string "0x1.fffffep+127"
-  and max_double = float_of_string "0x1.fffffffffffffp+1023" in
-  let single_limits = { min = -. max_single ;  max = max_single }
-  and double_limits =  { min = -. max_double ;  max = max_double } in
-  function
-  | FFloat      -> single_limits
-  | FDouble     -> double_limits
-  | FLongDouble -> assert false
-
-let float_grade_limits =
-  let single = float_of_string "0x1p+120"
-  and double = float_of_string "0x1p+960"
-  and long_double = float_of_string "0x1p+15360"
-  in function
-    | FFloat      -> single
-    | FDouble     -> double
-    | FLongDouble -> long_double
-
-let is_large_float_range fkind (min,max) =
-  let limit = float_grade_limits fkind in
-  if (min < 0.0) = (max < 0.0) then (* if bounds have same sign *)
-    max -. min >= limit
-  else
-    min <= -.limit || max >= limit
-
-let float_grade fkind (min,max) =
-  if min = max then
-    Singleton
-  else if is_large_float_range fkind (min,max) then
-    Wide
-  else
-    Normal
-
-let ikind_limits ikind =
-  let open Cil in
-  let bits = bitsSizeOfInt ikind in
-  if isSigned ikind then
-    { min=min_signed_number bits; max=max_signed_number bits }
-  else
-    { min=Integer.zero; max=max_unsigned_number bits }
-
-let int_grade_limits ikind =
-  let bits = Cil.bitsSizeOfInt ikind in
-  Integer.(pred (two_power_of_int (bits - bits / 8)))
-
-let is_large_int_range ikind (l,u) =
-  let limit = int_grade_limits ikind in
-  if Integer.(lt l zero) = Integer.(lt u zero) then (* if bounds have same sign *)
-    Integer.(ge (sub u l) limit)
-  else
-    Integer.(le l (neg limit)) || Integer.(ge u limit)
-
-let int_grade ikind (min,max) =
-  if min = max then
-    Singleton
-  else if is_large_int_range ikind (min,max) then
-    Wide
-  else
-    Normal
-
-
 let update_node_values node kinstr lval =
   let typ = Cil.typeOfLval lval in
   let state = Db.Value.get_state kinstr in
   let _,cvalue = !Db.Value.eval_lval None state lval in
-  try
-    let ival = Cvalue.V.project_ival cvalue in
-    match typ with
-    | TInt (ikind,_) ->
-      let size =  Integer.of_int (Cil.bitsSizeOfInt ikind)
-      and signed = Cil.isSigned ikind in
-      let ival = Ival.reinterpret_as_int ~size ~signed ival in
-      let min, max = match Ival.min_and_max ival with
-        | Some min, Some max -> min, max
-        | _, _ -> assert false (* ival have been reinterpreted *)
-      in
-      Graph.update_node_int_values node {
-        values_interval = {min;max};
-        values_limits = ikind_limits ikind;
-        values_grade = int_grade ikind (min,max)
-      }
-
-    | TFloat (fkind,_) ->
-      begin match Ival.min_and_max_float ival with
-        | None, _can_be_nan -> ()
-        | Some (min, max), _can_be_nan ->
-          let min = Fval.F.to_float min and max = Fval.F.to_float max in
-          Graph.update_node_float_values node {
-            values_interval = {min;max};
-            values_limits = fkind_limits fkind;
-            values_grade = float_grade fkind (min,max);
-          }
-      end
-    | _ -> ()
-  with Cvalue.V.Not_based_on_null -> ()
+  Graph.update_node_values node cvalue typ
 
 
 (* --- Locations handling --- *)
diff --git a/src/plugins/dive/dive_graph.ml b/src/plugins/dive/dive_graph.ml
index 7b8b8162ad7..c8ecf5d5000 100644
--- a/src/plugins/dive/dive_graph.ml
+++ b/src/plugins/dive/dive_graph.ml
@@ -64,8 +64,8 @@ let create_node ~node_kind ~node_locality g =
     node_kind;
     node_locality;
     node_hidden = false;
-    node_int_values = None;
-    node_float_values = None;
+    node_values = None;
+    node_range = Empty;
     node_writes_computation = NotDone;
     node_writes_stmts = [];
   }
@@ -75,44 +75,11 @@ let create_node ~node_kind ~node_locality g =
 
 let remove_node = remove_vertex
 
-let union_int_interval i1 i2 =
-  { min = Integer.min i1.min i2.min ; max = Integer.max i1.max i2.max }
-
-let union_float_interval i1 i2 =
-  { min = min i1.min i2.min ; max = max i1.max i2.max }
-
-let worst_precision_grade q1 q2 =
-  match q1, q2 with
-  | Wide, _ | _, Wide -> Wide
-  | Normal, _ | _, Normal -> Normal
-  | Singleton, Singleton -> Singleton
-
-let merge_int_values p1 p2 =
-  (* TODO: prevent assertion failure *)
-  assert (Integer.equal p1.values_limits.min p2.values_limits.min);
-  assert (Integer.equal p1.values_limits.max p2.values_limits.max);
-  {
-    values_interval = union_int_interval p1.values_interval p2.values_interval;
-    values_limits = p1.values_limits;
-    values_grade = worst_precision_grade p1.values_grade p2.values_grade;
-  }
-
-let merge_float_values p1 p2 =
-  (* TODO: prevent assertion failure *)
-  assert (p1.values_limits = p2.values_limits);
-  {
-    values_interval = union_float_interval p1.values_interval p2.values_interval;
-    values_limits = p1.values_limits;
-    values_grade = worst_precision_grade p1.values_grade p2.values_grade;
-  }
-
-let update_node_int_values node new_values =
-  node.node_int_values <-
-    Some (Extlib.opt_fold merge_int_values node.node_int_values new_values)
-
-let update_node_float_values node new_values =
-  node.node_float_values <-
-    Some (Extlib.opt_fold merge_float_values node.node_float_values new_values)
+let update_node_values node new_values typ =
+  node.node_values <-
+    Some (Extlib.opt_fold Cvalue.V.join node.node_values new_values);
+  node.node_range <-
+    Node_range.(upper_bound node.node_range (evaluate new_values typ))
 
 let create_dependency ~allow_folding g kinstr v1 dependency_kind v2 =
   let same_kind (_,e,_) =
@@ -237,13 +204,6 @@ let ouptput_to_dot out_channel g =
       let default_vertex_attributes _g = []
       let vertex_name v = "cp" ^ (string_of_int v.node_key)
       let vertex_attributes v =
-        let grade = match v.node_int_values, v.node_float_values with
-          | Some v1, Some v2 ->
-            Some (worst_precision_grade v1.values_grade v2.values_grade)
-          | Some v, _ -> Some v.values_grade
-          | _, Some v -> Some v.values_grade
-          | None, None -> None
-        in
         let l = ref [] in
         let text = Pretty_utils.to_string Node_kind.pretty v.node_kind in
         if text <> "" then
@@ -258,16 +218,16 @@ let ouptput_to_dot out_channel g =
                           `Style `Filled ; `Fillcolor 0xff0000 ]
           | AbsoluteMemory | String _ -> [`Shape `Box3d]
           | Error _ -> [`Color 0xff0000]
-        and values = match grade with
-          | None -> []
-          | Some Singleton ->
+        and range = match v.node_range with
+          | Empty -> []
+          | Singleton ->
             [`Color 0x88aaff ; `Style `Filled ; `Fillcolor 0xaaccff ]
-          | Some Normal ->
+          | Normal _ ->
             [ `Color 0x004400 ; `Style `Filled ; `Fillcolor 0xeeffee ]
-          | Some Wide ->
+          | Wide ->
             [ `Color 0xff0000 ; `Style `Filled ; `Fillcolor 0xffbbbb ]
         in
-        l := values @ kind @ !l;
+        l := range @ kind @ !l;
         if v.node_writes_computation <> Done then
           l := [ `Style `Dotted ] @ !l;
         !l
@@ -329,13 +289,12 @@ struct
     in
     `Assoc fields
 
-  let output_node_precision_grade grade =
-    let s = match grade with
-      | Singleton -> "singleton"
-      | Normal -> "normal"
-      | Wide -> "wide"
-    in
-    `String s
+  let output_range range =
+    match range with
+    | Empty -> `String "empty"
+    | Singleton -> `String "singleton"
+    | Normal range_grade -> `Int range_grade
+    | Wide -> `String "wide"
 
   let output_dep_kind kind =
     let s = match kind with
@@ -347,32 +306,10 @@ struct
     in
     `String s
 
-  let output_int_interval interval =
-    (* TODO: handle overflow *)
-    `Assoc [
-      ("min", `Int (Integer.to_int interval.min)) ;
-      ("max", `Int (Integer.to_int interval.max)) ;
-    ]
-
-  let output_float_interval interval =
-    `Assoc [
-      ("min", `Float interval.min) ;
-      ("max", `Float interval.max) ;
-    ]
-
-  let output_node_int_values values =
-    `Assoc [
-      ("computed", output_int_interval values.values_interval) ;
-      ("limits", output_int_interval values.values_limits) ;
-      ("grade", output_node_precision_grade values.values_grade) ;
-    ]
-
-  let output_node_float_values values =
-    `Assoc [
-      ("computed", output_float_interval values.values_interval) ;
-      ("limits", output_float_interval values.values_limits) ;
-      ("grade", output_node_precision_grade values.values_grade) ;
-    ]
+  let output_node_values values =
+    match values with
+    | None -> `Null
+    | Some cvalue -> `String (Pretty_utils.to_string Cvalue.V.pretty cvalue)
 
   let output_node node =
     let label = Pretty_utils.to_string Node_kind.pretty node.node_kind in
@@ -383,17 +320,9 @@ struct
         ("locality", output_node_locality node.node_locality) ;
         ("explored", `Bool (node.node_writes_computation = Done)) ;
         ("writes", `List (List.map output_stmt node.node_writes_stmts)) ;
+        ("values",  output_node_values node.node_values) ;
+        ("range",  output_range node.node_range) ;
       ] @
-        begin match node.node_int_values with
-          | None -> []
-          | Some node_values ->
-            [("int_values", output_node_int_values node_values)]
-        end @
-        begin match node.node_float_values with
-          | None -> []
-          | Some node_values ->
-            [("float_values", output_node_float_values node_values)]
-        end @
         begin match Node_kind.to_lval node.node_kind with
           | None -> []
           | Some lval ->
diff --git a/src/plugins/dive/dive_graph.mli b/src/plugins/dive/dive_graph.mli
index 24b50766572..e093170794f 100644
--- a/src/plugins/dive/dive_graph.mli
+++ b/src/plugins/dive/dive_graph.mli
@@ -38,8 +38,7 @@ val create_node :
 
 val remove_node : t -> node -> unit
 
-val update_node_int_values : node -> Integer.t node_values -> unit
-val update_node_float_values : node -> float node_values -> unit
+val update_node_values : node -> Cvalue.V.t -> Cil_types.typ -> unit
 
 val create_dependency : allow_folding:bool -> t -> Cil_types.kinstr ->
   node -> dependency_kind -> node -> unit
diff --git a/src/plugins/dive/dive_types.mli b/src/plugins/dive/dive_types.mli
index 1acada506a3..3fcfe0e87c9 100644
--- a/src/plugins/dive/dive_types.mli
+++ b/src/plugins/dive/dive_types.mli
@@ -35,25 +35,21 @@ type node_locality = {
   loc_callstack : Callstack.t;
 }
 
-type 'a interval = {min: 'a; max: 'a}
-
-type precision_grade = Singleton | Normal | Wide
+type node_range =
+  | Empty (* No values assigned to the node *)
+  | Singleton (* A unique value ever assigned *)
+  | Normal of int (* From 0 = almost singleton to 100 = almost all possible values *)
+  | Wide (* Too many values for the type to be reasonable *)
 
 type 'a computation = NotDone | Partial of 'a | Done
 
-type 'a node_values = {
-  values_interval : 'a interval;
-  values_limits : 'a interval;
-  values_grade : precision_grade;
-}
-
 type node = {
   node_key : int;
   node_kind : node_kind;
   node_locality : node_locality;
   mutable node_hidden : bool;
-  mutable node_int_values : (Integer.t node_values) option;
-  mutable node_float_values : (float node_values) option;
+  mutable node_values : Cvalue.V.t option;
+  mutable node_range : node_range;
   mutable node_writes_computation : (Cil_types.stmt list) computation;
   mutable node_writes_stmts : Cil_types.stmt list;
 }
diff --git a/src/plugins/dive/node_range.ml b/src/plugins/dive/node_range.ml
new file mode 100644
index 00000000000..f93d3b73ab6
--- /dev/null
+++ b/src/plugins/dive/node_range.ml
@@ -0,0 +1,103 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2020                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Dive_types
+
+type t = node_range
+
+let fkind_limits =
+  let max_single = float_of_string "0x1.fffffep+127"
+  and max_double = float_of_string "0x1.fffffffffffffp+1023" in
+  function
+  | Cil_types.FFloat -> max_single
+  | FDouble | FLongDouble -> max_double
+
+let ikind_limits ikind =
+  let open Cil in
+  let bits = bitsSizeOfInt ikind in
+  if isSigned ikind then
+    (min_signed_number bits, max_signed_number bits)
+  else
+    (Integer.zero, max_unsigned_number bits)
+
+let logscale x limit =
+  Float.(to_int (min 100. (100. *. log (max x 1.) /. log limit)))
+
+let log2scale x limit =
+  assert (x > 0.);
+  assert (limit > 0.);
+  Float.(logscale (log x) (log limit))
+
+let interval_range l u limit =
+  let range =
+    if (l < 0.0) = (u < 0.0) then (* if bounds have same sign *)
+      u -. l
+    else
+      Float.(max (abs u) (abs l))
+  in
+  let s = log2scale range limit in
+  if s > 98 then Wide else Normal s
+
+let integer_range ikind l u =
+  let _, limit = ikind_limits ikind
+  and l = Integer.to_float l
+  and u = Integer.to_float u in
+  interval_range l u (Integer.to_float limit)
+
+let float_range fkind l u =
+  let limit = fkind_limits fkind
+  and l = Fval.F.to_float l
+  and u = Fval.F.to_float u in
+  interval_range l u limit
+
+let default_range n =
+  Normal (logscale (Integer.to_float n) 100.)
+
+let evaluate cvalue typ =
+  try
+    let size = Integer.of_int (Cil.bitsSizeOf typ) in
+    let cardinal = Cvalue.V.cardinal_estimate ~size cvalue in
+    match typ with
+    | _ when Integer.is_zero cardinal -> Empty
+    | _ when Integer.is_one cardinal -> Singleton
+    | TInt (ikind,_) ->
+      begin match Ival.min_and_max (Cvalue.V.project_ival cvalue) with
+        | Some l, Some u -> integer_range ikind l u
+        | _, _ | exception Cvalue.V.Not_based_on_null -> Wide
+        | exception Abstract_interp.Error_Bottom -> Empty
+      end
+    | TFloat (fkind,_) ->
+      begin match Ival.min_and_max_float (Cvalue.V.project_ival cvalue) with
+        | Some (l, u), _can_be_nan -> float_range fkind l u
+        | _, _ | exception Cvalue.V.Not_based_on_null -> Wide
+        | exception Abstract_interp.Error_Bottom -> Empty
+      end
+    | _ ->
+      default_range cardinal
+  with Cil.SizeOfError _ -> Empty
+
+let upper_bound r1 r2 =
+  match r1, r2 with
+  | Empty, r | r, Empty -> r
+  | Singleton, r | r, Singleton -> r
+  | Normal i1, Normal i2 -> Normal (max i1 i2)
+  | Wide, _ | _, Wide -> Wide
diff --git a/src/plugins/dive/node_range.mli b/src/plugins/dive/node_range.mli
new file mode 100644
index 00000000000..1f8f3f24b85
--- /dev/null
+++ b/src/plugins/dive/node_range.mli
@@ -0,0 +1,26 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2020                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+type t = Dive_types.node_range
+
+val evaluate : Cvalue.V.t -> Cil_types.typ -> t
+val upper_bound : t -> t -> t
diff --git a/src/plugins/dive/server_interface.ml b/src/plugins/dive/server_interface.ml
index 4fe0960a05a..847558a2db7 100644
--- a/src/plugins/dive/server_interface.ml
+++ b/src/plugins/dive/server_interface.ml
@@ -201,8 +201,8 @@ struct
       "locality", NodeLocality.syntax;
       "explored", Syntax.boolean;
       "writes", Syntax.array Kernel_ast.Marker.syntax;
-      "int_values", Syntax.any;
-      "float_values", Syntax.any;
+      "values", Syntax.option Syntax.string;
+      "range", Syntax.union [ Syntax.string ; Syntax.int ];
       "type", Syntax.option Syntax.string
     ]
 
diff --git a/src/plugins/dive/tests/dive/oracle/ranges.dot b/src/plugins/dive/tests/dive/oracle/ranges.dot
new file mode 100644
index 00000000000..93374387479
--- /dev/null
+++ b/src/plugins/dive/tests/dive/oracle/ranges.dot
@@ -0,0 +1,62 @@
+digraph G {
+  cp1 [label=<res>, shape=box, ];
+  cp2 [label=<i>, shape=box, fillcolor="#FFBBBB", color="#FF0000",
+       style="filled", ];
+  cp4 [label=<f>, shape=box, fillcolor="#FFBBBB", color="#FF0000",
+       style="filled", ];
+  cp6 [label=<i0>, shape=box, fillcolor="#AACCFF", color="#88AAFF",
+       style="filled", ];
+  cp8 [label=<i1>, shape=box, fillcolor="#EEFFEE", color="#004400",
+       style="filled", ];
+  cp10 [label=<i2>, shape=box, fillcolor="#EEFFEE", color="#004400",
+        style="filled", ];
+  cp12 [label=<i3>, shape=box, fillcolor="#EEFFEE", color="#004400",
+        style="filled", ];
+  cp14 [label=<i4>, shape=box, fillcolor="#EEFFEE", color="#004400",
+        style="filled", ];
+  cp16 [label=<i5>, shape=box, fillcolor="#EEFFEE", color="#004400",
+        style="filled", ];
+  cp18 [label=<i6>, shape=box, fillcolor="#EEFFEE", color="#004400",
+        style="filled", ];
+  cp20 [label=<i7>, shape=box, fillcolor="#FFBBBB", color="#FF0000",
+        style="filled", ];
+  cp22 [label=<f0>, shape=box, fillcolor="#AACCFF", color="#88AAFF",
+        style="filled", ];
+  cp24 [label=<f1>, shape=box, fillcolor="#EEFFEE", color="#004400",
+        style="filled", ];
+  cp26 [label=<f2>, shape=box, fillcolor="#EEFFEE", color="#004400",
+        style="filled", ];
+  cp28 [label=<f3>, shape=box, fillcolor="#EEFFEE", color="#004400",
+        style="filled", ];
+  cp30 [label=<f4>, shape=box, fillcolor="#EEFFEE", color="#004400",
+        style="filled", ];
+  cp32 [label=<f5>, shape=box, fillcolor="#EEFFEE", color="#004400",
+        style="filled", ];
+  cp34 [label=<f6>, shape=box, fillcolor="#EEFFEE", color="#004400",
+        style="filled", ];
+  cp36 [label=<f7>, shape=box, fillcolor="#FFBBBB", color="#FF0000",
+        style="filled", ];
+  
+  subgraph cluster_cs_1 { label=<main>; cp36;cp34;cp32;cp30;cp28;cp26;cp24;cp22;cp20;cp18;cp16;cp14;cp12;cp10;cp8;cp6;cp4;cp2;cp1;
+     };
+  
+  cp2 -> cp1;
+  cp4 -> cp1;
+  cp6 -> cp2;
+  cp8 -> cp2;
+  cp10 -> cp2;
+  cp12 -> cp2;
+  cp14 -> cp2;
+  cp16 -> cp2;
+  cp18 -> cp2;
+  cp20 -> cp2;
+  cp22 -> cp4;
+  cp24 -> cp4;
+  cp26 -> cp4;
+  cp28 -> cp4;
+  cp30 -> cp4;
+  cp32 -> cp4;
+  cp34 -> cp4;
+  cp36 -> cp4;
+  
+  }
\ No newline at end of file
diff --git a/src/plugins/dive/tests/dive/oracle/ranges.res.oracle b/src/plugins/dive/tests/dive/oracle/ranges.res.oracle
new file mode 100644
index 00000000000..8e49661f1ec
--- /dev/null
+++ b/src/plugins/dive/tests/dive/oracle/ranges.res.oracle
@@ -0,0 +1,25 @@
+[kernel] Parsing tests/dive/ranges.i (no preprocessing)
+[eva] Analyzing a complete application starting at main
+[eva] Computing initial state
+[eva] Initial state computed
+[eva] using specification for function Frama_C_interval
+[eva:alarm] tests/dive/ranges.i:25: Warning: 
+  signed overflow.
+  assert
+  (int)((int)((int)((int)((int)((int)(i0 + i1) + i2) + i3) + i4) + i5) + i6) +
+  i7 ≤ 2147483647;
+[eva] using specification for function Frama_C_float_interval
+[eva] done for function main
+[eva:summary] ====== ANALYSIS SUMMARY ======
+  ----------------------------------------------------------------------------
+  1 function analyzed (out of 1): 100% coverage.
+  In this function, 20 statements reached (out of 20): 100% coverage.
+  ----------------------------------------------------------------------------
+  No errors or warnings raised during the analysis.
+  ----------------------------------------------------------------------------
+  1 alarm generated by the analysis:
+       1 integer overflow
+  ----------------------------------------------------------------------------
+  No logical properties have been reached by the analysis.
+  ----------------------------------------------------------------------------
+[dive] output to tests/dive/result/ranges.dot
diff --git a/src/plugins/dive/tests/dive/oracle/various.dot b/src/plugins/dive/tests/dive/oracle/various.dot
index 019cbcbc7c7..10fcc013eb8 100644
--- a/src/plugins/dive/tests/dive/oracle/various.dot
+++ b/src/plugins/dive/tests/dive/oracle/various.dot
@@ -18,7 +18,8 @@ digraph G {
         style="filled", ];
   cp19 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000",
         style="filled", ];
-  cp22 [label=<pf>, shape=box, ];
+  cp22 [label=<pf>, shape=box, fillcolor="#AACCFF", color="#88AAFF",
+        style="filled", ];
   cp24 [label=<x2>, shape=box, fillcolor="#FFBBBB", color="#FF0000",
         style="filled", ];
   cp27 [label=<y>, shape=box, fillcolor="#FFBBBB", color="#FF0000",
diff --git a/src/plugins/dive/tests/dive/ranges.i b/src/plugins/dive/tests/dive/ranges.i
new file mode 100644
index 00000000000..221deca7bc7
--- /dev/null
+++ b/src/plugins/dive/tests/dive/ranges.i
@@ -0,0 +1,39 @@
+/* run.config
+STDOPT: +"-dive-from-variables main::res -dive-depth-limit 4 -kernel-warn-key parser:decimal-float=inactive"
+*/
+
+/*@ assigns \result \from min, max;
+    ensures result_bounded: min <= \result <= max ;
+ */
+extern int Frama_C_interval(int min, int max);
+
+/*@ assigns \result \from min, max;
+    ensures result_bounded: \is_finite(\result) && min <= \result <= max;
+ */
+extern float Frama_C_float_interval(float min, float max);
+
+float main()
+{
+  int i0 = 2;
+  int i1 = Frama_C_interval(0, 3);
+  int i2 = Frama_C_interval(0, 12);
+  int i3 = Frama_C_interval(0, 127);
+  int i4 = Frama_C_interval(0, 42000);
+  int i5 = Frama_C_interval(0, 1350000);
+  int i6 = Frama_C_interval(0, 910000000);
+  int i7 = Frama_C_interval(0, 2000000000);
+  int i = i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7;
+
+  float f0 = 0.;
+  float f1 = Frama_C_float_interval(0.f, 0.001f);
+  float f2 = Frama_C_float_interval(0.f, 3.14f);
+  float f3 = Frama_C_float_interval(0.f, 1020.f);
+  float f4 = Frama_C_float_interval(0.f, 1e7f);
+  float f5 = Frama_C_float_interval(0.f, 1e20f);
+  float f6 = Frama_C_float_interval(0.f, 1e36f);
+  float f7 = Frama_C_float_interval(0.f, 1e37f);
+  float f = f0 + f1 + f2 + f3 + f4 + f5 + f6 + f7;
+
+  float res = i + f;
+  return res;
+}
-- 
GitLab