From ed85673439c445d9b7679202e0e8c0d0a88cd25b Mon Sep 17 00:00:00 2001
From: Andre Maroneze <andre.oliveiramaroneze@cea.fr>
Date: Fri, 27 Jul 2018 15:19:13 +0200
Subject: [PATCH] [Kernel] refactor removeUnusedTemps to no longer use
 '*referenced' fields

---
 src/kernel_internals/typing/rmtmps.ml | 544 ++++++++++++++------------
 1 file changed, 296 insertions(+), 248 deletions(-)

diff --git a/src/kernel_internals/typing/rmtmps.ml b/src/kernel_internals/typing/rmtmps.ml
index 9eae099fe52..c15193c0bc6 100644
--- a/src/kernel_internals/typing/rmtmps.ml
+++ b/src/kernel_internals/typing/rmtmps.ml
@@ -46,7 +46,30 @@ let dkey = Kernel.dkey_rmtmps
 open Extlib
 open Cil_types
 open Cil
-module H = Hashtbl
+
+(* Reachability of used data is stored in a table mapping [info] to [bool].
+   Note that due to mutability, we need to use our own Hashtbl module which
+   uses [Cil_datatype] equality functions. *)
+type info =
+  | Type of typeinfo
+  | Enum of enuminfo
+  | Comp of compinfo
+  | Var of varinfo
+
+module InfoHashtbl = Hashtbl.Make(struct
+    type t = info
+    let equal i1 i2 = match i1, i2 with
+      | Type t1, Type t2 -> Cil_datatype.Typeinfo.equal t1 t2
+      | Enum e1, Enum e2 -> Cil_datatype.Enuminfo.equal e1 e2
+      | Comp c1, Comp c2 -> Cil_datatype.Compinfo.equal c1 c2
+      | Var v1, Var v2 -> Cil_datatype.Varinfo.equal v1 v2
+      | _, _ -> false
+    let hash = function
+      | Type t -> Cil_datatype.Typeinfo.hash t
+      | Enum e -> Cil_datatype.Enuminfo.hash e
+      | Comp c -> Cil_datatype.Compinfo.hash c
+      | Var v -> Cil_datatype.Varinfo.hash v
+  end)
 
 (* Used by external plug-ins: *)
 let keepUnused = ref false
@@ -55,44 +78,7 @@ let keepUnused = ref false
 let rmUnusedInlines = ref false
 let rmUnusedStatic = ref false
 
