From 447df042773724cdf7aa3cf0afdfd1c696c1ff11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr>
Date: Thu, 9 Jan 2025 16:11:09 +0100
Subject: [PATCH] [Eva] Replaces environment variable FRAMA_C_MEMORY_FOOTPRINT
 by an Eva parameter.

The new parameter is -eva-cache-size.
---
 share/analysis-scripts/analysis.mk   |   1 +
 share/analysis-scripts/prologue.mk   |   5 -
 src/libraries/utils/binary_cache.ml  | 220 ++++++++++++++-------------
 src/libraries/utils/binary_cache.mli |   4 +
 src/plugins/eva/parameters.ml        |  18 +++
 5 files changed, 137 insertions(+), 111 deletions(-)

diff --git a/share/analysis-scripts/analysis.mk b/share/analysis-scripts/analysis.mk
index 73de1f4ccd1..9165754c380 100644
--- a/share/analysis-scripts/analysis.mk
+++ b/share/analysis-scripts/analysis.mk
@@ -125,6 +125,7 @@ EVAFLAGS   ?= \
   -no-deps-print -no-calldeps-print \
   -eva-warn-key garbled-mix=active,garbled-mix:write=active \
   -calldeps -from-verbose 0 \
+	-eva-cache-size 8 \
   $(if $(EVABUILTINS), -eva-builtin=$(call fc_list,$(EVABUILTINS)),) \
   $(if $(EVAUSESPECS), -eva-use-spec $(call fc_list,$(EVAUSESPECS)),)
 WPFLAGS    ?=
diff --git a/share/analysis-scripts/prologue.mk b/share/analysis-scripts/prologue.mk
index 7a3b4d65cac..60f7a93e163 100644
--- a/share/analysis-scripts/prologue.mk
+++ b/share/analysis-scripts/prologue.mk
@@ -26,11 +26,6 @@
 # Note: this variable must be defined before including any files
 makefile_dir := $(dir $(lastword $(MAKEFILE_LIST)))
 
-## Useful definitions (to be overridden later if needed)
-
-# Improves analysis time, at the cost of extra memory usage
-export FRAMA_C_MEMORY_FOOTPRINT = 8
-
 # analysis.mk contains the main rules and targets
 include $(makefile_dir)/analysis.mk
 
diff --git a/src/libraries/utils/binary_cache.ml b/src/libraries/utils/binary_cache.ml
index 97b3f40d131..9b1e58c5d00 100644
--- a/src/libraries/utils/binary_cache.ml
+++ b/src/libraries/utils/binary_cache.ml
@@ -20,25 +20,15 @@
 (*                                                                        *)
 (**************************************************************************)
 
-let memory_footprint_var_name = "FRAMA_C_MEMORY_FOOTPRINT"
-
-let memory_footprint =
-  let error () =
-    Cmdline.Kernel_log.error
-      "@[Bad value for environment variable@ %s.@ Expected value: \
-       integer between@ 1 and 10.@ Using@ default value@ of 2.@]"
-      memory_footprint_var_name;
-    2
-  in
-  try
-    let i = int_of_string (Sys.getenv memory_footprint_var_name) in
-    if i <= 0 || i > 10 then error ()
-    else i
-  with
-  | Not_found -> 2
-  | Failure _ -> error ()
-
-let cache_size = 1 lsl (8 + memory_footprint)
+let compute_cache_size i = 1 lsl (8 + i)
+let default_value = 2
+
+(* Should not be used to create cache before the value from -eva-cache-size
+   has been set by Eva. *)
+let cache_size = ref (compute_cache_size default_value)
+
+(* Set by Eva, according to the -eva-cache-size parameter. *)
+let set_cache_size i = cache_size := compute_cache_size i
 
 (** The caches of this module are lazy, for two reasons:
 
@@ -55,7 +45,6 @@ let cache_size = 1 lsl (8 + memory_footprint)
       (This is not perfect: once a lazy cache has been forced, each 'clear'
       operation becomes costly again.)
 *)
-let (!!) = Lazy.force
 
 module type Cacheable =
 sig
@@ -163,18 +152,23 @@ end
 
 module Symmetric_Binary (H: Cacheable) (R: Result) =
 struct
