From e5e0f11a2985c910ddef7d2e34f3857eacccab3e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr>
Date: Mon, 1 Feb 2021 09:51:21 +0100
Subject: [PATCH] [wp] fix corner cases for unreachability

---
 src/plugins/wp/wpAnnot.ml    | 25 +++++++++----------
 src/plugins/wp/wpReached.ml  | 48 ++++++++++++++++++++++++------------
 src/plugins/wp/wpReached.mli |  8 +++---
 3 files changed, 48 insertions(+), 33 deletions(-)

diff --git a/src/plugins/wp/wpAnnot.ml b/src/plugins/wp/wpAnnot.ml
index c27e5f587c1..2a3326280b0 100644
--- a/src/plugins/wp/wpAnnot.ml
+++ b/src/plugins/wp/wpAnnot.ml
@@ -238,7 +238,7 @@ module HdefAnnotBhv = Cil2cfg.HE (struct type t = (stmt * int) end)
 type strategy_info = {
   kf : Kernel_function.t;
   cfg : Cil2cfg.t;
-  reached : WpReached.reached option ;
+  reachability : WpReached.reachability option ;
   cur_bhv : asked_bhv;
   asked_bhvs : asked_bhv list;
   asked_prop : asked_prop;
@@ -915,7 +915,7 @@ let get_loop_annots config vloop s =
 let add_stmt_deadcode_smoke config acc s =
   if cur_fct_default_bhv config
   then
-    match config.reached with
+    match config.reachability with
     | Some r when WpReached.smoking r s ->
         WpStrategy.add_prop_dead_code acc config.kf s
     | _ -> acc
@@ -1298,6 +1298,8 @@ let process_unreached_annots cfg =
   let do_annot s _ a acc =
     List.fold_left add_id acc (WpPropId.mk_code_annot_ids kf s a)
   in
+  let do_stmt s acc =
+    Annotations.fold_code_annot (do_annot s) s acc in
   let do_node acc n =
     debug
       "process annotations of unreachable node %a@."
@@ -1312,13 +1314,13 @@ let process_unreached_annots cfg =
         ignore Visitor.(visitFramacKf (visitor :> frama_c_visitor) kf) ;
         visitor#acc
     | Cil2cfg.Vcall (s, _, call, _) ->
-        Annotations.fold_code_annot (do_annot s) s acc @
+        do_stmt s acc @
         preconditions_at_call s call
     | Cil2cfg.Vstmt s
     | Cil2cfg.VblkIn (Cil2cfg.Bstmt s, _)
     | Cil2cfg.VblkOut (Cil2cfg.Bstmt s, _)
     | Cil2cfg.Vtest (true, s, _) | Cil2cfg.Vloop (_, s) | Cil2cfg.Vswitch (s,_)
-      -> Annotations.fold_code_annot (do_annot s) s acc
+      -> do_stmt s acc
     | Cil2cfg.Vtest (false, _, _) | Cil2cfg.Vloop2 _
     | Cil2cfg.VblkIn _ | Cil2cfg.VblkOut _ | Cil2cfg.Vend -> acc
   in
@@ -1330,11 +1332,6 @@ let process_unreached_annots cfg =
 (* Everything must go through here.                                           *)
 (*----------------------------------------------------------------------------*)
 
-let get_cfg kf model =
-  if Wp_parameters.RTE.get () then WpRTE.generate model kf ;
-  let cfg = Cil2cfg.get kf in
-  let _ = process_unreached_annots cfg in cfg
-
 let build_configs assigns kf model behaviors ki property =
   debug "[get_strategies] for behaviors names: %a@."
     (Wp_error.pp_string_list ~sep:" " ~empty:"<none>")
@@ -1348,16 +1345,18 @@ let build_configs assigns kf model behaviors ki property =
         debug
           "[get_strategies] select stmt %d properties@." s.sid
   in
-  let cfg = get_cfg kf model in
-  let reached =
+  if Wp_parameters.RTE.get () then WpRTE.generate model kf ;
+  let cfg = Cil2cfg.get kf in
+  let reachability =
     if Wp_parameters.SmokeTests.get ()
     && Wp_parameters.SmokeDeadcode.get ()
-    then Some (WpReached.reached kf)
+    then Some (WpReached.reachability kf)
     else None in
+  process_unreached_annots cfg ;
   let def_annot_bhv, bhvs = find_behaviors kf cfg ki behaviors in
   if bhvs <> [] then debug "[get_strategies] %d behaviors" (List.length bhvs);
   let mk_bhv_config bhv = {
-    kf; reached; cfg;
+    kf; reachability; cfg;
     cur_bhv = bhv;
     asked_prop = property;
     asked_bhvs = bhvs;
diff --git a/src/plugins/wp/wpReached.ml b/src/plugins/wp/wpReached.ml
index b21fa463f9f..3ef23a0bdbf 100644
--- a/src/plugins/wp/wpReached.ml
+++ b/src/plugins/wp/wpReached.ml
@@ -41,13 +41,14 @@ type node = {
   mutable flow : flow ;
   mutable prev : node list ;
   mutable reached : bool option ;
+  mutable alive : bool option ;
 }
 
 let kid = ref 0
 
 let node () =
   incr kid ;
-  { id = !kid ; prev = [] ; reached = None ; flow = F_goto }
+  { id = !kid ; prev = [] ; alive = None ; reached = None ; flow = F_goto }
 
 (* -------------------------------------------------------------------------- *)
 (* --- Unrolled Loop                                                      --- *)
@@ -83,7 +84,7 @@ let rec is_predicate cond p =
 let is_dead_annot ca =
   match ca.annot_content with
   | APragma (Loop_pragma (Unroll_specs [ spec ; _ ])) ->
-      false && is_unrolled_completely spec
+      is_unrolled_completely spec
   | AAssert([],p)
   | AInvariant([],_,p) ->
       not p.tp_only_check && is_predicate false p.tp_statement
@@ -102,8 +103,8 @@ let is_dead_code stmt =
 (* --- Compute CFG                                                        --- *)
 (* -------------------------------------------------------------------------- *)
 
-type reached = node Stmt.Map.t
-type cfg = reached ref
+type reachability = node Stmt.Map.t
+type cfg = reachability ref (* working cfg during compilation *)
 
 let of_stmt cfg s =
   try Stmt.Map.find s !cfg with Not_found ->
@@ -133,11 +134,8 @@ type env = {
 
 let rec stmt env s b =
   let a = of_stmt env.cfg s in
-  if is_dead_code s then
-    a.flow <- F_dead
-  else
-    a.flow <- skind env a b s.skind ;
-  a
+  let f = skind env a b s.skind in
+  a.flow <- if is_dead_code s then F_dead else f ; a
 
 and skind env a b = function
   | Instr i -> flow i (goto a b)
@@ -182,7 +180,12 @@ let rec reached node =
   | Some r -> r
   | None ->
       node.reached <- Some true ; (* cut loops *)
-      let r = List.for_all reached_after node.prev in
+      let r =
+        match node.flow with
+        | F_dead | F_entry -> true
+        | F_goto | F_effect | F_return | F_branch | F_call ->
+            List.for_all reached_after node.prev
+      in
       node.reached <- Some r ; r
 
 and reached_after node =
@@ -191,14 +194,26 @@ and reached_after node =
   | F_effect | F_entry | F_dead -> true
   | F_return | F_branch | F_call -> false
 
+let rec alive node =
+  match node.alive with
+  | Some a -> a
+  | None ->
+      match node.flow with
+      | F_dead -> false
+      | F_entry -> true
+      | _ ->
+          node.alive <- Some false ;
+          let a = List.exists alive node.prev in
+          node.alive <- Some a ; a
+
 let smoking_node n =
   match n.flow with
-  | F_effect | F_call | F_return -> not (reached n)
+  | F_effect | F_call | F_return -> alive n && not (reached n)
   | F_goto | F_branch | F_entry | F_dead -> false
 
 (* returns true if the stmt requires a reachability smoke test *)
-let smoking nodes stmt =
-  try Stmt.Map.find stmt nodes |> smoking_node
+let smoking reachability stmt =
+  try Stmt.Map.find stmt reachability |> smoking_node
   with Not_found -> false
 
 let compute kf =
@@ -262,7 +277,8 @@ let dump ~dir kf reached =
          | UnspecifiedSequence  _ -> Printf.sprintf "Seq. s%d" s.sid
          | Throw _ | TryExcept _ | TryCatch _ | TryFinally _ ->
              Printf.sprintf "Exn. s%d" s.sid
-       in G.node dot (N.get n) [`Box;`Label label])
+       in G.node dot (N.get n)
+         [`Box;`Label (Printf.sprintf "s%d n%d: %s" s.sid n.id label)])
     reached ;
   G.run dot ;
   G.close dot ;
@@ -276,7 +292,7 @@ let dump ~dir kf reached =
 module FRmap = Kernel_function.Make_Table
     (Datatype.Make
        (struct
-         type t = reached
+         type t = reachability
          include Datatype.Serializable_undefined
          let reprs = [Stmt.Map.empty]
          let name = "WpReachable.reached"
@@ -289,7 +305,7 @@ module FRmap = Kernel_function.Make_Table
 
 let dkey = Wp_parameters.register_category "reached"
 
-let reached = FRmap.memo
+let reachability = FRmap.memo
     begin fun kf ->
       let r = compute kf in
       (if Wp_parameters.has_dkey dkey then
diff --git a/src/plugins/wp/wpReached.mli b/src/plugins/wp/wpReached.mli
index 2f7bc0283bc..0c9c6f46ee4 100644
--- a/src/plugins/wp/wpReached.mli
+++ b/src/plugins/wp/wpReached.mli
@@ -26,7 +26,7 @@
 
 open Cil_types
 
-type reached
+type reachability
 (** control flow graph dedicated to smoke tests *)
 
 val is_predicate : bool -> predicate -> bool
@@ -39,14 +39,14 @@ val is_dead_annot : code_annotation -> bool
 val is_dead_code : stmt -> bool
 (** Checks whether the stmt has a dead-code annotation. *)
 
-val reached : Kernel_function.t -> reached
+val reachability : Kernel_function.t -> reachability
 (** memoized reached cfg for function *)
 
-val smoking : reached -> Cil_types.stmt -> bool
+val smoking : reachability -> Cil_types.stmt -> bool
 (** Returns whether a stmt need a smoke tests to avoid being unreachable.
     This is restricted to assignments, returns and calls not dominated
     another smoking statement. *)
 
-val dump : dir:Datatype.Filepath.t -> Kernel_function.t -> reached -> unit
+val dump : dir:Datatype.Filepath.t -> Kernel_function.t -> reachability -> unit
 
 (* -------------------------------------------------------------------------- *)
-- 
GitLab