From ff4cd3b7e2525a1dcf552f44c74dac10304e7357 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr>
Date: Tue, 8 Jan 2019 10:49:55 +0100
Subject: [PATCH] [Inout] Avoids folding implicit zero-initializers of large
 arrays.

Fixes a performance issue.
---
 src/plugins/inout/cumulative_analysis.ml  | 12 ++++++++++++
 src/plugins/inout/cumulative_analysis.mli |  4 ++++
 src/plugins/inout/inputs.ml               |  4 +++-
 src/plugins/inout/operational_inputs.ml   | 16 +++++++++++-----
 src/plugins/inout/outputs.ml              | 17 +++++++++++++----
 5 files changed, 43 insertions(+), 10 deletions(-)

diff --git a/src/plugins/inout/cumulative_analysis.ml b/src/plugins/inout/cumulative_analysis.ml
index ca86a013946..9bdb2f6624b 100644
--- a/src/plugins/inout/cumulative_analysis.ml
+++ b/src/plugins/inout/cumulative_analysis.ml
@@ -23,6 +23,18 @@
 open Cil_types
 open Visitor
 
+(* To avoid a performance issue, do not fold implicit zero-initializers of large
+   arrays. For arrays of scalar elements, the outputs of an initializer is
+   exactly the zone covered by the array. For arrays containing structs with
+   padding bits, this is an over-approximation, so we prefer folding the
+   initializer if the array is not too big (the 100 cells limit is arbitrary).
+   We still need to fold the explicit initializers to collect the inputs. *)
+let fold_implicit_initializer typ =
+  not
+    (Cil.isArrayType typ &&
+     (Cil.isArithmeticOrPointerType (Cil.typeOf_array_elem typ)
+      || Ast_info.array_size typ > (Integer.of_int 100)))
+
 let specialize_state_on_call ?stmt kf =
   match stmt with
     | Some ({ skind = Instr (Call (_, _, l, _)) } as stmt) ->
diff --git a/src/plugins/inout/cumulative_analysis.mli b/src/plugins/inout/cumulative_analysis.mli
index 78448372050..e5af1ddfa41 100644
--- a/src/plugins/inout/cumulative_analysis.mli
+++ b/src/plugins/inout/cumulative_analysis.mli
@@ -32,6 +32,10 @@ open Cil_types
     can be gathered using a Cil visitor).
 *)
 
+(** Should implicit zero-initializers for [typ] be folded? False for big arrays
+    to avoid a performance issue. *)
+val fold_implicit_initializer: typ -> bool
+
 
 val specialize_state_on_call: ?stmt:stmt -> kernel_function -> Db.Value.state
   (** If the given statement is a call to the given function,
diff --git a/src/plugins/inout/inputs.ml b/src/plugins/inout/inputs.ml
index 2819a6864d3..96edd8a1f3d 100644
--- a/src/plugins/inout/inputs.ml
+++ b/src/plugins/inout/inputs.ml
@@ -96,7 +96,9 @@ class virtual do_it_ = object(self)
             self#do_assign lv;
             ignore (visitFramacExpr (self:>frama_c_visitor) e)
           | CompoundInit (ct,initl) ->
-            let implicit = true in
+            (* No need to consider implicit zero-initializers, for which
+               nothing is read. *)
+            let implicit = false in
             let doinit o i _ () =
               ignore (visitFramacOffset (self:>frama_c_visitor) o);
               aux (Cil.addOffsetLval o lv) i
diff --git a/src/plugins/inout/operational_inputs.ml b/src/plugins/inout/operational_inputs.ml
index d4d164bb0b3..1c070d2ddf9 100644
--- a/src/plugins/inout/operational_inputs.ml
+++ b/src/plugins/inout/operational_inputs.ml
@@ -283,7 +283,6 @@ end) = struct
     let new_inputs = Zone.diff inputs data.under_outputs_d in
     store_non_terminating_inputs new_inputs;
     {data with over_inputs_d = Zone.join data.over_inputs_d new_inputs}
-  ;;
 
   (* Initialized const variables should be included as outputs of the function,
      so [for_writing] must be false for local initializations. It should be
@@ -302,8 +301,8 @@ end) = struct
            Add it into the under-approximated outputs. *)
         Zone.link data.under_outputs_d new_outs
       else data.under_outputs_d
-    in {
-      under_outputs_d = new_sure_outs;
+    in
+    { under_outputs_d = new_sure_outs;
       over_inputs_d = Zone.join data.over_inputs_d new_inputs;
       over_outputs_d = Zone.join data.over_outputs_d new_outs }
 
@@ -372,9 +371,16 @@ end) = struct
           let e_inputs = !Db.From.find_deps_no_transitivity_state state e in
           add_out ~for_writing:false state lv e_inputs acc
         | CompoundInit(ct, initl) ->
-          let implicit = true in
+          (* Avoid folding implicit zero-initializer of large arrays. *)
+          let implicit = Cumulative_analysis.fold_implicit_initializer ct in
           let doinit o i _ data = aux (Cil.addOffsetLval o lv) i data in
-          Cil.foldLeftCompound ~implicit ~doinit ~ct ~initl ~acc
+          let data = Cil.foldLeftCompound ~implicit ~doinit ~ct ~initl ~acc in
+          if implicit then data else
+            (* If the implicit zero-initializers hade been skipped, add the
+               zone of the array as outputs. It is exactly the written zone for
+               arrays of scalar elements. Nothing is read by zero-initializers,
+               so the inputs are empty. *)
+            add_out ~for_writing:false state lv Zone.bottom acc
       in
       aux (Cil.var v) i data
     | Call (lvaloption,funcexp,argl,loc) ->
diff --git a/src/plugins/inout/outputs.ml b/src/plugins/inout/outputs.ml
index 35e15968f18..67ff63012dc 100644
--- a/src/plugins/inout/outputs.ml
+++ b/src/plugins/inout/outputs.ml
@@ -89,10 +89,19 @@ class virtual do_it_ = object(self)
         let rec aux lv = function
           | SingleInit _ -> self#do_assign ~for_writing:false lv
           | CompoundInit (ct, initl) ->
-            let implicit = true in
-            let doinit o i _ () = aux (Cil.addOffsetLval o lv) i in
-            Cil.foldLeftCompound ~implicit ~doinit ~ct ~initl ~acc:()
-        in aux (Cil.var v) i
+            (* Avoid folding the implicit zero-initializers of large arrays. *)
+            if Cumulative_analysis.fold_implicit_initializer ct
+            then
+              let implicit = true in
+              let doinit o i _ () = aux (Cil.addOffsetLval o lv) i in
+              Cil.foldLeftCompound ~implicit ~doinit ~ct ~initl ~acc:()
+            else
+              (* For arrays of scalar elements, all the zone covered by the
+                 array is written. For arrays of structs containing padding
+                 bits, this is a sound over-approximation. *)
+              self#do_assign ~for_writing:false lv
+        in
+        aux (Cil.var v) i
       | Local_init (v, ConsInit(f, _, _),_) ->
         let state = Db.Value.get_state self#current_kinstr in
         if Cvalue.Model.is_top state then self#join Zone.top
-- 
GitLab