diff --git a/.Makefile.lint b/.Makefile.lint
index cdddb760969cc6f1075006562db65d2c0cc5af02..fc916bfa1e33d42ff2e73b588b496e21b2d85da4 100644
--- a/.Makefile.lint
+++ b/.Makefile.lint
@@ -23,8 +23,6 @@ ML_LINT_KO+=src/kernel_internals/typing/mergecil.ml
 ML_LINT_KO+=src/kernel_internals/typing/mergecil.mli
 ML_LINT_KO+=src/kernel_internals/typing/oneret.ml
 ML_LINT_KO+=src/kernel_internals/typing/oneret.mli
-ML_LINT_KO+=src/kernel_internals/typing/rmtmps.ml
-ML_LINT_KO+=src/kernel_internals/typing/rmtmps.mli
 ML_LINT_KO+=src/kernel_internals/typing/translate_lightweight.ml
 ML_LINT_KO+=src/kernel_internals/typing/translate_lightweight.mli
 ML_LINT_KO+=src/kernel_internals/typing/unroll_loops.ml
diff --git a/src/kernel_internals/typing/cabs2cil.ml b/src/kernel_internals/typing/cabs2cil.ml
index b09617ac5eb3413608e822429cb55b652cb349cb..2f297109e145abeccf4f84695fc285b976aadb02 100644
--- a/src/kernel_internals/typing/cabs2cil.ml
+++ b/src/kernel_internals/typing/cabs2cil.ml
@@ -2882,8 +2882,8 @@ let makeGlobalVarinfo (isadef: bool) (vi: varinfo) : varinfo * bool =
          * local. This can happen when we declare an extern variable with
          * global scope but we are in a local scope. *)
       Kernel.debug ~dkey:Kernel.dkey_typing_global
-        "makeGlobalVarinfo isadef=%B vi.vname=%s(%d)"
-        isadef vi.vname vi.vid;
+        "makeGlobalVarinfo isadef=%B vi.vname=%s(%d), vreferenced=%B"
+        isadef vi.vname vi.vid vi.vreferenced;
       (* This may throw an exception Not_found *)
       let oldvi, oldloc = lookupGlobalVar vi.vname in
       Kernel.debug ~dkey:Kernel.dkey_typing_global
@@ -4644,6 +4644,7 @@ and makeVarInfoCabs
     ~(isformal: bool)
     ~(isglobal: bool)
     ?(isgenerated=false)
+    ?(referenced=false)
     (ldecl : location)
     (bt, sto, inline, attrs)
     (n,ndt,a)
@@ -4660,7 +4661,8 @@ and makeVarInfoCabs
     Kernel.error ~once:true ~current:true "inline for a non-function: %s" n;
   checkRestrictQualifierDeep vtype;
   (*  log "Looking at %s(%b): (%a)@." n isformal d_attrlist nattr;*)
-  let vi = makeVarinfo ~temp:isgenerated isglobal isformal n vtype in
+  let vi = makeVarinfo ~referenced ~temp:isgenerated isglobal isformal n vtype
+  in
   vi.vstorage <- sto;
   vi.vattr <- nattr;
   vi.vdecl <- ldecl;
