From 0bc431dab82ddf501190f95b24d90e2dcc66f85f Mon Sep 17 00:00:00 2001
From: Allan Blanchard <allan.blanchard@cea.fr>
Date: Thu, 6 Feb 2020 15:05:18 +0100
Subject: [PATCH] [ghost] Ghost CFG, no more references

---
 src/kernel_internals/typing/ghost_cfg.ml | 139 ++++++++++++-----------
 1 file changed, 70 insertions(+), 69 deletions(-)

diff --git a/src/kernel_internals/typing/ghost_cfg.ml b/src/kernel_internals/typing/ghost_cfg.ml
index 8090c44052b..997a7564318 100644
--- a/src/kernel_internals/typing/ghost_cfg.ml
+++ b/src/kernel_internals/typing/ghost_cfg.ml
@@ -54,89 +54,88 @@ let noGhostBlock b =
   end in
   (visitCilBlock (noGhostVisitor :> cilVisitor) b), (noGhostVisitor#getBehavior ())
 
-let isSkip stmt =
-  match stmt.skind with
-  (* We ignore blocks: their successors are their 1st stmt so we visit them *)
-  | Instr(Skip(_)) | Block(_) | Continue(_) | Break(_) -> true
-  | _ -> false
-
 type follower =
   (* For a stmt, an "Infinite" follower means that following skip instructions
      we just go back to this stmt. *)
   | Infinite | Exit | Stmt of stmt
 
-let rec skipSkip ?(visited=StmtSet.empty) s =
-  if StmtSet.mem s visited then Infinite
-  else match isSkip s with
-    | false -> Stmt s
-    | true when s.succs = [] -> Exit
-    | _ ->
-      skipSkip ~visited:(StmtSet.add s visited) (Extlib.as_singleton s.succs)
-
-let firstNonSkipNonGhosts stmt =
-  let rec do_find ~visited stmt =
-    if List.mem stmt !visited then []
+let sync stmt =
+  match stmt.skind with
+  (* We ignore blocks: their successors are their 1st stmt so we visit them *)
+  | Instr(Skip(_)) | Block(_) | Continue(_) | Break(_) -> false
+  | _ -> true
+
+let nextSync stmt =
+  let rec aux visited s =
+    if StmtSet.mem s visited then Infinite
+    else match sync s with
+      | true -> Stmt s
+      | false when s.succs = [] -> Exit
+      | _ ->
+        aux (StmtSet.add s visited) (Extlib.as_singleton s.succs)
+  in aux StmtSet.empty stmt
+
+let nextNonGhostSync stmt =
+  let rec do_find (res, visited) stmt =
+    if StmtSet.mem stmt visited then res, visited
     else begin
-      visited := stmt :: !visited ;
-      if isSkip stmt then do_find ~visited (Extlib.as_singleton stmt.succs)
-      else if not (stmt.ghost) then [ stmt ]
-      else List.flatten (List.map (do_find ~visited) stmt.succs)
+      let visited = StmtSet.add stmt visited in
+      if not (sync stmt) then
+        do_find (res, visited) (Extlib.as_singleton stmt.succs)
+      else if not (stmt.ghost) then StmtSet.add stmt res, visited
+      else List.fold_left do_find (res, visited) stmt.succs
     end
   in
-  do_find ~visited:(ref []) stmt
+  fst (do_find (StmtSet.empty, StmtSet.empty) stmt)
 
 let alteredCFG stmt =
   error ~source:(fst (Stmt.loc stmt)) "Ghost code breaks CFG starting at:@.%a"
     Cil_printer.pp_stmt stmt
 
-let rec checkGhostCFG bhv ?(visited=ref StmtSet.empty) withGhostStart noGhost =
-  match (skipSkip withGhostStart), (skipSkip noGhost) with
-  | Stmt withGhost, Stmt noGhost -> begin
-      if StmtSet.mem withGhost !visited then ()
-      else begin
-        visited := StmtSet.add withGhost !visited ;
-        let withGhost = List.sort_uniq Stmt.compare (firstNonSkipNonGhosts withGhost) in
-        match withGhost, noGhost with
-        | [ s1 ], s2 when not (Stmt.equal s1 (Get_orig.stmt bhv s2)) ->
-          alteredCFG withGhostStart
-        | [ { skind = If(_) } as s1 ], s2 ->
-          checkIf bhv ~visited s1 s2
-        | [ { skind = Switch(_) } as s1 ], s2 ->
-          checkSwitch bhv ~visited s1 s2
-        | [ { skind = Loop(_) } as s1 ], s2 ->
-          checkLoop bhv ~visited s1 s2
-        | [ { succs = [next_s1] } ], { succs = [next_s2] } ->
-          checkGhostCFG bhv ~visited next_s1 next_s2
-        | [ { succs = [] } ], { succs = [] } -> ()
-        | _, _  ->
-          alteredCFG withGhostStart
+let checkGhostCFG bhv withGhostStart noGhost =
+  let rec do_check visited withGhostStart noGhostStart =
+    match (nextSync withGhostStart), (nextSync noGhostStart) with
+    | Stmt withGhost, Stmt _ when StmtSet.mem withGhost visited -> visited
+    | Stmt withGhost, Stmt noGhost ->
+      let visited = StmtSet.add withGhost visited in
+      let withGhost =
+        StmtSet.contains_single_elt (nextNonGhostSync withGhost)
+      in
+      begin match withGhost, noGhost with
+        | Some s1, s2 when not (Stmt.equal s1 (Get_orig.stmt bhv s2)) ->
+          alteredCFG withGhostStart ; visited
+
+        | Some ({ skind = If(_) } as withGhost), noGhost ->
+          let wgThen, wgElse = Cil.separate_if_succs withGhost in
+          let ngThen, ngElse = Cil.separate_if_succs noGhost in
+          let visited = do_check visited wgThen ngThen in
+          do_check visited wgElse ngElse
+
+        | Some ({ skind = Switch(_) } as withGhost), noGhost ->
+          let ngSuccs, ngDef = Cil.separate_switch_succs noGhost in
+          let wgAllSuccs, wgDef = Cil.separate_switch_succs withGhost in
+          let wgSuccsG, wgSuccsNG =
+            List.partition (fun s -> s.ghost) wgAllSuccs
+          in
+          let mustDefault = wgDef :: wgSuccsG in
+          assert(List.length ngSuccs = List.length wgSuccsNG) ;
+          let visited = List.fold_left2 do_check visited wgSuccsNG ngSuccs in
+          List.fold_left (fun v s -> do_check v s ngDef) visited mustDefault
+
+        | Some ({ skind=Loop(_,wgb,_,_,_) }), { skind=Loop (_, ngb,_,_,_) } ->
+          begin match wgb.bstmts, ngb.bstmts with
+            | s1 :: _ , s2 :: _ -> do_check visited s1 s2
+            | _, _ -> visited
+          end
+
+        | Some { succs = [wg] }, { succs = [ng] } -> do_check visited wg ng
+        | Some { succs = [] }, { succs = [] } -> visited
+        | _, _  -> alteredCFG withGhostStart ; visited
       end
-    end ;
-  | Exit, Exit | Infinite, Infinite -> ()
-  | _ , _ -> alteredCFG withGhostStart
-and checkIf bhv ~visited withGhost noGhost =
-  let withGhostThen, withGhostElse = Cil.separate_if_succs withGhost in
-  let noGhostThen  , noGhostElse   = Cil.separate_if_succs noGhost in
-  checkGhostCFG bhv ~visited withGhostThen noGhostThen ;
-  checkGhostCFG bhv ~visited withGhostElse noGhostElse
-and checkLoop bhv ~visited withGhost noGhost =
-  let withGhostBlock, noGhostBlock = match withGhost.skind, noGhost.skind with
-    | Loop(_, b1, _, _, _), Loop(_, b2, _, _, _) -> b1, b2
-    | _ -> assert false
-  in
-  match withGhostBlock.bstmts, noGhostBlock.bstmts with
-  | s1 :: _ , s2 :: _ -> checkGhostCFG bhv ~visited s1 s2
-  | _, _ -> ()
-and checkSwitch bhv ~visited withGhost noGhost =
-  let noGhostSuccs, noGhostDef = Cil.separate_switch_succs noGhost in
-  let withGhostAllSuccs, withGhostDef = Cil.separate_switch_succs withGhost in
-  let withGhostSuccsGhost, withGhostSuccsNonGhost =
-    List.partition (fun s -> s.ghost) withGhostAllSuccs
+    | Exit, Exit | Infinite, Infinite -> visited
+    | _ , _ -> alteredCFG withGhostStart ; visited
   in
-  let mustDefault = withGhostDef :: withGhostSuccsGhost in
-  assert(List.length noGhostSuccs = List.length withGhostSuccsNonGhost) ;
-  List.iter2 (checkGhostCFG bhv ~visited) withGhostSuccsNonGhost noGhostSuccs ;
-  List.iter (fun s -> checkGhostCFG bhv ~visited s noGhostDef) mustDefault
+  ignore (do_check StmtSet.empty withGhostStart noGhost)
 
 let checkGhostCFGInFun (fd : fundec) =
   if fd.svar.vghost then ()
@@ -155,7 +154,9 @@ let checkGhostCFGInFun (fd : fundec) =
   end
 
 let checkGhostCFGInFile (f : file) =
-  Cil.iterGlobals f (function GFun(fd,_) -> checkGhostCFGInFun fd | _ -> ())
+  Cil.iterGlobals f (function
+      | GFun (fd, _) -> checkGhostCFGInFun fd
+      | _ -> ())
 
 let transform_category =
   File.register_code_transformation_category "Ghost CFG checking"
-- 
GitLab