-(***********************************************************************
- *
- *  Clearing of "referenced" bits
- *
-*)
-
-
-let clearReferencedBits file =
-  let considerGlobal global =
-    match global with
-    | GType (info, _) ->
-      info.treferenced <- false
-
-    | GEnumTag (info, _)
-    | GEnumTagDecl (info, _) ->
-      Kernel.debug ~dkey "clearing mark: %a" Cil_printer.pp_global global;
-      info.ereferenced <- false
-
-    | GCompTag (info, _)
-    | GCompTagDecl (info, _) ->
-      info.creferenced <- false
-
-    | GVar (vi, _, _)
-    | GFunDecl (_, vi, _)
-    | GVarDecl (vi, _) ->
-      vi.vreferenced <- false
-
-    | GFun ({svar = info} as func, _) ->
-      info.vreferenced <- false;
-      let clearMark local =
-        local.vreferenced <- false
-      in
-      List.iter clearMark func.slocals
-
-    | _ ->
-      ()
-  in
-  iterGlobals file considerGlobal
+let is_reachable t r = try InfoHashtbl.find t r with Not_found -> false
 
 
 (***********************************************************************
@@ -103,7 +89,7 @@ let clearReferencedBits file =
 
 
 (* collections of names of things to keep *)
-type collection = (string, unit) H.t
+type collection = (string, unit) Hashtbl.t
 type keepers = {
   typedefs : collection;
   enums : collection;
@@ -121,15 +107,15 @@ exception Bad_pragma
  * up collections of the corresponding varinfos' names.
 *)
 
-let categorizePragmas file =
+let categorizePragmas ast =
 
   (* names of things which should be retained *)
   let keepers = {
-    typedefs = H.create 1;
-    enums = H.create 1;
-    structs = H.create 1;
-    unions = H.create 1;
-    defines = H.create 1
+    typedefs = Hashtbl.create 1;
+    enums = Hashtbl.create 1;
+    structs = Hashtbl.create 1;
+    unions = Hashtbl.create 1;
+    defines = Hashtbl.create 1
   } in
 
   (* populate these name collections in light of each pragma *)
@@ -170,7 +156,7 @@ let categorizePragmas file =
                 | _ ->
                   raise Bad_pragma
               in
-              H.add collection name ()
+              Hashtbl.add collection name ()
             | _ ->
               raise Bad_pragma
           with Bad_pragma ->
@@ -183,7 +169,7 @@ let categorizePragmas file =
         match filterAttributes "alias" v.vattr with
         | [] -> ()  (* ordinary prototype. *)
         | [ Attr("alias", [AStr othername]) ] ->
-          H.add keepers.defines othername ()
+          Hashtbl.add keepers.defines othername ()
         | _ ->
           Kernel.fatal ~current:true
             "Bad alias attribute at %a"
@@ -192,7 +178,7 @@ let categorizePragmas file =
     | _ ->
       ()
   in
-  iterGlobals file considerPragma;
+  iterGlobals ast considerPragma;
   keepers
 
 
@@ -206,19 +192,19 @@ let categorizePragmas file =
 
 let isPragmaRoot keepers = function
   | GType ({tname = name}, _) ->
-    H.mem keepers.typedefs name
+    Hashtbl.mem keepers.typedefs name
   | GEnumTag ({ename = name}, _)
   | GEnumTagDecl ({ename = name}, _) ->
-    H.mem keepers.enums name
+    Hashtbl.mem keepers.enums name
   | GCompTag ({cname = name; cstruct = structure}, _)
   | GCompTagDecl ({cname = name; cstruct = structure}, _) ->
     let collection = if structure then keepers.structs else keepers.unions in
-    H.mem collection name
+    Hashtbl.mem collection name
   | GVar ({vname = name; vattr = attrs}, _, _)
   | GVarDecl ({vname = name; vattr = attrs}, _)
   | GFunDecl (_,{vname = name; vattr = attrs}, _)
   | GFun ({svar = {vname = name; vattr = attrs}}, _) ->
-    H.mem keepers.defines name ||
+    Hashtbl.mem keepers.defines name ||
     hasAttribute "used" attrs
   | _ ->
     false
@@ -230,15 +216,6 @@ let isPragmaRoot keepers = function
  *  Common root collecting utilities
  *
 *)
-(*TODO:remove
-  let traceRoot _reason _global =
-  (*  trace (dprintf "root (%s): %a@!" reason d_shortglobal global);*)
-  true
-
-  let traceNonRoot _reason _global =
-  (*  trace (dprintf "non-root (%s): %a@!" reason d_shortglobal global);*)
-  false
-*)
 let hasExportingAttribute funvar =
   let isExportingAttribute = function
     | Attr ("constructor", []) -> true
@@ -247,8 +224,6 @@ let hasExportingAttribute funvar =
   in
   List.exists isExportingAttribute funvar.vattr
 