@@ -8218,11 +8220,17 @@ and createGlobal ghost logic_spec ((t,s,b,attr_list) : (typ * storage * bool * A
   let is_fc_builtin {A.expr_node=enode} =
     match enode with A.VARIABLE "FC_BUILTIN" -> true | _ -> false
   in
+  let is_fc_stdlib {A.expr_node=enode} =
+    match enode with A.VARIABLE v when v = fc_stdlib -> true | _ -> false
+  in
   let isgenerated =
     List.exists (fun (_,el) -> List.exists is_fc_builtin el) a
   in
+  let islibc =
+    List.exists (fun (_,el) -> List.exists is_fc_stdlib el) a
+  in
   (* Make a first version of the varinfo *)
-  let vi = makeVarInfoCabs ~ghost ~isformal:false
+  let vi = makeVarInfoCabs ~ghost ~isformal:false ~referenced:islibc
       ~isglobal:true ~isgenerated (convLoc cloc) (t,s,b,attr_list) (n,ndt,a)
   in
   (* Add the variable to the environment before doing the initializer
diff --git a/src/kernel_internals/typing/rmtmps.ml b/src/kernel_internals/typing/rmtmps.ml
index 2cc4c84b91adf8d54d4ffb42b36ce889c651aa7c..e22c883010414384126989c604b625b02faefc05 100644
--- a/src/kernel_internals/typing/rmtmps.ml
+++ b/src/kernel_internals/typing/rmtmps.ml
@@ -46,69 +46,63 @@ let dkey = Kernel.dkey_rmtmps
 open Extlib
 open Cil_types
 open Cil
-module H = Hashtbl
 
-(* Set on the command-line: *)
+(* 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
+
+(* Possibly no longer used: *)
 let rmUnusedInlines = ref false
 let rmUnusedStatic = ref false
 
-(***********************************************************************
- *
- *  Clearing of "referenced" bits
- *
- *)
-
+let is_reachable t r = try InfoHashtbl.find t r with Not_found -> false
 
-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 pp_info fmt = function
+  | Type ti -> Format.fprintf fmt "%s" ti.tname
+  | Enum ei -> Format.fprintf fmt "%s" ei.ename
+  | Comp ci -> Format.fprintf fmt "%s" ci.cname
+  | Var vi -> Format.fprintf fmt "%s" vi.vname
 
 
 (***********************************************************************
  *
  *  Scanning and categorization of pragmas
  *
- *)
+*)
 
 
 (* 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;
-    structs : collection;
-    unions : collection;
-    defines : collection;
-  }
+  typedefs : collection;
+  enums : collection;
+  structs : collection;
+  unions : collection;
+  defines : collection;
+}
 
 
 (* rapid transfer of control when we find a malformed pragma *)
@@ -117,17 +111,17 @@ exception Bad_pragma
 (* CIL and CCured define several pragmas which prevent removal of
  * various global varinfos.  Here we scan for those pragmas and build
  * 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 *)
@@ -138,59 +132,59 @@ let categorizePragmas file =
     in
 
     function
-      | GPragma (Attr ("cilnoremove" as directive, args), (location,_)) ->
-	  (* a very flexible pragma: can retain typedefs, enums,
-	   * structs, unions, or globals (functions or variables) *)
-	  begin
-	    let processArg arg =
-	      try
-		match arg with
-		| AStr specifier ->
-		    (* isolate and categorize one varinfo name *)
-		    let collection, name =
-		      (* Two words denotes a typedef, enum, struct, or
-		       * union, as in "type foo" or "enum bar".  A
-		       * single word denotes a global function or
-		       * variable. *)
-		      let whitespace = Str.regexp "[ \t]+" in
-		      let words = Str.split whitespace specifier in
-		      match words with
-		      | ["type"; name] ->
-			  keepers.typedefs, name
-		      | ["enum"; name] ->
-			  keepers.enums, name
-		      | ["struct"; name] ->
-			  keepers.structs, name
-		      | ["union"; name] ->
-			  keepers.unions, name
-		      | [name] ->
-			  keepers.defines, name
-		      | _ ->
-			  raise Bad_pragma
-		    in
-		    H.add collection name ()
-		| _ ->
-		    raise Bad_pragma
-	      with Bad_pragma ->
-		badPragma location directive
-	    in
-	    List.iter processArg args
-	  end
-      | GFunDecl (_,v, _) -> begin
-          (* Look for alias attributes, e.g. Linux modules *)
-          match filterAttributes "alias" v.vattr with
-          | [] -> ()  (* ordinary prototype. *)
-          | [ Attr("alias", [AStr othername]) ] ->
-            H.add keepers.defines othername ()
-          | _ ->
-	    Kernel.fatal ~current:true
-	      "Bad alias attribute at %a"
-	      Cil_printer.pp_location (CurrentLoc.get ())
-        end
-      |	_ ->
-	  ()
+    | GPragma (Attr ("cilnoremove" as directive, args), (location,_)) ->
+      (* a very flexible pragma: can retain typedefs, enums,
+       * structs, unions, or globals (functions or variables) *)
+      begin
+        let processArg arg =
+          try
+            match arg with
+            | AStr specifier ->
+              (* isolate and categorize one varinfo name *)
+              let collection, name =
+                (* Two words denotes a typedef, enum, struct, or
+                 * union, as in "type foo" or "enum bar".  A
+                 * single word denotes a global function or
+                 * variable. *)
+                let whitespace = Str.regexp "[ \t]+" in
+                let words = Str.split whitespace specifier in
+                match words with
+                | ["type"; name] ->
+                  keepers.typedefs, name
+                | ["enum"; name] ->
+                  keepers.enums, name
+                | ["struct"; name] ->
+                  keepers.structs, name
+                | ["union"; name] ->
+                  keepers.unions, name
+                | [name] ->
+                  keepers.defines, name
+                | _ ->
+                  raise Bad_pragma
+              in
+              Hashtbl.add collection name ()
+            | _ ->
+              raise Bad_pragma
+          with Bad_pragma ->
+            badPragma location directive
+        in
+        List.iter processArg args
+      end
+    | GFunDecl (_,v, _) -> begin
+        (* Look for alias attributes, e.g. Linux modules *)
+        match filterAttributes "alias" v.vattr with
+        | [] -> ()  (* ordinary prototype. *)
+        | [ Attr("alias", [AStr othername]) ] ->
+          Hashtbl.add keepers.defines othername ()
+        | _ ->
+          Kernel.fatal ~current:true
+            "Bad alias attribute at %a"
+            Cil_printer.pp_location (CurrentLoc.get ())
+      end
+    | _ ->
+      ()
   in
-  iterGlobals file considerPragma;
+  iterGlobals ast considerPragma;
   keepers
 
 
@@ -199,27 +193,27 @@ let categorizePragmas file =
  *
  *  Root collection from pragmas
  *
- *)
+*)
 
 
 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
+    let collection = if structure then keepers.structs else keepers.unions in
+    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 ||
-      hasAttribute "used" attrs
+    Hashtbl.mem keepers.defines name ||
+    hasAttribute "used" attrs
   | _ ->
-      false
+    false
 
 
 
@@ -227,15 +221,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
@@ -245,13 +230,11 @@ let hasExportingAttribute funvar =
   in
   List.exists isExportingAttribute funvar.vattr
 
-
-
 (***********************************************************************
  *
  *  Root collection from external linkage
  *
- *)
+*)
 
 
 (* Exported roots are those global varinfos which are visible to the
@@ -264,43 +247,44 @@ let hasExportingAttribute funvar =
  * - the function named "main"
  * gcc incorrectly (according to C99) makes inline functions visible to
  * the linker.  So we can only remove inline functions on MSVC.
- *)
+*)
 
 let isExportedRoot global =
   let name, result, reason = match global with