-  let size = cache_size
-  let cache = lazy (Array_3.make size H.sentinel H.sentinel R.sentinel)
 
-  let mask = pred size
+  let create () =
+    let size = !cache_size in
+    let array = Array_3.make size H.sentinel H.sentinel R.sentinel in
+    array, pred size
+
+  let cache = lazy (create ())
 
   let clear () =
     if Lazy.is_val cache then
-      Array_3.clear !!cache H.sentinel H.sentinel R.sentinel
+      let array, _ = Lazy.force cache in
+      Array_3.clear array H.sentinel H.sentinel R.sentinel
 
   let hash = H.hash
 
   let merge f a0 a1 =
+    let array, mask = Lazy.force cache in
     let a0', a1', h0, h1 =
       let h0 = hash a0 in
       let h1 = hash a1 in
@@ -185,106 +179,114 @@ struct
     let has = h1 lsl 5 - h1 + h0
     in
     let has = has land mask in
-
-    if H.equal (Array_3.get0 !!cache has) a0'
-    && H.equal (Array_3.get1 !!cache has) a1'
-    then begin
-      (*      Format.printf "Cache O@.";  *)
-      Array_3.get2 !!cache has
-    end
+    if H.equal (Array_3.get0 array has) a0'
+    && H.equal (Array_3.get1 array has) a1'
+    then
+      Array_3.get2 array has
     else
       let result = f a0 a1 in
-      (*      Format.printf "Cache N@."; *)
-      Array_3.set !!cache has a0' a1' result;
+      Array_3.set array has a0' a1' result;
       result
 end
 
 module Arity_One (H: Cacheable) (R: Result) =
 struct
-  let size = cache_size
-  let cache = lazy (Array_2.make size H.sentinel R.sentinel)
 
-  let mask = pred size
+  (* Creates the array used for the cache, and the mask to be used on keys
+     according to the array size. *)
+  let create () =
+    let size = !cache_size in
+    let array = Array_2.make size H.sentinel R.sentinel in
+    array, pred size
+
+  let cache = lazy (create ())
 
   let clear () =
     if Lazy.is_val cache then
-      Array_2.clear !!cache H.sentinel R.sentinel
+      let array, _ = Lazy.force cache in
+      Array_2.clear array H.sentinel R.sentinel
 
   let merge f a0 =
+    let array, mask = Lazy.force cache in
     let h0 = H.hash a0 in
     let has = h0 land mask in
-    if H.equal (Array_2.get0 !!cache has) a0
-    then begin
-      (*      Format.printf "Cache O@.";  *)
-      Array_2.get1 !!cache has
-    end
+    if H.equal (Array_2.get0 array has) a0
+    then
+      Array_2.get1 array has
     else
       let result = f a0 in
-      (*      Format.printf "Cache N@."; *)
-      Array_2.set !!cache has a0 result;
+      Array_2.set array has a0 result;
       result
 end
 
 module Arity_Two (H0: Cacheable) (H1: Cacheable) (R: Result) =
 struct
 
-  let size = cache_size
-  let cache = lazy (Array_3.make size H0.sentinel H1.sentinel R.sentinel)
-  let mask = pred size
+  (* Creates the array used for the cache, and the mask to be used on keys
+     according to the array size. *)
+  let create () =
+    let size = !cache_size in
+    let array = Array_3.make size H0.sentinel H1.sentinel R.sentinel in
+    array, pred size
+
+  let cache = lazy (create ())
 
   let clear () =
     if Lazy.is_val cache then
-      Array_3.clear !!cache H0.sentinel H1.sentinel R.sentinel
+      let array, _ = Lazy.force cache in
+      Array_3.clear array H0.sentinel H1.sentinel R.sentinel
 
   let merge f a0 a1 =
+    let array, mask = Lazy.force cache in
     let h0 = H0.hash a0 in
     let h1 = H1.hash a1 in
     let has = h1 lsl 5 - h1 + h0
     in
     let has = has land mask in
-
-    if H0.equal (Array_3.get0 !!cache has) a0
-    && H1.equal (Array_3.get1 !!cache has) a1
-    then begin
-      (*      Format.printf "Cache O@.";  *)
-      Array_3.get2 !!cache has
-    end
+    if H0.equal (Array_3.get0 array has) a0
+    && H1.equal (Array_3.get1 array has) a1
+    then
+      Array_3.get2 array has
     else
       let result = f a0 a1 in