-
-
 (***********************************************************************
  *
  *  Root collection from external linkage
@@ -345,166 +320,168 @@ let isCompleteProgramRoot global =
 
 (* This visitor recursively marks all reachable types and variables as used. *)
 class markReachableVisitor
-    ((globalMap: (string, Cil_types.global) H.t),
-     (currentFunc: Cil_types.fundec option ref)) = object (self)
-  inherit nopCilVisitor
+    (globalMap: (string, Cil_types.global) Hashtbl.t)
+    (currentFunc: Cil_types.fundec option ref)
+    (reachable_tbl: bool InfoHashtbl.t)
+  = object (self)
+    inherit nopCilVisitor
 
-  method! vglob = function
-    | GType (typeinfo, _) ->
-      typeinfo.treferenced <- true;
-      DoChildren
-    | GCompTag (compinfo, _)
-    | GCompTagDecl (compinfo, _) ->
-      compinfo.creferenced <- true;
-      DoChildren
-    | GEnumTag (enuminfo, _)
-    | GEnumTagDecl (enuminfo, _) ->
-      enuminfo.ereferenced <- true;
-      DoChildren
-    | GVar (varinfo, _, _)
-    | GVarDecl (varinfo, _)
-    | GFunDecl (_,varinfo, _)
-    | GFun ({svar = varinfo}, _) ->
-      if not (hasAttribute "FC_BUILTIN" varinfo.vattr) then
-        varinfo.vreferenced <- true;
-      DoChildren
-    | GAnnot _ -> DoChildren
-    | _ ->
-      SkipChildren
+    method! vglob = function
+      | GType (typeinfo, _) ->
+        InfoHashtbl.replace reachable_tbl (Type typeinfo) true;
+        DoChildren
+      | GCompTag (compinfo, _)
+      | GCompTagDecl (compinfo, _) ->
+        InfoHashtbl.replace reachable_tbl (Comp compinfo) true;
+        DoChildren
+      | GEnumTag (enuminfo, _)
+      | GEnumTagDecl (enuminfo, _) ->
+        InfoHashtbl.replace reachable_tbl (Enum enuminfo) true;
+        DoChildren
+      | GVar (varinfo, _, _)
+      | GVarDecl (varinfo, _)
+      | GFunDecl (_,varinfo, _)
+      | GFun ({svar = varinfo}, _) ->
+        if not (hasAttribute "FC_BUILTIN" varinfo.vattr) then
+          InfoHashtbl.replace reachable_tbl (Var varinfo) true;
+        DoChildren
+      | GAnnot _ -> DoChildren
+      | _ ->
+        SkipChildren
+
+    method! vstmt s =
+      match s.skind with
+      | TryCatch(_,c,_) ->
+        List.iter
+          (fun (decl,_) ->
+             match decl with
+             | Catch_exn(v,l) ->
+               (* treat all variables declared in exn clause as used. *)
+               ignore (self#vvrbl v);
+               List.iter (fun (v,_) -> ignore (self#vvrbl v)) l
+             | Catch_all -> ())
+          c;
+        DoChildren
+      | _ -> DoChildren
 
-  method! vstmt s =
-    match s.skind with
-    | TryCatch(_,c,_) ->
-      List.iter
-        (fun (decl,_) ->
-           match decl with
-           | Catch_exn(v,l) ->
-             (* treat all variables declared in exn clause as used. *)
-             ignore (self#vvrbl v);
-             List.iter (fun (v,_) -> ignore (self#vvrbl v)) l
-           | Catch_all -> ())
-        c;
-      DoChildren
-    | _ -> DoChildren
-
-  method! vinst = function
-    | Asm (_, tmpls, _, _) when Cil.msvcMode () ->
-      (* If we have inline assembly on MSVC, we cannot tell which locals
-       * are referenced. Keep them all *)
-      (match !currentFunc with
-         Some fd ->
-         List.iter (fun v ->
-             let vre = Str.regexp_string (Str.quote v.vname) in
-             if List.exists (fun tmp ->
-                 try ignore (Str.search_forward vre tmp 0); true
-                 with Not_found -> false)
-                 tmpls
-             then
-               v.vreferenced <- true) fd.slocals
-       | _ -> assert false);
-      DoChildren
-    | _ -> DoChildren
+    method! vinst = function
+      | Asm (_, tmpls, _, _) when Cil.msvcMode () ->
+        (* If we have inline assembly on MSVC, we cannot tell which locals
+         * are referenced. Keep them all *)
+        (match !currentFunc with
+           Some fd ->
+           List.iter (fun v ->
+               let vre = Str.regexp_string (Str.quote v.vname) in
+               if List.exists (fun tmp ->
+                   try ignore (Str.search_forward vre tmp 0); true
+                   with Not_found -> false)
+                   tmpls
+               then
+                 InfoHashtbl.replace reachable_tbl (Var v) true
+             ) fd.slocals
+         | _ -> assert false);
+        DoChildren
+      | _ -> DoChildren
 
-  method! vvrbl v =
-    if not v.vreferenced then
-      begin
-        let name = v.vname in
-        if v.vglob then
-          Kernel.debug ~dkey "marking transitive use: global %s" name
-        else
-          Kernel.debug ~dkey "marking transitive use: local %s" name;
+    method! vvrbl v =
+      if not (is_reachable reachable_tbl (Var v)) then
+        begin
+          let name = v.vname in
+          if v.vglob then
+            Kernel.debug ~dkey "marking transitive use: global %s" name
+          else
+            Kernel.debug ~dkey "marking transitive use: local %s" name;
+
+          (* If this is a global, we need to keep everything used in its
+           * definition and declarations. *)
+          InfoHashtbl.replace reachable_tbl (Var v) true;
+          if v.vglob then
+            begin
+              Kernel.debug ~dkey "descending: global %s" name;
+              let descend global =
+                ignore (visitCilGlobal (self :> cilVisitor) global)
+              in
+              let globals = Hashtbl.find_all globalMap name in
+              List.iter descend globals
+            end
+        end;
+      SkipChildren
 
-        (* If this is a global, we need to keep everything used in its
-         * definition and declarations. *)
-        v.vreferenced <- true;
-        if v.vglob then
-          begin
-            Kernel.debug ~dkey "descending: global %s" name;
-            let descend global =
-              ignore (visitCilGlobal (self :> cilVisitor) global)
-            in
-            let globals = Hashtbl.find_all globalMap name in
-            List.iter descend globals
-          end
-      end;
-    SkipChildren
+    method private mark_enum e =
+      if not (is_reachable reachable_tbl (Enum e)) then
+        begin
+          Kernel.debug ~dkey "marking transitive use: enum %s\n" e.ename;
+          InfoHashtbl.replace reachable_tbl (Enum e) true;
+          self#visitAttrs e.eattr;
+          (* Must visit the value attributed to the enum constants *)
+          ignore (visitCilEnumInfo (self:>cilVisitor) e);
+        end
+      else
+        Kernel.debug ~dkey "not marking transitive use: enum %s\n" e.ename;
+
+    method! vexpr e =
+      match e.enode with
+        Const (CEnum {eihost = ei}) -> self#mark_enum ei; DoChildren
+      | _ -> DoChildren
 
-  method private mark_enum e =
-    if not e.ereferenced then
-      begin
-        Kernel.debug ~dkey "marking transitive use: enum %s\n" e.ename;
-        e.ereferenced <- true;
-        self#visitAttrs e.eattr;
-        (* Must visit the value attributed to the enum constants *)
-        ignore (visitCilEnumInfo (self:>cilVisitor) e);
-      end
-    else
-      Kernel.debug ~dkey "not marking transitive use: enum %s\n" e.ename;
+    method! vterm_node t =
+      match t with
+        TConst (LEnum {eihost = ei}) -> self#mark_enum ei; DoChildren
+      | _ -> DoChildren
 
-  method! vexpr e =
-    match e.enode with
-      Const (CEnum {eihost = ei}) -> self#mark_enum ei; DoChildren
-    | _ -> DoChildren
-
-  method! vterm_node t =
-    match t with
-      TConst (LEnum {eihost = ei}) -> self#mark_enum ei; DoChildren
-    | _ -> DoChildren
-
-  method private visitAttrs attrs =
-    ignore (visitCilAttributes (self :> cilVisitor) attrs)
-
-  method! vtype typ =
-    (match typ with
-     | TEnum(e, attrs) ->
-       self#visitAttrs attrs;
-       self#mark_enum e
-
-     | TComp(c, _, attrs) ->
-       let old = c.creferenced in
-       if not old then
-         begin
-           Kernel.debug ~dkey "marking transitive use: compound %s\n"
-             c.cname;
-           c.creferenced <- true;
-
-           (* to recurse, we must ask explicitly *)
-           let recurse f = ignore (self#vtype f.ftype) in
-           List.iter recurse c.cfields;
-           self#visitAttrs attrs;
-           self#visitAttrs c.cattr
-         end;
-
-     | TNamed(ti, attrs) ->
-       let old = ti.treferenced in
-       if not old then
-         begin
-           Kernel.debug ~dkey "marking transitive use: typedef %s\n"
-             ti.tname;
-           ti.treferenced <- true;
-
-           (* recurse deeper into the type referred-to by the typedef *)
-           (* to recurse, we must ask explicitly *)
-           ignore (self#vtype ti.ttype);
-           self#visitAttrs attrs
-         end;
-
-     | TVoid a | TInt (_,a) | TFloat (_,a) | TBuiltin_va_list a ->
-       self#visitAttrs a
-     | TPtr(ty,a) -> ignore (self#vtype ty); self#visitAttrs a
-     | TArray(ty,sz, _, a) ->
-       ignore (self#vtype ty); self#visitAttrs a;
-       Extlib.may (ignore $ (visitCilExpr (self:>cilVisitor))) sz
-     | TFun (ty, args,_,a) ->
-       ignore (self#vtype ty);
-       Extlib.may (List.iter (fun (_,ty,_) -> ignore (self#vtype ty))) args;
-       self#visitAttrs a
-    );
-    SkipChildren
-end
+    method private visitAttrs attrs =
+      ignore (visitCilAttributes (self :> cilVisitor) attrs)
+
+    method! vtype typ =
+      (match typ with
+       | TEnum(e, attrs) ->
+         self#visitAttrs attrs;
+         self#mark_enum e
+
+       | TComp(c, _, attrs) ->
+         let old = is_reachable reachable_tbl (Comp c) in
+         if not old then
+           begin
+             Kernel.debug ~dkey "marking transitive use: compound %s\n"
+               c.cname;
+             InfoHashtbl.replace reachable_tbl (Comp c) true;
+
+             (* to recurse, we must ask explicitly *)
+             let recurse f = ignore (self#vtype f.ftype) in
+             List.iter recurse c.cfields;
+             self#visitAttrs attrs;
+             self#visitAttrs c.cattr
+           end;
+
+       | TNamed(ti, attrs) ->
+         let old = (is_reachable reachable_tbl (Type ti)) in
+         if not old then
+           begin
+             Kernel.debug ~dkey "marking transitive use: typedef %s\n"
+               ti.tname;
+             InfoHashtbl.replace reachable_tbl (Type ti) true;
+             (* recurse deeper into the type referred-to by the typedef *)
+             (* to recurse, we must ask explicitly *)
+             ignore (self#vtype ti.ttype);
+             self#visitAttrs attrs
+           end;
+
+       | TVoid a | TInt (_,a) | TFloat (_,a) | TBuiltin_va_list a ->
+         self#visitAttrs a
+       | TPtr(ty,a) -> ignore (self#vtype ty); self#visitAttrs a
+       | TArray(ty,sz, _, a) ->
+         ignore (self#vtype ty); self#visitAttrs a;
+         Extlib.may (ignore $ (visitCilExpr (self:>cilVisitor))) sz
+       | TFun (ty, args,_,a) ->
+         ignore (self#vtype ty);
+         Extlib.may (List.iter (fun (_,ty,_) -> ignore (self#vtype ty))) args;
+         self#visitAttrs a
+      );
+      SkipChildren
+  end
 
 
-let markReachable file isRoot =
+let markReachable isRoot ast reachable_tbl =
   (* build a mapping from global names back to their definitions &
    * declarations *)
   let globalMap = Hashtbl.create 137 in
@@ -518,12 +495,12 @@ let markReachable file isRoot =
     | _ ->
       ()
   in
-  iterGlobals file considerGlobal;
+  iterGlobals ast considerGlobal;
 
   let currentFunc = ref None in
 
   (* mark everything reachable from the global roots *)
-  let visitor = new markReachableVisitor (globalMap, currentFunc) in
+  let visitor = new markReachableVisitor globalMap currentFunc reachable_tbl in
   let visitIfRoot global =
     if isRoot global then
       begin
@@ -537,8 +514,85 @@ let markReachable file isRoot =
       (*      trace (dprintf "skipping non-root global: %a\n" d_shortglobal global)*)
       ()
   in
-  iterGlobals file visitIfRoot
+  iterGlobals ast visitIfRoot
+
+(**********************************************************************
+ *
+ * Marking of referenced infos
+ *
+ **********************************************************************)
+
+class markReferencedVisitor = object
+  inherit nopCilVisitor
 
+  val inside_exp : exp Stack.t = Stack.create ()
+  val inside_typ : typ Stack.t = Stack.create ()
+
+  method! vglob = function
+    | GType (typeinfo, _loc) ->
+      typeinfo.treferenced <- true;
+      DoChildren
+    | GCompTag (compinfo, _loc)
+    | GCompTagDecl (compinfo, _loc) ->
+      compinfo.creferenced <- true;
+      DoChildren
+    | GEnumTag (enuminfo, _loc)
+    | GEnumTagDecl (enuminfo, _loc) ->
+      enuminfo.ereferenced <- true;
+      DoChildren
+    | GVar (varinfo, _, _loc)
+    | GVarDecl (varinfo, _loc)
+    | GFunDecl (_,varinfo, _loc)
+    | GFun ({svar = varinfo}, _loc) ->
+      varinfo.vreferenced <- true;
+      DoChildren
+    | GAnnot _ -> DoChildren
+    | _ ->
+      SkipChildren
+
+  method! vtype = function
+    | TNamed (ti, _) ->
+      if not (Stack.is_empty inside_typ) then begin
+        ti.treferenced <- true;
+      end;
+      DoChildren
+    | TComp (ci, _, _) ->
+      if not (Stack.is_empty inside_typ) then begin
+        ci.creferenced <- true;
+      end;
+      DoChildren
+    | TEnum (ei, _) ->
+      if not (Stack.is_empty inside_typ) then begin
+        ei.ereferenced <- true;
+      end;
+      DoChildren
+    | TVoid _
+    | TInt _
+    | TFloat _
+    | TPtr _
+    | TArray _
+    | TFun _
+    | TBuiltin_va_list _ -> DoChildren
+
+  method! vexpr e =
+    match e.enode with
+    | SizeOf t | AlignOf t | UnOp (_, _, t) | BinOp (_, _, _, t) ->
+      Stack.push t inside_typ;
+      DoChildrenPost (fun e -> ignore (Stack.pop inside_typ); e)
+    | _ ->
+      Stack.push e inside_exp;
+      DoChildrenPost (fun e -> ignore (Stack.pop inside_exp); e)
+
+  method! vvrbl v =
+    if not (Stack.is_empty inside_exp) then begin
+      v.vreferenced <- true;
+    end;
+    SkipChildren
+
+end
+
+let markReferenced ast =
+  visitCilFileSameGlobals (new markReferencedVisitor) ast
 
 (**********************************************************************
  *
@@ -574,13 +628,13 @@ let labelsToKeep is_removable ll =
   in
   loop ("", Label("", Cil_datatype.Location.unknown, false)) ll
 
-class markUsedLabels is_removable (labelMap: (string, unit) H.t) =
+class markUsedLabels is_removable (labelMap: (string, unit) Hashtbl.t) =
   let keep_label dest =
     let (ln, _), _ = labelsToKeep is_removable !dest.labels in
     if ln = "" then
       Kernel.fatal "Statement has no label:@\n%a" Cil_printer.pp_stmt !dest ;
     (* Mark it as used *)
-    H.replace labelMap ln ()
+    Hashtbl.replace labelMap ln ()
   in
   let keep_label_logic = function
     | FormalLabel _ | BuiltinLabel _ -> ()
@@ -619,14 +673,14 @@ class markUsedLabels is_removable (labelMap: (string, unit) H.t) =
     method! vtype _ = SkipChildren
   end
 
-class removeUnusedLabels is_removable (labelMap: (string, unit) H.t) = object
+class removeUnusedLabels is_removable (labelMap: (string, unit) Hashtbl.t) = object
   inherit nopCilVisitor
 
   method! vstmt (s: stmt) =
     let (ln, lab), lrest = labelsToKeep is_removable s.labels in
     s.labels <-
       (if ln <> "" &&
-          (H.mem labelMap ln || not (is_removable lab))
+          (Hashtbl.mem labelMap ln || not (is_removable lab))
           (* keep user-provided labels *)
        then (* We had labels *)
          (lab :: lrest)
@@ -682,37 +736,37 @@ let label_removable = function
 let remove_unused_labels ?(is_removable=label_removable) func =
   (* We also want to remove unused labels. We do it all here, including
    * marking the used labels *)
-  let usedLabels:(string, unit) H.t = H.create 13 in
+  let usedLabels:(string, unit) Hashtbl.t = Hashtbl.create 13 in
   ignore
     (visitCilBlock (new markUsedLabels is_removable usedLabels) func.sbody);
   (* And now we scan again and we remove them *)
   ignore
     (visitCilBlock (new removeUnusedLabels is_removable usedLabels) func.sbody)
 
-let removeUnmarked isRoot file =
+let removeUnmarked isRoot ast reachable_tbl =
   let removedLocals = ref [] in
 
   let filterGlobal global =
     match global with
     (* unused global types, variables, and functions are simply removed *)
     | GType (t, _) ->
-      t.treferenced ||
+      is_reachable reachable_tbl (Type t) ||
       Cil.hasAttribute "FC_BUILTIN" (Cil.typeAttr t.ttype)
       || isRoot global
     | GCompTag (c,_) | GCompTagDecl (c,_) ->
-      c.creferenced ||
+      is_reachable reachable_tbl (Comp c) ||
       Cil.hasAttribute "FC_BUILTIN" c.cattr || isRoot global
     | GEnumTag (e, _) | GEnumTagDecl (e,_) ->
-      e.ereferenced ||
+      is_reachable reachable_tbl (Enum e) ||
       Cil.hasAttribute "FC_BUILTIN" e.eattr || isRoot global
     | GVar (v, _, _) ->
-      v.vreferenced ||
+      is_reachable reachable_tbl (Var v) ||
       Cil.hasAttribute "FC_BUILTIN" v.vattr || isRoot global
     | GVarDecl (v, _)
     | GFunDecl (_,v, _)->
-      v.vreferenced ||
+      is_reachable reachable_tbl (Var v) ||
       Cil.hasAttribute "FC_BUILTIN" v.vattr ||
-      (Cil.removeFormalsDecl v; isRoot global)
+      (if isRoot global then true else (Cil.removeFormalsDecl v; false))
     (* keep FC_BUILTIN, as some plug-ins might want to use them later
        for semi-legitimate reasons. *)
     | GFun (func, _) ->
@@ -720,7 +774,7 @@ let removeUnmarked isRoot file =
          Keep variables that were already present in the code.
       *)
       let filterLocal local =
-        if local.vtemp && not local.vreferenced then
+        if local.vtemp && not (is_reachable reachable_tbl (Var local)) then
           begin
             (* along the way, record the interesting locals that were removed *)
             let name = local.vname in
@@ -739,7 +793,7 @@ let removeUnmarked isRoot file =
           DoChildren
       end
       in
-      (func.svar.vreferenced
+      ((is_reachable reachable_tbl (Var func.svar))
        || Cil.hasAttribute "FC_BUILTIN" func.svar.vattr
        || isRoot global) &&
       (ignore (visitCilBlock remove_blocals func.sbody);
@@ -749,7 +803,8 @@ let removeUnmarked isRoot file =
     (* all other globals are retained *)
     | _ -> true
   in
-  file.globals <- List.filter filterGlobal file.globals;
+  let keptGlobals, _removedGlobals = List.partition filterGlobal ast.globals in
+  ast.globals <- keptGlobals;
   !removedLocals
 
 
@@ -762,14 +817,15 @@ let removeUnmarked isRoot file =
 
 type rootsFilter = global -> bool
 
-let removeUnusedTemps ?(isRoot : rootsFilter = isExportedRoot) file =
+let removeUnusedTemps ?(isRoot : rootsFilter = isExportedRoot) ast =
   if not !keepUnused then
     begin
       Kernel.debug ~dkey "Removing unused temporaries" ;
 
       (* digest any pragmas that would create additional roots *)
-      let keepers = categorizePragmas file in
+      let keepers = categorizePragmas ast in
 
+      let reachable_tbl = InfoHashtbl.create 43 in
       (* build up the root set *)
       let isRoot global =
         isPragmaRoot keepers global ||
@@ -777,20 +833,12 @@ let removeUnusedTemps ?(isRoot : rootsFilter = isExportedRoot) file =
       in
 
       (* mark everything reachable from the global roots *)
-      clearReferencedBits file;
-      markReachable file isRoot;
+      markReachable isRoot ast reachable_tbl;
 
-      (* take out the trash *)
-      let removedLocals = removeUnmarked isRoot file in
+      markReferenced ast;
 
-      (* print which original source variables were removed *)
-      if false && removedLocals != [] then
-        let count = List.length removedLocals in
-        if count > 2000 then
-          (Kernel.warning "%d unused local variables removed" count)
-        else
-          (Kernel.warning "%d unused local variables removed:@!%a"
-             count (Pretty_utils.pp_list ~sep:",@," Format.pp_print_string) removedLocals)
+      (* take out the trash *)
+      ignore (removeUnmarked isRoot ast reachable_tbl)
     end
 
 (*
-- 
GitLab