-  | GVar ({vstorage = Static} as v, _, _) when
-      Cil.hasAttribute "FC_BUILTIN" v.vattr ->
-    v.vname, true, "FC_BUILTIN attribute"
-  | GVar ({vstorage = Static; vname}, _, _) -> vname, false, "static variable"
-  | GVar (v,_,_) ->
-    v.vname, true, "non-static variable"
-  | GFun ({svar = v}, _) -> begin
-    if hasExportingAttribute v then
-      v.vname,true, "constructor or destructor function"
-    else if v.vstorage = Static then
-      v.vname, not !rmUnusedStatic, "static function"
-    else if v.vinline && v.vstorage != Extern
-         && (Cil.msvcMode () || !rmUnusedInlines) then
-      v.vname, false, "inline function"
-    else
-      v.vname, true, "other function"
-  end
-  | GFunDecl(_,v,_) when hasAttribute "alias" v.vattr ->
-    v.vname, true, "has GCC alias attribute"
-  | GFunDecl(_,v,_) | GVarDecl(v,_) when hasAttribute "FC_BUILTIN" v.vattr ->
-    v.vname, true, "has FC_BUILTIN attribute"
-  | GAnnot _ -> "", true, "global annotation"
-  | GType (t, _) when
-    Cil.hasAttribute "FC_BUILTIN" (Cil.typeAttr t.ttype) ->
-    t.tname, true, "has FC_BUILTIN attribute"
-  | GCompTag (c,_) | GCompTagDecl (c,_) when
-      Cil.hasAttribute "FC_BUILTIN" c.cattr ->
-    c.cname, true, "has FC_BUILTIN attribute"
-  | GEnumTag (e, _) | GEnumTagDecl (e,_) when
-      Cil.hasAttribute "FC_BUILTIN" e.eattr ->
-    e.ename, true, "has FC_BUILTIN attribute"
-  | _ ->
-    "", false, "neither function nor variable nor annotation"
+    | GVar ({vstorage = Static} as v, _, _) when
+        Cil.hasAttribute "FC_BUILTIN" v.vattr ->
+      v.vname, true, "FC_BUILTIN attribute"
+    | GVar ({vstorage = Static; vname}, _, _) -> vname, false, "static variable"
+    | GVar (v,_,_) ->
+      v.vname, true, "non-static variable"
+    | GFun ({svar = v}, _) -> begin
+        if hasExportingAttribute v then
+          v.vname,true, "constructor or destructor function"
+        else if v.vstorage = Static then
+          v.vname, not !rmUnusedStatic, "static function"
+        else if v.vinline && v.vstorage != Extern
+                && (Cil.msvcMode () || !rmUnusedInlines) then
+          v.vname, false, "inline function"
+        else
+          v.vname, true, "other function"
+      end
+    | GFunDecl(_,v,_) when hasAttribute "alias" v.vattr ->
+      v.vname, true, "has GCC alias attribute"
+    | GFunDecl(_,v,_) | GVarDecl(v,_) when hasAttribute "FC_BUILTIN" v.vattr ->
+      v.vname, true, "has FC_BUILTIN attribute"
+    | GAnnot _ -> "", true, "global annotation"
+    | GType (t, _) when
+        Cil.hasAttribute "FC_BUILTIN" (Cil.typeAttr t.ttype) ->
+      t.tname, true, "has FC_BUILTIN attribute"
+    | GCompTag (c,_) | GCompTagDecl (c,_) when
+        Cil.hasAttribute "FC_BUILTIN" c.cattr ->
+      c.cname, true, "has FC_BUILTIN attribute"
+    | GEnumTag (e, _) | GEnumTagDecl (e,_) when
+        Cil.hasAttribute "FC_BUILTIN" e.eattr ->
+      e.ename, true, "has FC_BUILTIN attribute"
+    | _ ->
+      (Format.asprintf "%a" Cil_types_debug.pp_global global), false,
+      "neither fundef nor vardef nor annotation"
   in
   Kernel.debug
     ~dkey "isExportedRoot %s -> %B, %s"  name result reason;
@@ -312,25 +296,25 @@ let isExportedRoot global =
  *
  *  Root collection for complete programs
  *
- *)
+*)
 
 
 (* Exported roots are "main()" and functions bearing a "constructor"
  * or "destructor" attribute.  These are the only things which must be
  * retained in a complete program.
- *)
+*)
 
 let isCompleteProgramRoot global =
   let result = match global with
-  | GFun ({svar = {vname = "main"; vstorage = vstorage}}, _) ->
+    | GFun ({svar = {vname = "main"; vstorage = vstorage}}, _) ->
       vstorage <> Static
-  | GFun (fundec, _)
-    when hasExportingAttribute fundec.svar ->
+    | GFun (fundec, _)
+      when hasExportingAttribute fundec.svar ->
       true
-  | _ ->
+    | _ ->
       false
   in
-(*  trace (dprintf "complete program root -> %b for %a@!" result d_shortglobal global);*)
+  (*  trace (dprintf "complete program root -> %b for %a@!" result d_shortglobal global);*)
   result
 
 
@@ -338,171 +322,211 @@ let isCompleteProgramRoot global =
  *
  *  Transitive reachability closure from roots
  *