-      (*      Format.printf "Cache N@."; *)
-      Array_3.set !!cache has a0 a1 result;
+      Array_3.set array has a0 a1 result;
       result
 end
 
 module Arity_Three (H0: Cacheable) (H1: Cacheable) (H2: Cacheable) (R: Result) =
 struct
-  let size = cache_size
-  let cache =
-    lazy (Array_4.make size H0.sentinel H1.sentinel H2.sentinel R.sentinel)
-  let mask = pred size
+
+  (* Creates the array used for the cache, and the mask to be used on keys
+     according to the array size. *)
+  let create () =
+    let size = !cache_size in
+    let array =
+      Array_4.make size H0.sentinel H1.sentinel H2.sentinel R.sentinel
+    in
+    array, pred size
+
+  let cache = lazy (create ())
 
   let clear () =
     if Lazy.is_val cache then
-      Array_4.clear !!cache H0.sentinel H1.sentinel H2.sentinel R.sentinel
+      let array, _ = Lazy.force cache in
+      Array_4.clear array H0.sentinel H1.sentinel H2.sentinel R.sentinel
 
   let merge f a0 a1 a2 =
+    let array, mask = Lazy.force cache in
     let h0 = H0.hash a0 in
     let h1 = H1.hash a1 in
     let h2 = H2.hash a2 in
     let has = h0 + 117 * h1 + 2375 * h2 in
     let has = has land mask in
-
-    if H0.equal (Array_4.get0 !!cache has) a0
-    && H1.equal (Array_4.get1 !!cache has) a1
-    && H2.equal (Array_4.get2 !!cache has) a2
-    then begin
-      (*      Format.printf "Cache O@.";  *)
-      Array_4.get3 !!cache has
-    end
+    if H0.equal (Array_4.get0 array has) a0
+    && H1.equal (Array_4.get1 array has) a1
+    && H2.equal (Array_4.get2 array has) a2
+    then
+      Array_4.get3 array has
     else
       let result = f a0 a1 a2 in
-      (*      Format.printf "Cache N@."; *)
-      Array_4.set !!cache has a0 a1 a2 result;
+      Array_4.set array has a0 a1 a2 result;
       result
 end
 
@@ -320,55 +322,65 @@ end
 
 module Binary_Predicate (H0: Cacheable) (H1: Cacheable) =
 struct
-  let size = cache_size
-  let cache = lazy (Array_2.make size H0.sentinel H1.sentinel)
-  let result = lazy (Array_Bit.make size)
-  let mask = pred size
+
+  (* Creates two arrays (one for the arguments, one for the boolean result),
+     and the mask to be used on keys according to the array size. *)
+  let create () =
+    let size = !cache_size in
+    let array_args = Array_2.make size H0.sentinel H1.sentinel in
+    let array_result = Array_Bit.make size in
+    array_args, array_result, pred size
+
+  let cache = lazy (create ())
 
   let clear () =
     if Lazy.is_val cache then
-      Array_2.clear !!cache H0.sentinel H1.sentinel;
-    if Lazy.is_val result then
-      Array_Bit.clear !!result
+      let array_args, array_result, _ = Lazy.force cache in
+      Array_2.clear array_args H0.sentinel H1.sentinel;
+      Array_Bit.clear array_result
 
   let merge f a0 a1 =
+    let array_args, array_result, mask = Lazy.force cache in
     let has =
       let h0 = H0.hash a0 in
       let h1 = H1.hash a1 in
       599 * h0 + h1
     in
     let has = has land mask in
-
-    if H0.equal (Array_2.get0 !!cache has) a0
-    && H1.equal (Array_2.get1 !!cache has) a1
-    then begin
-      (*      Format.printf "Cache O@.";  *)
-      Array_Bit.get !!result has
-    end
+    if H0.equal (Array_2.get0 array_args has) a0
+    && H1.equal (Array_2.get1 array_args has) a1
+    then
+      Array_Bit.get array_result has
     else
       let r = f a0 a1 in