- *)
+*)
 
 
 (* 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
-
-  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! 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
+    (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, _) ->
+        Kernel.debug ~dkey "marking reachable: type %s" typeinfo.tname;
+        InfoHashtbl.replace reachable_tbl (Type typeinfo) true;
+        DoChildren
+      | GCompTag (compinfo, _)
+      | GCompTagDecl (compinfo, _) ->
+        Kernel.debug ~dkey "marking reachable: comp decl %s" compinfo.cname;
+        InfoHashtbl.replace reachable_tbl (Comp compinfo) true;
+        DoChildren
+      | GEnumTag (enuminfo, _)
+      | GEnumTagDecl (enuminfo, _) ->
+        Kernel.debug ~dkey "marking reachable: enum decl %s" enuminfo.ename;
+        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
+          begin
+            Kernel.debug ~dkey "marking reachable: function %s" varinfo.vname;
+            InfoHashtbl.replace reachable_tbl (Var varinfo) true;
+          end;
+        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! vinst = function
-    | Asm (_, tmpls, _, _) when Cil.msvcMode () ->
-          (* If we have inline assembly on MSVC, we cannot tell which locals
-           * are referenced. Keep them all *)
+    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);
+           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
+      | _ -> DoChildren
+
+    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 (%d)" name v.vid
+          else
+            Kernel.debug ~dkey "marking transitive use: local %s (%d)" name v.vid;
+
+          (* 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 (%d)" name v.vid;
+              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" 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" e.ename;
 
-  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;
-
-        (* 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! 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! 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 ->
+    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 = is_reachable reachable_tbl (Comp c) in
+         if not old then
+           begin
+             Kernel.debug ~dkey "marking transitive use: compound %s"
+               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"
+               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
+       | 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
 
+    method! vlogic_var_decl lv =
+      Kernel.debug ~dkey "markReachable: found LOGIC VAR DECL for: %s (%d)\n" lv.lv_name lv.lv_id;
+      DoChildren
 
-let markReachable file isRoot =
+    method! vlogic_var_use lv =
+      Kernel.debug ~dkey "markReachable: found LOGIC VAR USE for: %s (%d)\n" lv.lv_name lv.lv_id;
+      match lv.lv_origin with
+      | None -> SkipChildren
+      | Some 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 for logic var: global %s (%d)" name v.vid
+            else
+              Kernel.debug ~dkey "marking transitive use for logic var: local %s (%d)" name v.vid;
+
+            (* 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 (%d)" name v.vid;
+                let descend global =
+                  ignore (visitCilGlobal (self :> cilVisitor) global)
+                in
+                let globals = Hashtbl.find_all globalMap name in
+                List.iter descend globals
+              end
+          end;
+        SkipChildren
+  end
+
+
+let markReachable isRoot ast reachable_tbl =
   (* build a mapping from global names back to their definitions &
    * declarations *)
   let globalMap = Hashtbl.create 137 in
@@ -512,31 +536,137 @@ let markReachable file isRoot =
     | GVar (info, _, _)
     | GFunDecl (_,info, _)
     | GVarDecl (info, _) ->
-	Hashtbl.add globalMap info.vname global
+      Hashtbl.add globalMap info.vname global
     | _ ->
-	()
+      ()
   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
-(*	trace (dprintf "traversing root global: %a\n" d_shortglobal global);*)
+        (* trace (dprintf "traversing root global: %a\n" d_shortglobal global);*)
         (match global with
-          GFun(fd, _) -> currentFunc := Some fd
-        | _ -> currentFunc := None);
-	ignore (visitCilGlobal visitor global)
+           GFun(fd, _) -> currentFunc := Some fd
+         | _ -> currentFunc := None);
+        ignore (visitCilGlobal visitor global)
       end
     else
-(*      trace (dprintf "skipping non-root global: %a\n" d_shortglobal global)*)
+      (*      trace (dprintf "skipping non-root global: %a\n" d_shortglobal global)*)
       ()
   in
-  iterGlobals file visitIfRoot
+  iterGlobals ast visitIfRoot
+
+(**********************************************************************
+ *
+ * Marking of referenced infos
+ *
+ **********************************************************************)
+
+let global_type_and_name = function
+  | GType (t, _) -> "type " ^ t.tname
+  | GCompTag (c,_) -> "comp " ^ c.cname
+  | GCompTagDecl (c,_) -> "comp decl " ^ c.cname
+  | GEnumTag (e, _) -> "enum " ^ e.ename
+  | GEnumTagDecl (e,_) -> "enum decl " ^ e.ename
+  | GVarDecl(v,_) -> "var decl " ^ v.vname
+  | GFunDecl(_,v,_) -> "fun decl " ^ v.vname
+  | GVar (v, _, _) -> "var " ^ v.vname
+  | GFun ({svar = v}, _) -> "fun " ^ v.vname
+  | GAsm _ -> "<asm>"
+  | GPragma _ -> "<pragma>"
+  | GText _ -> "<text>"
+  | GAnnot _ -> "<annot>"
+
+class markReferencedVisitor = object
+  inherit nopCilVisitor
+
+  val dkey = Kernel.dkey_referenced
+
+  val inside_exp : exp Stack.t = Stack.create ()
+  val inside_typ : typ Stack.t = Stack.create ()
+
+  method! vglob = function
+    | GType (typeinfo, loc) ->
+      Kernel.debug ~source:(fst loc) ~dkey "referenced: type %s" typeinfo.tname;
+      typeinfo.treferenced <- true;
+      DoChildren
+    | GCompTag (compinfo, loc)
+    | GCompTagDecl (compinfo, loc) ->
+      Kernel.debug ~source:(fst loc) ~dkey "referenced: comp %s" compinfo.cname;
+      compinfo.creferenced <- true;
+      DoChildren
+    | GEnumTag (enuminfo, loc)
+    | GEnumTagDecl (enuminfo, loc) ->
+      Kernel.debug ~source:(fst loc) ~dkey "referenced: enum %s" enuminfo.ename;
+      enuminfo.ereferenced <- true;
+      DoChildren
+    | GVar (varinfo, _, loc)
+    | GVarDecl (varinfo, loc)
+    | GFunDecl (_,varinfo, loc)
+    | GFun ({svar = varinfo}, loc) ->
+      Kernel.debug ~dkey "referenced: var/fun %s@." varinfo.vname;
+      Kernel.debug ~source:(fst loc) ~dkey "referenced: fun %s" varinfo.vname;
+      varinfo.vreferenced <- true;
+      DoChildren
+    | GAnnot _ -> DoChildren
+    | _ ->
+      SkipChildren
+
+  method! vtype = function
+    | TNamed (ti, _) ->
+      if not (Stack.is_empty inside_typ) then begin
+        Kernel.debug ~current:true ~dkey "referenced: type %s" ti.tname;
+        ti.treferenced <- true;
+      end;
+      DoChildren
+    | TComp (ci, _, _) ->
+      if not (Stack.is_empty inside_typ) then begin
+        Kernel.debug ~current:true ~dkey "referenced: comp %s" ci.cname;
+        ci.creferenced <- true;
+      end;
+      DoChildren
+    | TEnum (ei, _) ->
+      if not (Stack.is_empty inside_typ) then begin
+        Kernel.debug ~current:true ~dkey "referenced: enum %s" ei.ename;
+        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
+      Kernel.debug ~current:true ~dkey "referenced: var %s" v.vname;
+      v.vreferenced <- true;
+    end;
+    SkipChildren
+
+end
 
+let markReferenced ast =
+  Kernel.debug ~dkey "starting markReferenced (AST has %d globals)"
+    (List.length ast.globals);
+  visitCilFileSameGlobals (new markReferencedVisitor) ast;
+  Kernel.debug ~dkey "finished markReferenced"
 
 (**********************************************************************
  *
@@ -551,88 +681,88 @@ let labelsToKeep is_removable ll =
   let rec loop sofar = function
       [] -> sofar, []
     | l :: rest ->
-        let newlabel, keepl =
-          match l with
-          | Case _ | Default _ -> sofar, true
-          | Label (ln, _, _) as lab -> begin
-              match is_removable lab, sofar with
-              | true, ("", _) ->
-                  (* keep this one only if we have no label so far *)
-                  (ln, lab), false
-              | true, _ -> sofar, false
-              | false, (_, lab') when is_removable lab' ->
-                  (* this is an original label; prefer it to temporary or
-                   * missing labels *)
-                  (ln, lab), false
-              | false, _ -> sofar, false
+      let newlabel, keepl =
+        match l with
+        | Case _ | Default _ -> sofar, true
+        | Label (ln, _, _) as lab -> begin
+            match is_removable lab, sofar with
+            | true, ("", _) ->
+              (* keep this one only if we have no label so far *)
+              (ln, lab), false
+            | true, _ -> sofar, false
+            | false, (_, lab') when is_removable lab' ->
+              (* this is an original label; prefer it to temporary or
+               * missing labels *)
+              (ln, lab), false
+            | false, _ -> sofar, false
           end
-        in
-        let newlabel', rest' = loop newlabel rest in
-        newlabel', (if keepl then l :: rest' else rest')
+      in
+      let newlabel', rest' = loop newlabel rest in
+      newlabel', (if keepl then l :: rest' else rest')
   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 ()
-in
-let keep_label_logic = function
-  | FormalLabel _ | BuiltinLabel _ -> ()
-  | StmtLabel dest -> keep_label dest
-in
-object
-  inherit nopCilVisitor
+    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 *)
+    Hashtbl.replace labelMap ln ()
+  in
+  let keep_label_logic = function
+    | FormalLabel _ | BuiltinLabel _ -> ()
+    | StmtLabel dest -> keep_label dest
+  in
+  object
+    inherit nopCilVisitor
 
-  method! vstmt (s: stmt) =
-    match s.skind with
-      Goto (dest, _) -> keep_label dest; DoChildren
-    | _ -> DoChildren
+    method! vstmt (s: stmt) =
+      match s.skind with
+        Goto (dest, _) -> keep_label dest; DoChildren
+      | _ -> DoChildren
 
-  method! vterm_node t =
-    begin
-      match t with
-      | Tat (_,lab) -> keep_label_logic lab
-      | Tapp(_,labs,_) ->
+    method! vterm_node t =
+      begin
+        match t with
+        | Tat (_,lab) -> keep_label_logic lab
+        | Tapp(_,labs,_) ->
           List.iter keep_label_logic labs
-      | _ -> ()
-    end;
-    DoChildren
+        | _ -> ()
+      end;
+      DoChildren
 
-  method! vpredicate_node t =
-    begin
-      match t with
-      | Pat (_,lab) -> keep_label_logic lab
-      | Papp(_,labs,_) ->
+    method! vpredicate_node t =
+      begin
+        match t with
+        | Pat (_,lab) -> keep_label_logic lab
+        | Papp(_,labs,_) ->
           List.iter keep_label_logic labs
-      | _ -> ()
-    end;
-    DoChildren
+        | _ -> ()
+      end;
+      DoChildren
 
-   (* No need to go into expressions or types *)
-  method! vexpr _ = SkipChildren
-  method! vtype _ = SkipChildren
-                                                        end
+    (* No need to go into expressions or types *)
+    method! vexpr _ = SkipChildren
+    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))
+      (if ln <> "" &&
+          (Hashtbl.mem labelMap ln || not (is_removable lab))
           (* keep user-provided labels *)
-        then (* We had labels *)
+       then (* We had labels *)
          (lab :: lrest)
        else
          lrest);
     DoChildren
 
-   (* No need to go into expressions or instructions *)
+  (* No need to go into expressions or instructions *)
   method! vexpr _ = SkipChildren
   method! vinst _ = SkipChildren
   method! vtype _ = SkipChildren
@@ -642,36 +772,7 @@ end
  *
  *  Removal of unused varinfos
  *
- *)
-
-
-(* regular expression matching names of uninteresting locals *)
-let uninteresting =
-  let names = [
-    (* Cil.makeTempVar *)
-    "__cil_tmp";
-
-    (* sm: I don't know where it comes from but these show up all over. *)
-    (* this doesn't seem to do what I wanted.. *)
-    "iter";
-
-    (* various macros in glibc's <bits/string2.h> *)
-    "__result";
-    "__s"; "__s1"; "__s2";
-    "__s1_len"; "__s2_len";
-    "__retval"; "__len";
-
-    (* various macros in glibc's <ctype.h> *)
-    "__c"; "__res";
-
-    (* We remove the __malloc variables *)
-  ] in
-
-  (* optional alpha renaming *)
-  let alpha = "\\(___[0-9]+\\)?" in
-
-  let pattern = "\\(" ^ (String.concat "\\|" names) ^ "\\)" ^ alpha ^ "$" in
-  Str.regexp pattern
+*)
 
 let label_removable = function
     Label (_,_,user) -> not user
@@ -680,74 +781,94 @@ 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 ||
-          Cil.hasAttribute "FC_BUILTIN" (Cil.typeAttr t.ttype) 
-          || isRoot global
-      | GCompTag (c,_) | GCompTagDecl (c,_) ->
-          c.creferenced ||
-            Cil.hasAttribute "FC_BUILTIN" c.cattr || isRoot global
-      | GEnumTag (e, _) | GEnumTagDecl (e,_) ->
-          e.ereferenced ||
-            Cil.hasAttribute "FC_BUILTIN" e.eattr || isRoot global
-      | GVar (v, _, _) ->
-          v.vreferenced || 
-            Cil.hasAttribute "FC_BUILTIN" v.vattr || isRoot global
-      | GVarDecl (v, _)
-      | GFunDecl (_,v, _)->
-          v.vreferenced ||
-            Cil.hasAttribute "FC_BUILTIN" v.vattr ||
-            (Cil.removeFormalsDecl v; isRoot global)
-       (* keep FC_BUILTIN, as some plug-ins might want to use them later
-          for semi-legitimate reasons. *)
-      | GFun (func, _) ->
-           (* if some generated temp variables are useless, remove them.
-              Keep variables that were already present in the code.
-           *)
-	   let filterLocal local =
-	     if local.vtemp && not local.vreferenced then
-	       begin
-	         (* along the way, record the interesting locals that were removed *)
-	         let name = local.vname in
-	         (Kernel.debug ~dkey "removing local: %s\n" name);
-	         if not (Str.string_match uninteresting name 0) then
-	          removedLocals :=
-                    (func.svar.vname ^ "::" ^ name) :: !removedLocals;
-                 false
-	       end else true
-	   in
-	   func.slocals <- List.filter filterLocal func.slocals;
-           let remove_blocals = object
-             inherit Cil.nopCilVisitor
-             method! vblock b =
-               b.blocals <- List.filter filterLocal b.blocals;
-               DoChildren
-           end
-           in
-           (func.svar.vreferenced 
-            || Cil.hasAttribute "FC_BUILTIN" func.svar.vattr
-            || isRoot global) &&
-             (ignore (visitCilBlock remove_blocals func.sbody);
-              remove_unused_labels func;
-	      true)
-
-      (* all other globals are retained *)
-      | _ -> true
+    (* unused global types, variables, and functions are simply removed *)
+    | GType (t, _) ->
+      is_reachable reachable_tbl (Type t) ||
+      Cil.hasAttribute "FC_BUILTIN" (Cil.typeAttr t.ttype)
+      || isRoot global
+    | GCompTag (c,_) | GCompTagDecl (c,_) ->
+      is_reachable reachable_tbl (Comp c) ||
+      Cil.hasAttribute "FC_BUILTIN" c.cattr || isRoot global
+    | GEnumTag (e, _) | GEnumTagDecl (e,_) ->
+      is_reachable reachable_tbl (Enum e) ||
+      Cil.hasAttribute "FC_BUILTIN" e.eattr || isRoot global
+    | GVar (v, _, _) ->
+      is_reachable reachable_tbl (Var v) ||
+      Cil.hasAttribute "FC_BUILTIN" v.vattr || isRoot global
+    | GVarDecl (v, _)
+    | GFunDecl (_,v, _)->
+      is_reachable reachable_tbl (Var v) ||
+      Cil.hasAttribute "FC_BUILTIN" v.vattr ||
+      (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, _) ->
+      (* if some generated temp variables are useless, remove them.
+         Keep variables that were already present in the code.
+      *)
+      let filterLocal local =
+        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
+            (Kernel.debug ~dkey "removing local: %s" name);
+            removedLocals :=
+              (func.svar.vname ^ "::" ^ name) :: !removedLocals;
+            false
+          end else true
+      in
+      func.slocals <- List.filter filterLocal func.slocals;
+      let remove_blocals = object
+        inherit Cil.nopCilVisitor
+        method! vblock b =
+          b.blocals <- List.filter filterLocal b.blocals;
+          DoChildren
+      end
+      in
+      ((is_reachable reachable_tbl (Var func.svar))
+       || Cil.hasAttribute "FC_BUILTIN" func.svar.vattr
+       || isRoot global) &&
+      (ignore (visitCilBlock remove_blocals func.sbody);
+       remove_unused_labels func;
+       true)
+
+    (* 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;
+  if Kernel.is_debug_key_enabled dkey then
+    List.iter (fun rg ->
+        Kernel.debug ~dkey "removing global: %s" (global_type_and_name rg)
+      ) removedGlobals;
+  if Kernel.is_debug_key_enabled dkey then
+    List.iter (fun rg ->
+        begin
+          match rg with
+          | GFunDecl (_s, vi, _) ->
+            begin
+              try
+                let kf = Globals.Functions.get vi in
+                Kernel.debug ~dkey "GFunDecl: %a@." Kernel_function.pretty_code kf
+              with Not_found ->
+                Kernel.debug ~dkey "GFunDecl: not found for %a@." Printer.pp_varinfo vi;
+            end
+          | _ -> ()
+        end;
+        Kernel.debug ~dkey "kept global %s (%a)" (global_type_and_name rg) Printer.pp_global rg
+      ) keptGlobals;
   !removedLocals
 
 
@@ -755,42 +876,40 @@ let removeUnmarked isRoot file =
  *
  *  Exported interface
  *
- *)
+*)
 
 
 type rootsFilter = global -> bool
 
-let isDefaultRoot = isExportedRoot
-
-let removeUnusedTemps ?(isRoot : rootsFilter = isDefaultRoot) 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 ||
-	isRoot global
+        isPragmaRoot keepers global ||
+        isRoot global
       in
 
       (* mark everything reachable from the global roots *)
-      clearReferencedBits file;
-      markReachable file isRoot;
+      markReachable isRoot ast reachable_tbl;
+
+      let elements =
+        InfoHashtbl.fold (fun k v acc -> Format.asprintf "%a:%B" pp_info k v :: acc)
+          reachable_tbl []
+      in
+      Kernel.debug ~dkey "reachable_tbl: %a"
+        (Pretty_utils.pp_list ~sep:"@\n" Format.pp_print_string) elements;
+
+      markReferenced ast;
 
       (* take out the trash *)
-      let removedLocals = removeUnmarked isRoot file in
-
-      (* 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)
+      ignore (removeUnmarked isRoot ast reachable_tbl)
     end
 
 (*
diff --git a/src/kernel_internals/typing/rmtmps.mli b/src/kernel_internals/typing/rmtmps.mli
index c99591522f1790b69002d4ed3a572c01067441dd..510a6565b0551e9045c09aa3913ec2d8735dd80d 100644
--- a/src/kernel_internals/typing/rmtmps.mli
+++ b/src/kernel_internals/typing/rmtmps.mli
@@ -72,11 +72,10 @@
  * Note that certain CIL- and CCured-specific pragmas induce
  * additional global roots.  This functionality is always present, and
  * is not subject to replacement by "filterRoots".
- *)
+*)
 
 
 type rootsFilter = Cil_types.global -> bool
-val isDefaultRoot : rootsFilter
 val isExportedRoot : rootsFilter
 val isCompleteProgramRoot : rootsFilter
 
@@ -84,10 +83,10 @@ val isCompleteProgramRoot : rootsFilter
 val removeUnusedTemps: ?isRoot:rootsFilter -> Cil_types.file -> unit
 
 (** removes unused labels for which [is_removable] is true.
-[is_removable] defaults to the negation of boolean flag of [Label]
-{i i.e.} only labels generated by CIL may be removed.
-@since Carbon-20101201
- *)
+    [is_removable] defaults to the negation of boolean flag of [Label]
+    {i i.e.} only labels generated by CIL may be removed.
+    @since Carbon-20101201
+*)
 val remove_unused_labels:
   ?is_removable:(Cil_types.label -> bool) -> Cil_types.fundec -> unit
 
diff --git a/src/kernel_services/ast_queries/cil.ml b/src/kernel_services/ast_queries/cil.ml
index 79da27bd41da6e97700185a0d1cf6801b9f1c812..c5e01b7134df44a227c6d801e2ab9e1e6f2c0da0 100644
--- a/src/kernel_services/ast_queries/cil.ml
+++ b/src/kernel_services/ast_queries/cil.ml
@@ -578,7 +578,7 @@ type attributeClass =
    | x -> x
 
  (* Make a varinfo. Used mostly as a helper function below  *)
- let makeVarinfo ?(source=true) ?(temp=false) global formal name typ =
+ let makeVarinfo ?(source=true) ?(temp=false) ?(referenced=false) global formal name typ =
    let vi =
      { vorig_name = name;
        vname = name;
@@ -593,7 +593,7 @@ type attributeClass =
        vattr = [];
        vstorage = NoStorage;
        vaddrof = false;
-       vreferenced = false;
+       vreferenced = referenced;
        vdescr = None;
        vdescrpure = true;
        vghost = false;
@@ -6280,16 +6280,16 @@ let need_cast ?(force=false) oldt newt =
  let refresh_local_name fdec vi =
    let new_name = findUniqueName fdec vi.vname in vi.vname <- new_name
 
- let makeLocal ?(temp=false) ?(formal=false) fdec name typ =
+ let makeLocal ?(temp=false) ?referenced ?(formal=false) fdec name typ =
    (* a helper function *)
    let name = findUniqueName fdec name in
    fdec.smaxid <- 1 + fdec.smaxid;
-   let vi = makeVarinfo ~temp false formal name typ in
+   let vi = makeVarinfo ~temp ?referenced false formal name typ in
    vi
 
  (* Make a local variable and add it to a function *)
- let makeLocalVar fdec ?scope ?(temp=false) ?(insert = true) name typ =
-   let vi = makeLocal ~temp fdec name typ in
+ let makeLocalVar fdec ?scope ?(temp=false) ?referenced ?(insert = true) name typ =
+   let vi = makeLocal ~temp ?referenced fdec name typ in
    refresh_local_name fdec vi;
    if insert then
      begin
@@ -6377,8 +6377,8 @@ let need_cast ?(force=false) oldt newt =
 
     (* Make a global variable. Your responsibility to make sure that the name
      * is unique *)
- let makeGlobalVar ?source ?temp name typ =
-   makeVarinfo ?source ?temp true false name typ
+ let makeGlobalVar ?source ?temp ?referenced name typ =
+   makeVarinfo ?source ?temp ?referenced true false name typ
 
  let mkPureExprInstr ~fundec ~scope ?loc e =
    let loc = match loc with None -> e.eloc | Some l -> l in
diff --git a/src/kernel_services/ast_queries/cil.mli b/src/kernel_services/ast_queries/cil.mli
index 799986722ec0afbc0e43e5acc5cffcd8fb59c5a0..60819b3c6ef5382920d8c8ae345a4d829dbd443e 100644
--- a/src/kernel_services/ast_queries/cil.mli
+++ b/src/kernel_services/ast_queries/cil.mli
@@ -672,10 +672,13 @@ val splitFunctionTypeVI:
   [vtemp] field in type {!Cil_types.varinfo}.
   The [source] argument defaults to [true], and corresponds to the field
   [vsource] .
+  The [referenced] argument defaults to [false], and corresponds to the field
+  [vreferenced] .
   The first unnamed argument specifies whether the varinfo is for a global and
   the second is for formals. *)
 val makeVarinfo:
-  ?source:bool -> ?temp:bool -> bool -> bool -> string -> typ -> varinfo
+  ?source:bool -> ?temp:bool -> ?referenced:bool -> bool -> bool -> string ->
+  typ -> varinfo
 
 (** Make a formal variable for a function declaration. Insert it in both the
     sformals and the type of the function. You can optionally specify where to
@@ -696,7 +699,7 @@ val makeFormalVar: fundec -> ?where:string -> string -> typ -> varinfo
     @modify Chlorine-20180501 the name of the variable is guaranteed to be fresh.
 *)
 val makeLocalVar:
-  fundec -> ?scope:block -> ?temp:bool -> ?insert:bool
+  fundec -> ?scope:block -> ?temp:bool -> ?referenced:bool -> ?insert:bool
   -> string -> typ -> varinfo
 
 (** if needed, rename the given varinfo so that its [vname] does not
@@ -720,7 +723,8 @@ val makeTempVar: fundec -> ?insert:bool -> ?name:string -> ?descr:string ->
 
 (** Make a global variable. Your responsibility to make sure that the name
     is unique. [source] defaults to [true]. [temp] defaults to [false].*)
-val makeGlobalVar: ?source:bool -> ?temp:bool -> string -> typ -> varinfo
+val makeGlobalVar: ?source:bool -> ?temp:bool -> ?referenced:bool -> string ->
+  typ -> varinfo
 
 (** Make a shallow copy of a [varinfo] and assign a new identifier.
     If the original varinfo has an associated logic var, it is copied too and
diff --git a/src/kernel_services/ast_queries/file.ml b/src/kernel_services/ast_queries/file.ml
index 3e49d89eeee42a19df6936e96e16b390e18ba209..1f1726189a09cf9422cab0e6a3d1c11e3c29f5cf 100644
--- a/src/kernel_services/ast_queries/file.ml
+++ b/src/kernel_services/ast_queries/file.ml
@@ -637,7 +637,7 @@ let () =
     keep_unused_specified_function). This function is meant to be passed to
     {!Rmtmps.removeUnusedTemps}. *)
 let keep_entry_point ?(specs=Kernel.Keep_unused_specified_functions.get ()) g =
-  Rmtmps.isDefaultRoot g ||
+  Rmtmps.isExportedRoot g ||
     match g with
     | GFun({svar = v; sspec = spec},_)
     | GFunDecl(spec,v,_) ->
diff --git a/src/kernel_services/plugin_entry_points/kernel.ml b/src/kernel_services/plugin_entry_points/kernel.ml
index 0c95ee14a2c78bdc8ccf69d66875628a261e63c9..9c4a3b5306acdf51c56a02afc958a2132237bf28 100644
--- a/src/kernel_services/plugin_entry_points/kernel.ml
+++ b/src/kernel_services/plugin_entry_points/kernel.ml
@@ -82,6 +82,7 @@ let dkey_loops = register_category "natural-loops"
 
 let dkey_parser = register_category "parser"
 let dkey_rmtmps = register_category "parser:rmtmps"
+let dkey_referenced = register_category "parser:referenced"
 
 let dkey_pp = register_category "pp"
 let dkey_compilation_db = register_category "pp:compilation-db"
diff --git a/src/kernel_services/plugin_entry_points/kernel.mli b/src/kernel_services/plugin_entry_points/kernel.mli
index 06e4720e294a3e4c737f3c4adce2f988dbe72670..c837fb5a0919eddb59eabfe811336a46ccf66629 100644
--- a/src/kernel_services/plugin_entry_points/kernel.mli
+++ b/src/kernel_services/plugin_entry_points/kernel.mli
@@ -109,6 +109,8 @@ val dkey_prop_status_reg: category
 
 val dkey_rmtmps: category
 
+val dkey_referenced: category
+
 val dkey_task: category
 
 val dkey_typing_global: category
diff --git a/src/plugins/variadic/va_build.ml b/src/plugins/variadic/va_build.ml
index 77f3bbaf0475a97369a8dfdd005d54874ffa7cd4..a5852d4a84d93ea6f773325fb1ed5b2bfa2fccb3 100644
--- a/src/plugins/variadic/va_build.ml
+++ b/src/plugins/variadic/va_build.ml
@@ -28,7 +28,7 @@ open Cil
 
 let function_declaration ?vattr ~loc name typ mk_spec =
   (* Build the varinfo *)
-  let vi = makeGlobalVar name typ in
+  let vi = makeGlobalVar ~referenced:true name typ in
   Extlib.may (fun extra_vattr -> vi.vattr <- vi.vattr @ extra_vattr) vattr;
   vi.vdecl <- loc;
   (* Build the formals *)