-      (*      Format.printf "Cache N@."; *)
-      Array_2.set !!cache has a0 a1;
-      Array_Bit.set !!result has r;
+      Array_2.set array_args has a0 a1;
+      Array_Bit.set array_result has r;
       r
 end
 
 module Symmetric_Binary_Predicate (H0: Cacheable) =
 struct
-  let size = cache_size
-  let cache = lazy (Array_2.make size H0.sentinel H0.sentinel)
-  let result = lazy (Array_Bit.make size)
-  let mask = pred size
+
+  (* Creates two arrays (one for the arguments, one for the boolean result),
+     and the mask to be used on keys according to the array size. *)
+  let create () =
+    let size = !cache_size in
+    let array_args = Array_2.make size H0.sentinel H0.sentinel in
+    let array_result = Array_Bit.make size in
+    array_args, array_result, pred size
+
+  let cache = lazy (create ())
 
   let clear () =
     if Lazy.is_val cache then
-      Array_2.clear !!cache H0.sentinel H0.sentinel;
-    if Lazy.is_val result then
-      Array_Bit.clear !!result
+      let array_args, array_result, _ = Lazy.force cache in
+      Array_2.clear array_args H0.sentinel H0.sentinel;
+      Array_Bit.clear array_result
 
   let hash = H0.hash
 
   let merge f a0 a1 =
+    let array_args, array_result, mask = Lazy.force cache in
     let a0, a1, h0, h1 =
       let h0 = hash a0 in
       let h1 = hash a1 in
@@ -379,18 +391,14 @@ struct
     let has = h1 lsl 5 - h1 + h0
     in
     let has = has land mask in
-
-    if H0.equal (Array_2.get0 !!cache has) a0
-    && H0.equal (Array_2.get1 !!cache has) a1
-    then begin
-      (*      Format.printf "Cache O@.";  *)
-      Array_Bit.get !!result has
-    end
+    if H0.equal (Array_2.get0 array_args has) a0
+    && H0.equal (Array_2.get1 array_args has) a1
+    then
+      Array_Bit.get array_result has
     else
       let r = f a0 a1 in
-      (*      Format.printf "Cache N@."; *)
-      Array_2.set !!cache has a0 a1;
-      Array_Bit.set !!result has r;
+      Array_2.set array_args has a0 a1;
+      Array_Bit.set array_result has r;
       r
 end
 
diff --git a/src/libraries/utils/binary_cache.mli b/src/libraries/utils/binary_cache.mli
index bd063032205..8bdf6b25361 100644
--- a/src/libraries/utils/binary_cache.mli
+++ b/src/libraries/utils/binary_cache.mli
@@ -24,6 +24,10 @@
     you understand what happens in this module, and do not forget that
     those caches are not aware of projects. *)
 
+(** [set_cache_size n] sets the size cache to 2^(8+n).
+    Does not resize already created caches. *)
+val set_cache_size: int -> unit
+
 module type Cacheable = sig
   type t
   val hash : t -> int
diff --git a/src/plugins/eva/parameters.ml b/src/plugins/eva/parameters.ml
index 131d5afea37..c347010dd82 100644
--- a/src/plugins/eva/parameters.ml
+++ b/src/plugins/eva/parameters.ml
@@ -398,6 +398,24 @@ module JoinResults =
       let default = true
     end)
 
+let () = Parameter_customize.set_group performance
+module CacheSize =
+  Int
+    (struct
+      let default = 2
+      let option_name = "-eva-cache-size"
+      let arg_name = "n"
+      let help =
+        "Control the amount of memory allocated to the internal caches \
+         of Eva. Must be between 1 and 10; default value is 2. \
+         Each increase of 1 doubles the size of these caches. \
+         Small values are most suitable for the analysis of small programs \
+         (less than 1000 lines). Higher values (around 7-8) can speed up the \
+         analysis of large code bases."
+    end)
+let () = CacheSize.set_range ~min:1 ~max:10
+let () = CacheSize.add_update_hook (fun _ i -> Binary_cache.set_cache_size i)
+
 (* ------------------------------------------------------------------------- *)
 (* --- Non-standard alarms                                               --- *)
 (* ------------------------------------------------------------------------- *)
-- 
GitLab